> For the complete documentation index, see [llms.txt](/llms.txt)

# 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

```bash
pip install petstore
```

```python
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:

```python
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](/docs/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](/docs/retries).

## 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](/docs/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](/docs/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.

```python
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](/docs/pagination#fetch-one-page),
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](/docs/retries) for precedence and cancellation.

[Binary downloads](/docs/file-transfers) 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.
