Python
The Python SDK is an httpx-based client with typed models, emitted from the same
GlottoIR as every other target. It ships both async and sync surfaces so it fits a FastAPI
service or a plain script equally well.
Quickstart
pip install petstore
import os
from petstore import Client
client = Client(token=os.environ["PETSTORE_TOKEN"])
# a single call
pet = client.pets.create_pet(name="Rex")
# the sync client yields a plain Iterator[Pet]
for pet in client.pets.list_pets():
print(pet.name)
Async + sync clients
The default Client is synchronous over httpx.Client; AsyncClient mirrors the same methods over
httpx.AsyncClient for callers using await. Both are
generated from the one IR, so their surfaces stay in lockstep.
Typed models
Request and response shapes use dataclasses by default. Set targets.python.pydantic: true
to generate pydantic v2 models with validation. Both modes return model instances and resolve
supported discriminated unions to their variants.
Serializing models
Import to_dict or to_json from your SDK package to serialize a model with the same rules
used for request bodies:
from petstore import to_dict, to_json
payload = to_dict(pet)
text = to_json(pet)
Both helpers preserve wire field names, nested models, unknown response fields, and retained
temporal values. Optional fields left unspecified by create(...) or response decoding stay
omitted; explicitly supplied None remains JSON null. The helpers work with dataclasses and
the opt-in Pydantic models.
Iterator pagination
Paginated list methods return iterators that walk every page, the generated method advancing the
cursor. The sync Client yields a plain Iterator — for pet in client.pets.list_pets(): — and
AsyncClient yields an AsyncIterator you consume with async for. See
Pagination.
Retries & backoff
Transient failures (5xx, 429, transport errors) retry with exponential backoff and jitter;
non-idempotent writes only retry when idempotency is enabled. Tune max_attempts,
initial_delay_ms, max_delay_ms, and jitter per client. See Retries & timeouts.
SSE via async iterators
Server-sent-event endpoints yield typed events through an async iterator —
async for event in client.…() — decoded from the text/event-stream framing. See
Streaming.
Typed errors
Non-2xx responses raise typed exceptions carrying the parsed, typed error body, so you
except RateLimitError rather than inspecting status codes by hand. See Errors.
Unknown response fields
Your API can add a response field without it being a breaking change — but a generated dataclass hydrates only its declared fields, so the key would be dropped. The generated models keep it instead: a field the SDK wasn’t generated from is retained on decode, readable through an accessor, and written back out when the model is re-serialized.
pet = client.pets.create_pet(body)
# A field your API started returning after this SDK was generated.
species = pet.extra_fields().get("species")
extra_fields() returns a plain dict, so nested objects and lists survive intact, and retention
is recursive: a nested model keeps its own unknown fields.
Both emit modes carry the same surface — extra_fields() is spelled identically whether your
SDK was generated with dataclass or pydantic models, so a caller never has to know which one they
are holding.
The retained fields are read-only by design. To send a field your spec doesn’t model yet, use the per-call extra-body escape hatch rather than writing to the retained bag.
File uploads
multipart/form-data operations build an httpx files=/data= request from their fields for
you, and an application/octet-stream body is sent raw as the request content. Binary fields
come straight from the spec’s format: binary signal, so uploads work without hand-assembling the
multipart body.
Pages, request controls, and files
Paginated methods also expose manual pages, so you can inspect one response and request its successor explicitly. Request overrides apply to the initial call and to each requested next page; see Retries & timeouts for precedence and cancellation.
Binary downloads return an owned byte response with metadata, a bounded read helper, and incremental consumption. Close the response when you stop early. Your generated README demonstrates these operations with your API’s names and the language’s native calling conventions.