Build your integrationTargetsSDKs

Python

Generate synchronous and asynchronous Python clients for your API with typed models and errors.

Typeship generates synchronous and asynchronous Python clients for your API as one focused package with no runtime dependencies. This page covers Python-specific behavior; shared client behavior is on the SDK overview.

CLI and MCP packages are generated and released separately.

Package

  • The distribution and import names initially derive from your API’s title. When that name is the import name of an established package for the same API, such as github (PyGithub) or stripe, the default becomes github_client so installing both does not break either. Set the repository Delivery’s PyPI package name under Targets → Python SDK, using package_name in the API. Install with pip install . from the package directory, or from PyPI after publishing. Check the generated README for the normalized import name.
  • pyproject.toml declares dependencies = [] and requires-python >= 3.11. The runtime uses only the standard library.
  • The package ships py.typed, so type checkers see every TypedDict and Literal.
  • tests/ holds a unittest suite that calls one operation per resource against a local stub server, checks that a 404 raises NotFoundError, and walks two pages when the API paginates. Run python -m unittest in the package directory. The tests use only the standard library and are not installed with the package.

Idioms

  • Raise, don't return. Generated exceptions derive from SdkError and expose code, status, request_id, body, and a message with the next step. Catch ApiError for API responses or TransportError when no response arrives. See Results and errors.
  • Keyword arguments for bodies. When an operation's request body is an object (JSON, form-encoded, or multipart, inline or a named schema, including allOf compositions), its fields become keyword arguments: client.shipments.create(recipient="Ada Lovelace", service="ground"). Each argument is the field's snake_case name. A name that is a Python keyword, or that a path, query, or header parameter, request_options, or select already uses, takes a trailing underscore: a from field is from_=, and an owner body field beside an owner path parameter is owner_=. A body that is an array, a union, binary, or plain text stays one body= argument typed accordingly (List[ShipmentWrite], str).
  • Dicts, typed. Responses are the parsed JSON, typed as TypedDicts. You read shipment["id"], not shipment.id. There is no conversion layer between you and the wire.
  • Directional models stay honest. A component whose request and response contracts differ becomes ShipmentWrite and ShipmentRead; identical contracts keep the clean Shipment name. Response enums accept future strings and discriminator responses retain an unknown Dict[str, Any] fallback, while request types remain closed.
  • Generators for pages. for shipment in client.shipments.list(): walks every page. client.shipments.list_page() returns one raw envelope.
  • request_options per call. A dict with timeout, max_retries, and headers.

Choose credentials for each endpoint

Pass credentials keyed by the names in your Spec's components.securitySchemes. Bearer tokens and API keys accept a string or a token callback. Basic auth accepts a (username, password) tuple. The same configuration works with the synchronous and asynchronous clients, and the asynchronous client also accepts async def callbacks, awaited on the running event loop. Passing an async def callback to the synchronous client raises TypeError when a request needs it.

The client selects the first complete permitted combination for each endpoint. When an endpoint requires two schemes together, both must be configured. An incomplete combination sends neither credential; a public endpoint receives no generated credentials. Only selected callbacks run, before each attempt. Your API enforces token validity and permissions.

Named credentials take precedence over matching convenience options and environment variables. Use names to distinguish multiple credentials of the same type. Unknown names raise ValueError during client construction. Explicit default_headers, request headers and on_request hooks remain available for application-managed authentication.

When a request sent with a token callback or a client_credentials token receives 401, the client discards the cached client-credentials token, resolves the credential again, and resends the request once. The resend does not count against max_retries; a second 401 raises. A static string or tuple is never resent. An exception raised by a callback propagates unchanged and the request is not retried.

Use other credentials

client.with_credentials(...) returns a new client of the same class that sends different credentials, for example to act for one of your users with their token:

user_client = client.with_credentials(bearer_token=user_token)
shipment = user_client.shipments.get("shp_123")

It takes the same credential arguments as the constructor, such as bearer_token, api_key, client_credentials, and credentials, and replaces every credential. Nothing is inherited from the original client, and credential environment variables such as PARCEL_TOKEN are not read. The base URL, transport and pooled connections, timeouts, retries, hooks, and default headers are shared. Closing the new client leaves the shared connections open; close the original client when you are done with both.

Environment variables

The client reads these when the matching option is not passed:

VariableSets
PARCEL_TOKENbearer_token
PARCEL_BASE_URLbase_url
PARCEL_WEBHOOK_KEYwebhook_key
PARCEL_DEBUG=1debug

A single API key is api_key= and PARCEL_API_KEY, the same spelling as the TypeScript SDK and the CLI; several keys get one keyword and variable each, named after the header or parameter. Basic auth reads PARCEL_USERNAME and PARCEL_PASSWORD. The prefix comes from your package name. Credential variables use the same names as the CLI, which can follow the API's convention (such as PARCEL_API_KEY for a bearer scheme named for an API key) or your auth.credential_variables setting; see Auth and configuration. The README lists the names your package reads.

Transport

Pass transport= to replace the default keep-alive connection pool. A transport is a callable that takes (method, url, headers, body_bytes, timeout) and returns (status, headers, body_bytes). Use it to route through a proxy, a recording layer, or a test double. Streaming endpoints read the response incrementally and go straight to urllib, so they bypass a custom transport.

Webhooks

client.webhooks.unwrap(payload, headers) verifies and parses. unwrap_unsafe parses only. Module-level verify_webhook and sign_webhook are exported for handlers that do not hold a client. Events are TypedDicts discriminated by their type field. WEBHOOK_NAMES and WEBHOOK_SAMPLES list every declared event with a sample payload.

Async

AsyncParcelClient takes the same options and has the same methods, awaitable: await client.shipments.get(...), and async for over paginated methods and streams. Requests run on the event loop's default executor, so nothing blocks the loop and there is still nothing to install. Both clients are context managers (with / async with) and have close() / aclose().

The zero-dependency tradeoff is explicit: cancelling a coroutine stops waiting for its result, but it cannot interrupt a synchronous socket call already running in the executor. timeout bounds each socket attempt; it is not one wall-clock deadline across retries. The generated async client is therefore not the right transport when immediate in-flight socket cancellation is a hard requirement.

Uploads

A multipart/form-data field is a keyword argument that accepts raw bytes, an open binary file (its name becomes the filename), or a (filename, data, content_type) tuple, and a list of them sends one part per file. The other fields of the body travel alongside as form parts: a dict is one JSON part (application/json), and a type the spec's encoding names for a field is used for that part. A raw binary body (application/octet-stream, image/png, ...) is body= with bytes or a file object, sent under the spec's content type.

GraphQL

A GraphQL schema generates one method per query and mutation field, keyed under client.query and client.mutation. Arguments are keyword arguments and become variables. Every scalar field to depth 2 is selected by default, with a fragment per concrete type for unions and interfaces, and select="{ id name }" overrides that with a raw selection set on methods that return an object. Connection fields paginate like everything else. Errors in a 200 raise GraphQLRequestError (an ApiError) with the raw errors list.

Connections

The default transport pools keep-alive connections per client on http.client, one per scheme/host/port, honoring HTTP_PROXY/HTTPS_PROXY/NO_PROXY. A request that fails on a reused connection before any response arrived is retried once on a fresh one (the server closed an idle keep-alive, the same case Go's net/http retries), so pooling never turns into spurious transport errors. The default transport follows up to five redirects: GET and HEAD follow every redirect, 303 turns other methods into GET, 307 and 308 replay the request, and 301 or 302 responses to other methods are returned as they are. A redirect to a different scheme, host, or port drops credential headers, including Authorization, Cookie, and your spec's API-key headers.

On this page