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 usesreveal_type()to confirm that generic types and payload structures are preserved correctly.
You can run it withmypyor 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
dataanderror - ✅ 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
dataorerror, not both metalets you attach timing, versioning, request ID, etc.- If neither
datanorerroris passed, raisesApiResponseBuilderError
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 failsApiPayloadBuilderError– if payload data is inconsistent or incomplete
📦 Components
ApiResponseBuilder– abstract base class for buildersApiSuccessResponseBuilder– buildsApiSuccessResponse[T]ApiErrorResponseBuilder– buildsApiErrorResponseResponseMeta– optional metadata blockPayload/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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.