Skip to main content

openapi-burrito logo

openapi-burrito

Wrap your OpenAPI specs in type-safe Python clients

CI PyPI version Python License OpenAPI

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 - TypedDict models and @overload signatures; generated code passes mypy --strict
  • Async by Default - AsyncClient out of the box; --client sync or -c async -c sync when you need the synchronous Client too
  • 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.data IS 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), a TypedDict is 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:

Star History

Star History Chart

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)

Source distribution for openapi-burrito 0.2.2
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)

Table of built distributions (wheels) for openapi-burrito 0.2.2
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page