Table of Contents
Quick Start
# Install
uv tool install openapi-burrito
# Generate
openapi-burrito generate openapi.json -o ./my_client
import asyncio
from my_client import AsyncClient
async def main():
api = AsyncClient(base_url="https://api.example.com")
# Path-first API: type-checked paths and snake_case parameters
res = await api.GET("/users/{user_id}", user_id=123)
if res.is_success:
print(res.data)
else:
print(f"Error {res.status_code}: {res.error}")
# Or let errors raise: unwrap() returns the typed body directly
user = (await api.GET("/users/{user_id}", user_id=123)).unwrap()
asyncio.run(main())
Prefer sync? Generate with --client sync and drop the awaits — same
paths, same overloads, httpx.Client underneath:
from my_client import Client
api = Client(base_url="https://api.example.com")
res = api.GET("/users/{user_id}", user_id=123)
Features
- Path-First API - Call endpoints by path literal
(
api.GET("/users/{user_id}")), with full IDE autocomplete for paths and parameters - Type-Safe -
TypedDictmodels and@overloadsignatures; generated code passesmypy --strict - Async by Default -
AsyncClientout of the box;--client syncor-c async -c syncwhen you need the synchronousClienttoo - Zero Runtime - Generated code is yours; its only dependency is httpx
- httpx-Based - Connection pooling, timeouts, all httpx client features
- Middleware System - Logging, retry, auth via composable middleware
- Snake Case Params - Path parameters auto-converted to Python style
(
{userId}→{user_id})
Philosophy: Types Without Runtime Magic
This library has exactly one opinion: your types should come from the spec, and your runtime should stay out of the way.
- Dicts in, dicts out. Responses are plain dicts typed as
TypedDict— no model classes to instantiate, serialize, or fight with.res.dataIS the parsed JSON. This is what makes the zero-runtime promise possible: where model classes need a deserialization layer (and that layer needs a runtime dependency), aTypedDictis erased at runtime — nothing to construct, no per-request overhead, nothing to import but httpx. - No validation, no coercion. The generated client does not validate
payloads against the schema and does not convert strings to
datetime. Your type checker verifies structure at development time; at runtime you get what the API sent, untouched. - No inheritance hooks, no plugins, no config file. One command in, one small readable package out.
If you want runtime validation and rich models, you want a pydantic-based generator like openapi-python-client — that's a great tool with a different philosophy. openapi-burrito is for when you'd rather have the thinnest possible typed layer over httpx.
If you build FastAPI services, this will feel like home: the same httpx + type-hints + async idioms you already use, pointed at the APIs you consume instead of the ones you serve.
Installation
For Users
# As a CLI tool (recommended)
uv tool install openapi-burrito
# With preview server support (Swagger UI, Redoc)
uv tool install openapi-burrito[preview]
For Developers
# Clone and install all dev dependencies
git clone https://github.com/simon-lund/openapi-burrito.git
cd openapi-burrito
make install
# Run linting and type checks
make lint
# Run tests
make test
Security
Code generators turn untrusted input into code, so the design has to make injection impossible rather than filter it out. Always review untrusted specs before generating anyway.
How spec text reaches generated code
Nothing from the spec is concatenated into source as-is. Every value takes one of these paths:
| What | How it is emitted | Guarantee |
|---|---|---|
| Model and argument names | spec.naming.identifier() |
Valid, non-keyword identifier or generation fails |
| Model property keys | Verbatim in class syntax if a plain identifier, else repr() in functional TypedDict syntax |
Keys match the wire format; never hand-escaped |
| Paths, enum values, defaults, wire names | repr() |
A Python literal that evaluates back to the exact value |
| Descriptions/docs | emit.docstring() |
Backslashes, triple quotes and control chars escaped; property-tested for every string |
| Types | Rendered from the IR by match |
Only known constructors; unknown schemas become Any |
method, parameter in |
ir.Method / ir.Location enums |
Unknown values are skipped with a warning |
required, readOnly, writeOnly |
bool() cast |
Forced to boolean |
The runtime (Response, UNSET, …) |
Copied verbatim from runtime.py |
Never templated; type-checked and tested in this repo |
| pyproject.toml metadata | emit.toml_string() |
TOML-safe string literals |
As defense in depth, every emitted .py file must pass compile() and the
pyproject.toml must parse as TOML before anything is written to disk —
malformed output aborts generation instead of landing in your project.
A malicious spec could attempt injection like:
components:
schemas:
"User:\n pass\nimport os; os.system('rm -rf /') # ":
type: object
While this generator escapes such payloads, the safest approach is to only generate clients from trusted sources.
See CVE-2020-15142 for an example of this vulnerability class in other generators.
What a spec can reach
Whatever a $ref reads ends up in the generated code. So a spec may only
reference files inside its own folder, and a spec loaded from a URL may only
reference itself. Anything else (other URLs, files elsewhere on disk) is refused
before it is read, unless you pass --allow-external-refs for a spec you trust.
Path arguments
Generated clients percent-encode path arguments, so id="../../admin" stays one
URL segment and cannot reach another endpoint. . and .. raise ValueError,
because no encoding keeps them from being normalized away.
Documentation
| Guide | Description |
|---|---|
| Introduction | Installation and basic usage |
| Authentication | API keys, tokens, OAuth patterns |
| Middleware | Logging, retry, custom handling |
| Type System | UNSET, Unknown, NotRequired, limitations |
| CLI Reference | generate and preview commands |
| Contributing | Development setup and guidelines |
| Architecture | load → parse → emit, the IR, tree vs graph |
Examples
See the examples/ directory:
- Petstore - Classic Swagger Petstore API
- Artifacts MMO - Game API with complex schemas
Star History
Metadata
Release files for openapi-burrito 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| openapi_burrito-0.2.2.tar.gz | 32.1 kB | With provenance | Details for openapi_burrito-0.2.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| openapi_burrito-0.2.2-py3-none-any.whl | Python 3 | none | any | Details for openapi_burrito-0.2.2-py3-none-any.whl |
Total release size: 72.2 kB
Release files / openapi_burrito-0.2.2.tar.gz
| Download URL | openapi_burrito-0.2.2.tar.gz |
|---|---|
| Size | 32.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d9af924555ff2056770799de3218acb7c659abd40c3925870d5a00150070d034
|
|
BLAKE2b-256 checksum How to use checksums |
7f0a121bf1dd928297db03f233ef28e4d1e7102e96ccec032a119ff2408fe038
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.
Transparency logRelease files / openapi_burrito-0.2.2-py3-none-any.whl
| Download URL | openapi_burrito-0.2.2-py3-none-any.whl |
|---|---|
| Size | 40.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7fde3daf3e9a1d5012e8a985d8cfac8bb91aa675bd23cd369b190324606ea75d
|
|
BLAKE2b-256 checksum How to use checksums |
1890650aac00ef31db450d5742c41a904d9db03ea00c1eed6581df7ffab2b4af
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.
Transparency logSecurity for openapi-burrito 0.2.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for openapi_burrito-0.2.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.