Backend
FastAPI
Type-driven Python APIs and model-serving backends, built by engineers who run them in production.
Overview
FastAPI is a modern Python web framework for building APIs. It sits on two well-chosen foundations: Starlette, which gives it an ASGI core and native async/await, and Pydantic, which turns ordinary Python type hints into runtime validation. You annotate a request body or query parameter with a type, and FastAPI validates incoming data against it, serialises the response, and generates interactive OpenAPI documentation from the same annotations — no separate schema files, no hand-written Swagger. That single idea, that your type hints are the contract, is what makes FastAPI feel different from everything that came before it in Python.
We reach for FastAPI when the job is an API and the workload is I/O-bound — talking to databases, other services, message queues, or model servers, and waiting on all of them. Its async core lets one worker hold thousands of concurrent connections open while requests are in flight, which puts its throughput on I/O-bound work in the same conversation as Node and Go, and far ahead of a synchronous Flask or Django REST app. We use it to expose typed HTTP and WebSocket endpoints, to wire in dependency injection for database sessions and auth, and to give front-end and client teams accurate, always-current API docs for free.
The honest framing is that FastAPI is deliberately narrow. It is not a batteries-included framework in the Django sense — there is no ORM, no admin panel, no templating, no auth system in the box. You assemble those from the ecosystem: SQLAlchemy or an async driver for data, your own auth, Alembic for migrations. That narrowness is a feature when you are building a service and a cost when you are building a content-heavy web application. We are clear about which side of that line your project falls on before we recommend it.
Best for — Typed, high-concurrency Python APIs and microservices — especially ML and LLM backends — where async I/O and automatic validation earn their keep and a senior team owns the pieces FastAPI deliberately leaves to you.
Why teams choose FastAPI
Validation and docs from your types
Because Pydantic reads your type hints, request validation, response serialisation, and interactive OpenAPI docs all come from one source of truth. Malformed input is rejected at the edge with a clear error, and the docs can never drift out of sync with the code, because they are the code.
Real concurrency for I/O-bound work
The async core lets a single worker keep thousands of connections in flight while each waits on a database or upstream service. For workloads that are mostly waiting — which most API workloads are — that is a large throughput gain over a synchronous framework on the same hardware.
Native home for ML and AI
FastAPI lives in Python, so a model-serving endpoint sits inches from PyTorch, LangChain, and the OpenAI SDK. You avoid the cost and fragility of moving tensors or prompts across a language boundary, and your API and your model code share one toolchain.
Why businesses choose FastAPI
- You are building APIs or microservices and want validation, serialisation, and accurate documentation to fall out of your type hints rather than be maintained by hand.
- Your traffic is concurrent and I/O-bound, and you want the throughput of an async runtime without leaving Python.
- You are putting machine-learning or LLM capabilities behind an HTTP interface and want the API and the models in the same language and toolchain.
- You have — or want us to provide — the senior judgement to choose an ORM, an auth approach, and an async data layer correctly, once, and to keep the async code honest under load.
What we build with FastAPI
The capabilities this technology is genuinely strong at — and what we most often build with it.
Pydantic models as the request/response contract
Endpoints declare their inputs and outputs as Pydantic models. FastAPI validates and coerces incoming JSON against them, returns structured 422 errors on bad input, and serialises responses to match — and we use response_model to make sure internal fields never leak to clients by accident.
Automatic OpenAPI and Swagger UI
The framework generates an OpenAPI schema and interactive Swagger and ReDoc pages from your annotations. Client teams get a live, accurate reference and can generate typed clients from the schema, so the contract between services is machine-checked rather than described in a stale wiki.
Async endpoints on Starlette/ASGI
Handlers written with async def run on an event loop, so a worker serves many concurrent requests while each awaits I/O. We pair this with async database drivers and httpx so the whole request path is non-blocking — because one synchronous call in the middle defeats the point.
Dependency injection
FastAPI’s Depends system supplies database sessions, the current user, settings, and shared setup to endpoints as declared dependencies. It makes wiring explicit and, crucially, trivially overridable in tests, so we can swap a real database session for a fake without touching the endpoint code.
Background tasks and streaming responses
Lightweight follow-up work — sending an email, writing an audit record — runs in a BackgroundTask after the response is returned, and StreamingResponse lets us stream tokens from an LLM or rows from a large query to the client incrementally rather than buffering the whole payload.
WebSockets and long-lived connections
Because it is built on Starlette, FastAPI handles WebSocket endpoints as a first-class citizen, which suits streaming inference, live dashboards, and chat backends where a request/response round trip is the wrong shape.
Use cases
ML and LLM inference backends
An HTTP or WebSocket layer in front of a model — a PyTorch network, a LangChain pipeline, or a call out to the OpenAI SDK — with typed request validation, token streaming, and the model code living in the same Python process or a nearby worker.
High-concurrency service APIs
Public or internal APIs that fan out to databases and upstream services on every request, where async concurrency lets a modest fleet of workers absorb load that would need far more synchronous processes.
Microservices and internal platforms
Small, focused services with machine-checked OpenAPI contracts between them, so teams can consume each other’s endpoints with generated clients rather than guesswork.
Data and integration middleware
Services that sit between systems — validating, transforming, and forwarding data between third-party APIs, queues, and your own stores — where Pydantic validation at the boundary catches bad data before it spreads.
When FastAPI is the right choice
- You are building an API or a set of microservices — typed HTTP or WebSocket endpoints consumed by a front end, mobile app, or other services — rather than a server-rendered website. This is FastAPI’s home ground.
- Your workload is I/O-bound and concurrent: the service spends its time waiting on databases, third-party APIs, queues, or model servers, and async lets one process serve many requests at once instead of blocking on each.
- You are serving ML or AI models — inference endpoints, an LLM backend, a retrieval pipeline — and you want to stay in the Python ecosystem next to PyTorch, LangChain, and the OpenAI SDK rather than shipping data across a language boundary.
- Wrong for: a content-driven website that needs server-rendered pages, an admin interface, and a CMS out of the box. Django gives you all of that on day one; with FastAPI you would rebuild it. Reaching for FastAPI here is choosing more work.
- Wrong for: CPU-bound heavy lifting inside the request. Python and CPython’s GIL mean genuine parallel computation does not happen in one process — number-crunching, large synchronous model inference, or tight compute loops belong in a worker, a background task, or a different runtime, not in an async endpoint where they will stall every other request on that worker.
FastAPI: pros and cons
Strengths
- Type-hint-driven validation, serialisation, and OpenAPI documentation from a single source, which removes a whole class of contract bugs and keeps docs honest.
- Native async/await on an ASGI core gives excellent throughput on concurrent, I/O-bound workloads — comparable to Node and Go, well beyond synchronous Python frameworks.
- It lives in the Python ecosystem, which makes it the natural choice for serving ML models and LLM backends alongside PyTorch, LangChain, and the OpenAI SDK.
- A clean dependency-injection system makes database sessions, authentication, and shared setup explicit, testable, and easy to override in tests.
Trade-offs
- API-only by design: no ORM, no admin, no templating, no built-in auth. You assemble data access, migrations, and auth yourself, which is more setup than a batteries-included framework.
- A younger and smaller ecosystem than Django. For an unusual requirement you are more likely to build it than to install it, and there are fewer battle-tested conventions to copy.
- It is still Python: CPU-bound work is bound by CPython and the GIL, so heavy computation in a request will block the worker and must be pushed to background workers or another runtime.
- Async correctness takes discipline. A single blocking call — a synchronous database driver, a slow library, time.sleep — stalls the whole event loop, and these mistakes are quiet until the service is under load.
Architecture
FastAPI hands you a request layer and little else, so the architecture is where the project is decided. We settle the load-bearing choices early: the data layer (SQLAlchemy with an async driver, or a lighter query layer where an ORM is overkill), migrations via Alembic, how the application is structured into routers by feature, and where the dependency-injection boundaries sit for sessions, settings, and the current user. Getting these right up front is cheap; retrofitting them into a sprawling app is not.
We keep the async path honest end to end. An async endpoint is only as concurrent as its slowest blocking call, so the database driver, HTTP client, and any library touched inside a request must be async-aware — and genuinely synchronous work, including CPU-bound model inference, is pushed to a thread pool, a background worker, or a separate service rather than run inline. For ML backends, we are deliberate about where the model lives: loaded once at startup and shared, or fronted by a dedicated inference worker, so a heavy forward pass never blocks the event loop serving everyone else.
Performance
On I/O-bound workloads FastAPI performs well above synchronous Python frameworks and holds its own against Node and Go, because the async core keeps a worker busy across many in-flight requests instead of blocking on each. Realising that in practice means running under an ASGI server such as Uvicorn, sizing worker processes to CPU cores, and making sure every I/O call in the hot path is non-blocking. We profile real endpoints under representative concurrency rather than trusting a framework benchmark.
The honest limit is CPU. Python and the GIL mean a single process does not do parallel computation, so any heavy synchronous work in a request — serialising huge payloads, running a large model, tight loops — will stall that worker and everything queued behind it. We measure where the time actually goes, move compute off the event loop, and scale CPU-bound stages horizontally as separate workers rather than pretending async makes computation faster. It does not; it makes waiting cheaper.
Security
Pydantic validation at the edge is a genuine security asset: malformed or unexpected input is rejected before it reaches your logic, which closes off a large share of injection and type-confusion problems by default. We build on that with explicit authentication and authorisation — OAuth2 or JWT bearer tokens wired through the dependency system so that protected endpoints declare their requirements and cannot be reached without them — and we enforce authorisation on the server, never by hiding an option in the client.
Because FastAPI ships no auth or user system, these are decisions we make deliberately rather than defaults we inherit. We configure CORS narrowly, keep secrets in the environment and out of the code, use parameterised queries or an ORM to prevent SQL injection, and set sensible rate limits in front of the service. For LLM backends we treat prompts and tool inputs as untrusted, guarding against prompt injection and never letting model output reach a shell, a query, or a filesystem without validation.
Scalability
FastAPI services are stateless request handlers, which makes them straightforward to scale horizontally: containerise the app, run it under Uvicorn with an appropriate worker count, and put copies behind a load balancer or a Kubernetes deployment that scales on concurrency and latency. Because async workers absorb concurrent I/O well, you often need fewer instances than a synchronous stack would demand for the same traffic.
The parts that need thought are the ones FastAPI does not own. Shared state belongs in Postgres and Redis, not in process memory, so any instance can serve any request. Long-running and CPU-bound work moves to a background worker queue rather than the request path. For model serving, we scale the API tier and the inference tier independently — a cheap fleet of API workers in front of a smaller number of GPU-backed inference workers — because their cost and scaling profiles are nothing alike, and coupling them wastes money.
FastAPI integrations & ecosystem
The technologies we most often pair with it — each links to how we work with it.
How we work
We start by settling the decisions FastAPI leaves open — data layer, migrations, auth, project structure, and where async ends and background work begins — because these are cheap to get right at the start and painful to change once the codebase has grown around a wrong choice. Then we build in thin vertical slices: one real endpoint, validated, tested, documented, and deployed, rather than a scaffold that looks complete but serves nothing.
The engineers who design the service are the ones who write it and keep it running, which is what we mean by operating what we build. Async Python is unforgiving of casual mistakes that only surface under load, so we are the people who will be on the other end of that pager — and we write accordingly: typed, profiled, with the blocking-call traps understood rather than discovered in production.
The service behind it
Delivered throughCustom Software DevelopmentWhat we build with FastAPI
The disciplines this technology most often shows up in — from a first build to taking over and stabilising an existing one.
How we deliver
- 01
Discover
We map the system, the constraints and the business it serves — including the parts nobody documented.
Architecture brief
- 02
Architect
Decisions get made, written down and defended before a line of production code exists.
Decision records
- 03
Build
Short cycles against working software. You see progress in the product, not in a status deck.
Shipping increments
- 04
Operate
Monitoring, incident response and iteration. The system is alive, so the engagement is too.
Runbooks & SLOs
Industries we use FastAPI in
Domain knowledge changes what gets built. A few of the sectors we know before the first meeting.
Also in Backend
Why teams choose us for FastAPI
Senior engineers only
Async Python punishes guesswork and hides its mistakes until load exposes them. The people making your architectural and concurrency decisions have made them before and have run the result — there are no juniors learning async on your project.
We operate what we build
We run the services we ship, so we optimise for the on-call reality, not the demo: non-blocking hot paths, sensible worker sizing, and background work kept off the event loop. You get a service that stays up under real traffic.
Fluent across the Python stack
FastAPI is rarely alone. We bring the surrounding pieces — SQLAlchemy and Alembic, Redis, async drivers, and the ML tooling from PyTorch to LangChain and the OpenAI SDK — so the API and everything behind it are built by one team, not stitched together.
Honest about when not to use it
If your project is really a content site that wants Django’s admin and templating, or your workload is CPU-bound and fighting Python, we will tell you before you spend money. We would rather lose a project than build you the wrong thing.
Typical timeline
- 01
Discovery and architecture
One to two weeks agreeing the endpoints, data layer, auth approach, and async boundaries, and — for ML work — where and how the model is served, so the foundations are settled before code volume grows.
- 02
First vertical slice
Two to three weeks delivering one real endpoint end to end — validated, tested, documented via OpenAPI, and deployed — proving the architecture against reality rather than a plan.
- 03
Iterative build
Endpoint-by-endpoint delivery in short cycles, each shippable, with load testing and async-correctness checks done as we go rather than bolted on at the end.
- 04
Hardening and handover
Load profiling under representative concurrency, security review, test coverage on the load-bearing paths, and documentation so your team can own and extend the service.
How pricing works
- Fixed-scope builds for well-defined services — an API, a model-serving backend, a set of microservices — quoted once we understand the endpoints, the data, and the concurrency profile.
- Monthly senior engagement for ongoing product and platform work, where scope evolves and you want continuity rather than a fixed deliverable.
- Focused audits and rescues for existing FastAPI or Python API codebases — async correctness, performance under load, or a stalled build — priced by the assessment.
Hire FastAPI engineers
Need FastAPI capacity on your own team? We embed named senior engineers into your existing team — reporting to your leads, working in your rituals — so you add capacity without a hiring cycle.
Hire FastAPI engineersCommon questions
FastAPI or Django — how do you choose?
They solve different problems. Django is a batteries-included framework for content-driven web applications: it gives you an ORM, an admin, templating, and auth on day one, and Django REST Framework layers an API on top. FastAPI is API-first and async-first, with far less in the box. If you are building a website with server-rendered pages and an admin, Django. If you are building an API or microservices, especially concurrent I/O-bound ones or ML backends, FastAPI. We have shipped both and will recommend the one that fits, not the one we feel like writing.
What about FastAPI versus Flask?
Flask is a minimal synchronous framework — lovely for small services, but it blocks on each request and bolts async on awkwardly. FastAPI is async-native and gives you Pydantic validation and automatic OpenAPI docs that Flask leaves you to assemble by hand. For a new API where concurrency or a machine-checked contract matters, FastAPI is the stronger default. For a tiny synchronous script-like service where you want nothing clever, Flask is still perfectly reasonable.
Is FastAPI genuinely fast, or is that marketing?
It is genuinely fast for the workload it targets: concurrent, I/O-bound requests, where its async core keeps a worker serving many requests at once and puts throughput in the same range as Node and Go. It is not magic. It does not make CPU-bound work faster — Python and the GIL still apply — and a single blocking call in a request undoes the benefit. The performance is real when the code is written to be genuinely non-blocking, which is a discipline, not a default.
Why is FastAPI a good fit for AI and LLM backends?
Because it lives in Python, next to the tools the models need. Serving a PyTorch model, running a LangChain pipeline, or calling the OpenAI SDK all happen in the same language and process, so you avoid moving data across a language boundary. FastAPI adds typed request validation at the edge, streaming responses for token-by-token output, and WebSockets for chat-style interfaces. We do keep heavy inference off the event loop — in a worker or a dedicated inference tier — so one slow forward pass does not stall every other request.
Does FastAPI come with a database layer?
No, and that is by design. FastAPI is API-only — no ORM, no migrations, no admin. You bring your own data layer, which is usually SQLAlchemy (with an async driver so it does not block the event loop) plus Alembic for migrations, or a lighter query layer where an ORM would be overkill. We make that choice deliberately at the start of a project, because the data layer is one of the decisions that is cheap to settle early and expensive to change once the code has grown around it.
Building on FastAPI?
A technical conversation with the engineers who would do the work. If we are not the right fit, we will say so on the call.