Skip to main content

Typed API Response

A unified, type-safe API response format for Python — with full type inference, Pydantic support, and consistent structure for both success and error responses. Just pass your data or exception — build_api_response() handles the rest.

Works seamlessly with FastAPI, Django Ninja, or any Pydantic-based Python project. Accepts Pydantic models, dataclasses, or any structured object.

🧪 Type Safety

This library is:

  • Designed for Pylance and mypy strict mode
  • Fully generic — type-safe through all layers
  • Uses overloads to preserve type inference

No need to type hint manually:

response = build_api_response(data=MySchema(...), status=200)
# response.payload.data is inferred as MySchema ✅

➡️ Want proof? See the typecheck file

This is a static analysis file for mypy. It uses reveal_type() to confirm that generic types and payload structures are preserved correctly.
You can run it with mypy or open it in VSCode and hover to inspect types inline — no need to execute the file.

🔧 Features

  • ✅ Typed response builders for both success and error responses
  • ✅ Fully generic, Pylance-compliant with strict mode enabled
  • ✅ Single unified function: build_api_response(...)
  • ✅ Extensible metadata support via ResponseMeta
  • ✅ Automatically distinguishes between data and error
  • ✅ Raises clean custom exceptions on misconfiguration

🚀 Getting Started

Install with pip

pip install typed-api-response

Define your Pydantic response schema

from pydantic import BaseModel

class MyOutputSchema(BaseModel):
    product_name: str
    description: str
    price: float
    qty: int
    on_sale: bool

Create a typed API response:

@router.post("/foo")
def foo():
    your_data = MyOutputSchema(
      product_name="Big Bag of Rice",
      description="The world's greatest rice ever made. Anywhere. Ever.",
      price=17.99,
      qty=47328,
      on_sale=False,
    )
    response = build_api_response(data=your_data, status=200)
    # response.payload.data is inferred as MyOutputSchema ✅

✅ build_api_response() accepts any Pydantic model or well-typed object (e.g. a dataclass) and wraps it into a fully structured, metadata-rich response — with full type hint propagation and IDE support via generics.

Handling errors just as cleanly

You can also return exceptions using the same unified response format:

try:
    ...
except Exception as e:
    return build_api_response(error=e, status=418)

✅ build_api_response() wraps the exception in a type-safe, structured error payload — so your failure responses stay as consistent and predictable as your success ones.

🧱 API Structure

Unified Interface

def build_api_response(
    *,
    data: T | None = None,
    error: Exception | None = None,
    status: int,
    meta: ResponseMeta | None = None,
) -> ApiSuccessResponse[T] | ApiErrorResponse
  • Provide either data or error, not both
  • meta lets you attach timing, versioning, request ID, etc.
  • If neither data nor error is passed, raises ApiResponseBuilderError

Success Response Format

{
  "status": 200,
  "meta": {
    "duration": null,
    "extra": null,
    "method": null,
    "path": null,
    "request_id": null,
    "timestamp": "2025-07-30T04:33:44.833Z",
    "version": null
  },
  "payload": {
    "success": true,
    "data": {
      "product_name": "Big Bag of Rice",
      "description": "The world's greatest rice ever made. Anywhere. Ever.",
      "price": 17.99,
      "qty": 47328,
      "on_sale": false
    },
    "error": null
  }
}

Error Response Format

{
  "status": 418,
  "meta": {
    "duration": null,
    "extra": null,
    "method": null,
    "path": null,
    "request_id": null,
    "timestamp": "2025-07-30T00:13:55.531Z",
    "version": null
  },
  "payload": {
    "success": false,
    "data": null,
    "error": {
      "type": "ZeroDivisionError",
      "msg": "division by zero"
    }
  }
}

🧠 Customizing Metadata

Use ResponseMeta to attach custom fields:

meta = ResponseMeta(
    request_id="abc123",
    version="v1.2.0",
    extra={"model": "en_streetninja", "debug": True}
)

return build_api_response(data=result, status=200, meta=meta)

🛡️ Exceptions

This package raises:

  • ApiResponseBuilderError – if payload generation fails
  • ApiPayloadBuilderError – if payload data is inconsistent or incomplete

📦 Components

  • ApiResponseBuilder – abstract base class for builders
  • ApiSuccessResponseBuilder – builds ApiSuccessResponse[T]
  • ApiErrorResponseBuilder – builds ApiErrorResponse
  • ResponseMeta – optional metadata block
  • Payload / SuccessPayload[T] / ErrorPayload – structured payload schemas

☕ Support This Project

If this saved you time or made your API cleaner, feel free to buy me a coffee. Thanks for your support 🙏

Metadata

Release files for typed-api-response 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for typed-api-response 1.0.0
File Size Uploaded Provenance Actions
typed_api_response-1.0.0.tar.gz 12.8 kB Without provenance Details for typed_api_response-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for typed-api-response 1.0.0
File Interpreter ABI Platform Actions
typed_api_response-1.0.0-py3-none-any.whl Python 3 none any Details for typed_api_response-1.0.0-py3-none-any.whl

Total release size: 22.9 kB

Release files / typed_api_response-1.0.0.tar.gz

Download URL typed_api_response-1.0.0.tar.gz
Size 12.8 kB
Tags Source
SHA-256 checksum
How to use checksums
1f4a1217cc1dd8fa3be925e903f63e84c7b561e3f47828bc0acc364fbc752176
BLAKE2b-256 checksum
How to use checksums
ec47d90b72f3ca1519a3c2448e8fd273b4283969ec1e78f5aa7bdb72adad51f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.12

Release files / typed_api_response-1.0.0-py3-none-any.whl

Download URL typed_api_response-1.0.0-py3-none-any.whl
Size 10.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c751f90f2c8dbf11f07df1c96c28facb08f627874cc39aa3da3b88ea41c830bd
BLAKE2b-256 checksum
How to use checksums
ca52db2b1101aea385de2dbee13678d07069aacfdba9ce10dae5f9defe92f187
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.12

Security for typed-api-response 1.0.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

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