Quick start¶
This page walks you from "I have an OpenAPI document" to "I have a typed
Python client I can import and call." Five minutes, three commands.
For a tour of every API surface the generated client exposes — filters, sorts, pagination, actions, async, error handling, retries — see Using the client.
1. Pick (or grab) a spec¶
okapipy reads OpenAPI 3.x in JSON or YAML, from a local file or an
http(s) URL. For the rest of this page we'll use a local
openapi.yaml, but anywhere you see that, a URL works too.
2. Sanity-check the parse¶
Before generating code, see what okapipy thinks your spec looks like:
You'll get:
- a counts panel on stderr (namespaces, collections, resources, singletons, actions),
- a JSON dump of the parsed tree on stdout — pipe it into
lessorjq.
If you'd rather save the tree:
okapipy parse openapi.yaml --output tree.yaml
# .yaml or .yml → YAML; .json → JSON. Anything else errors.
If a classification looks off (Staff should be a collection, me
should be a singleton, a non-CRUD POST got dropped), fix it in the rules
file — see Rules and extensions — before generating.
3. Scaffold the project manifest¶
okapipy generate is manifest-driven: every project-level setting
(package name, client class, response shape) and every per-spec setting
(source, rules, strip-prefix) lives in a small YAML file. Scaffold one
with okapipy init:
That writes ./okapipy.yml:
# okapipy project manifest. See https://ffaraone.github.io/okapipy/ for details.
# Required.
package: acme.commerce
client_class: CommerceClient
# Project metadata — drives pyproject.toml, LICENSE, and README.
project_name: commerce
project_description: Generated client for commerce
project_version: "0.1.0"
python_version: "3.13"
license: Proprietary # SPDX id (MIT, Apache-2.0, BSD-3-Clause, MPL-2.0, ...)
author: Your Organization
repo_url: https://github.com/your-org/your-repo
# Optional generation settings — see the manifest reference for details.
# shape: auto # auto | models | dicts
# output: ./out
specs:
- namespace: ''
source: openapi.yaml
Edit the author and repo_url lines (and the license if you have
one in mind), commit the file alongside your consumer code, and you're
done — it's the source of truth for what the generated client looks
like.
4. Generate a client¶
That writes a complete Python project under ./my-client:
my-client/
├── pyproject.toml # one-shot
├── README.md # one-shot
├── LICENSE # one-shot
├── src/acme/commerce/
│ ├── __init__.py # one-shot — your public surface
│ ├── client.py # one-shot — class CommerceClient(CommerceClientBase)
│ ├── collections/ # one-shot — your subclasses live here
│ ├── resources/
│ ├── actions/
│ └── base/ # regenerated every run — DO NOT EDIT
│ ├── client.py
│ ├── collections/orders.py
│ ├── resources/order.py
│ ├── actions/order_submit.py
│ ├── models.py
│ ├── strategies.py
│ ├── filters.py
│ ├── sort.py
│ ├── transport.py
│ ├── exceptions.py
│ ├── types.py
│ └── _generated.json
└── tests/ # one-shot scaffolding
Two layers, two lifecycles: the base/ tree is rewritten on every
okapipy generate, the rest is emitted once and then yours
forever. See Code customization for the full story.
Tip
Add output: ./my-client to okapipy.yml and you can drop the
--output flag too — subsequent runs are just
okapipy generate.
5. Use it¶
from acme.commerce import CommerceClient
with CommerceClient(base_url="https://api.example.com") as client:
# Iterate a collection — pagination is automatic.
for order in client.commerce.orders:
print(order.id, order.total)
# Look up one resource by id (square brackets, not parens).
order = client.commerce.orders["ord_42"].retrieve()
# Sub-collections walk the tree.
line = client.commerce.orders["ord_42"].lines.create(
body={"sku": "SKU-1", "qty": 2},
)
# Actions are real properties returning an Action object whose
# one method is `run(...)`.
client.commerce.orders["ord_42"].submit.run()
That's the whole rhythm. Three observations to internalize:
- Listing is iteration, not a
fetch()method.for order in client.commerce.orders:issues paged GETs through the configured pagination strategy and yields parsed model instances. - Resources use
[id], not(id). The collection implements__getitem__, returning the resource object — no network call yet. - Actions have a single
.run()method. POST/.../submitis exposed assubmit.run(), notsubmit(), because the action object itself can carry per-call state (overrides, headers, options).
The generated client is also built to be navigated by hover: every
class lists the children you can reach from it (sub-namespaces,
collections, actions, ...) right in its docstring, so typing client.
and reading the IDE tooltip is enough to discover the surface. See
IDE tooltips for the full story.
6. Iterate¶
When the upstream spec changes, re-run the same command:
The base/ tree is rewritten to match the new spec. Your subclasses,
your client.py, your __init__.py — all left strictly alone. If the
spec gained a new namespace or collection, the generator emits new
one-shot stubs for it and prints a drift-detection warning telling
you which factory line to add to wire your subclass into the tree.
In CI, gate on regeneration being committed:
--check exits non-zero if any base file would change, any drift
warning fires, or any stale base file would be pruned.
The project manifest¶
Every option used to drive generation lives in okapipy.yml. A typical
one for a single-spec project:
package: acme.commerce # required: dotted Python package
client_class: CommerceClient # required: PascalCase
output: ./out # optional: --output overrides this
# Optional project metadata — populates pyproject.toml + LICENSE.
project_description: Acme commerce API client
license: Apache-2.0
author: Acme Corp
repo_url: https://github.com/acme/commerce
specs: # required: at least one entry
- namespace: '' # '' mounts at the package root
source: ./openapi.yaml # path or http(s) URL
rules: ./rules.yaml # optional, local path only
Paths are resolved relative to the manifest file's directory, so the
manifest is portable with the consumer repo. URLs (source:
https://…) are left verbatim.
For every field the manifest accepts — version, Python, response shape, templates, per-spec overrides — see the manifest reference.
Multi-spec projects¶
Microservice projects can mount several OpenAPI documents under one
client by adding more entries to specs:. Each entry pairs a spec
source with its own namespace, rules, and prefix-stripping rules;
callers reach each service as a top-level accessor on the client
(client.auth.*, client.restapi.*). See
Advanced usage for the full pattern.
Useful CLI flags¶
| Flag | What it does |
|---|---|
--manifest PATH |
Read a manifest from somewhere other than ./okapipy.yml. |
--output DIR, -o DIR |
Override the manifest's output field. |
--check |
CI dry-run: report drift, exit non-zero on any change. |
--quiet / -q |
Suppress drift-detection warnings (pruning still runs). |
-v / -vv |
INFO / DEBUG logging. |