Standardising FastAPI responses with clarity, consistency, and control.
APIException is a robust, production-ready Python library for FastAPI that simplifies exception handling and ensures consistent, well-structured API responses. Designed for developers who want to eliminate boilerplate error handling and improve Swagger/OpenAPI documentation, APIException makes your FastAPI projects cleaner and easier to maintain.
- π Consistent JSON responses for both success and errors.
- π Beautiful Swagger/OpenAPI documentation with clear error cases.
- βοΈ Customizable error codes with
BaseExceptionCode
. - π Global fallback for unhandled server-side errors.
- ποΈ Use with multiple FastAPI apps.
- π Automatic logging of every exception detail.
- βοΈ Production-ready with unit test examples.
Β· View on PyPI
Reading the full documentation is highly recommended β itβs clear, thorough, and helps you get started in minutes.
pip install apiexception
1οΈβ£ Register the Handler
from APIException import register_exception_handlers
from fastapi import FastAPI
app = FastAPI()
register_exception_handlers(app) # uses ResponseModel by default
# Use raw dict instead:
# register_exception_handlers(app, use_response_model=False)
from fastapi import FastAPI, Path
from APIException import APIException, ExceptionStatus, register_exception_handlers, ResponseModel, APIResponse, BaseExceptionCode
from pydantic import BaseModel
app = FastAPI()
# Register exception handlers globally to have the consistent
# error handling and response structure
register_exception_handlers(app=app)
# Create the validation model for your response
class UserResponse(BaseModel):
id: int
username: str
# Define your custom exception codes extending BaseExceptionCode
class CustomExceptionCode(BaseExceptionCode):
USER_NOT_FOUND = ("USR-404", "User not found.", "The user ID does not exist.")
INVALID_API_KEY = ("API-401", "Invalid API key.", "Provide a valid API key.")
PERMISSION_DENIED = ("PERM-403", "Permission denied.", "Access to this resource is forbidden.")
@app.get("/user/{user_id}",
response_model=ResponseModel[UserResponse],
responses=APIResponse.custom(
(401, CustomExceptionCode.INVALID_API_KEY),
(403, CustomExceptionCode.PERMISSION_DENIED)
)
)
async def user(user_id: int = Path()):
if user_id == 1:
raise APIException(
error_code=CustomExceptionCode.USER_NOT_FOUND,
http_status_code=401,
)
data = UserResponse(id=1, username="John Doe")
return ResponseModel[UserResponse](
data=data,
description="User found and returned."
)
2οΈβ£ Raise an Exception
from APIException import APIException, ExceptionCode, register_exception_handlers
from fastapi import FastAPI
app = FastAPI()
register_exception_handlers(app)
@app.get("/login")
async def login(username: str, password: str):
if username != "admin" or password != "admin":
raise APIException(
error_code=ExceptionCode.AUTH_LOGIN_FAILED,
http_status_code=401
)
return {"message": "Login successful!"}
3οΈβ£ Use ResponseModel for Success Responses
from APIException import ResponseModel, register_exception_handlers
from fastapi import FastAPI
app = FastAPI()
register_exception_handlers(app)
@app.get("/success")
async def success():
return ResponseModel(
data={"foo": "bar"},
message="Everything went fine!"
)
Response Model In Abstract:
Always extend BaseExceptionCode β donβt subclass ExceptionCode directly!
from APIException import BaseExceptionCode
class CustomExceptionCode(BaseExceptionCode):
USER_NOT_FOUND = ("USR-404", "User not found.", "User does not exist.")
INVALID_API_KEY = ("API-401", "Invalid API key.", "Key missing or invalid.")
And use it:
from APIException import APIException
raise APIException(
error_code=CustomExceptionCode.USER_NOT_FOUND,
http_status_code=404
)
from APIException import set_default_http_codes
set_default_http_codes({
"FAIL": 422,
"WARNING": 202
})
from fastapi import FastAPI
from APIException import register_exception_handlers
mobile_app = FastAPI()
admin_app = FastAPI()
merchant_app = FastAPI()
register_exception_handlers(mobile_app)
register_exception_handlers(admin_app)
register_exception_handlers(merchant_app)
Every APIException automatically logs:
-
File name & line number
-
Error code, status, message, description
Or use the built-in logger:
from APIException import logger
logger.info("Custom info log")
logger.error("Custom error log")
import unittest
from APIException import APIException, ExceptionCode, ResponseModel
class TestAPIException(unittest.TestCase):
def test_api_exception(self):
exc = APIException(error_code=ExceptionCode.AUTH_LOGIN_FAILED)
self.assertEqual(exc.status.value, "FAIL")
def test_response_model(self):
res = ResponseModel(data={"foo": "bar"})
self.assertEqual(res.status.value, "SUCCESS")
if __name__ == "__main__":
unittest.main()
Run the Tests
- To run the tests, you can use the following command in your terminal:
python -m unittest discover -s tests
Find detailed guides and examples in the official docs.
v0.1.16 (2025-07-22)
β Initial stable version
-
setup.py has been updated.
-
Project name has been updated. Instead of
APIException
we will useapiexception
to comply withPEP 625
. -
Documentation has been updated.
-
Readme.md has been updated.
v0.1.15 (2025-07-22)
-
setup.py has been updated.
-
Project name has been updated. Instead of
APIException
we will useapiexception
to comply withPEP 625
. -
Documentation has been updated.
-
Readme.md has been updated.
v0.1.14 (2025-07-22)
-
setup.py has been updated.
-
Project name has been updated. Instead of
APIException
we will useapiexception
to comply with PEP 625.
v0.1.13 (2025-07-21)
-
/examples/fastapi_usage.py has been updated.
-
422 Pydantic error has been fixed in APIResponse.default()
-
Documentation has been updated.
-
Exception Args has been added to the logs.
-
Readme has been updated. New gifs have been added.
v0.1.12 (2025-07-14)
-
/examples/fastapi_usage.py has been updated.
-
422 Pydantic error has been handled in register_handler
-
Documentation has been added.
-
use_fallback_middleware
has been added.
v0.1.11 (2025-07-13)
-
Added CLI entrypoint (api_exception-info)
-
Stable test suite with FastAPI TestClient
-
Multiple app support
-
Raw dict or Pydantic output
-
Automatic logging improvements
v0.1.0 (2025-06-25)
π Prototype started!
-
Project scaffolding
-
ResponseModel
has been added -
APIException
has been added -
Defined base ideas for standardizing error handling
This project is licensed under the MIT License. See the LICENSE file for more details. If you like this library and find it useful, donβt forget to give it a β on GitHub!
If you have any questions or suggestions, please feel free to reach out at ahmetkutayural.dev
π° Read the full article explaining the motivation, features, and examples:
π Tired of Messy FastAPI Responses? Standardise Them with APIException
π Full APIException Documentation
https://akutayural.github.io/APIException/
π PyPI
https://pypi.org/project/apiexception/
π» Author Website
https://ahmetkutayural.dev