Core API Patterns
Synchronous Chat Endpoint
The synchronous pattern is the simplest to reason about: the client sends a request and waits for the full response. Use this when response times are acceptable (under ~5 seconds) and the client can tolerate blocking. For anything longer, consider streaming or async jobs below.Streaming Endpoint
Streaming is the most important pattern in LLM API design. Without it, users stare at a blank screen for 5-20 seconds wondering if anything is happening. With streaming, the first token typically arrives in 200-500ms, and the user sees the response “typing” in real-time — the same pattern that makes ChatGPT feel responsive. Under the hood, this uses Server-Sent Events (SSE): a simple HTTP-based protocol where the server holds the connection open and pushes data chunks. Think of it like a news ticker — the server keeps broadcasting updates until it’s done.Async Job Pattern
For long-running LLM tasks (document summarization, multi-step agents, RAG pipelines over large corpora), synchronous endpoints fall apart. The client’s HTTP connection times out, the load balancer kills the request, and you’ve wasted compute with nothing to show for it. The async job pattern solves this by decoupling submission from completion. It works like a dry cleaner: you drop off clothes (submit a job), get a ticket number (job ID), and come back later to pick them up (poll or receive a webhook). This pattern is essential when processing takes more than ~30 seconds.Webhook Integration
Webhooks are the “don’t call us, we’ll call you” pattern. Instead of your client polling every few seconds asking “is my job done yet?” (which wastes bandwidth and adds latency), the server proactively notifies the client when something interesting happens. This is the preferred pattern for production LLM systems because it eliminates polling overhead and reduces time-to-notification from your poll interval down to near-zero. The tradeoff: the receiver must expose a public HTTP endpoint, which adds complexity for clients behind firewalls.Rate Limiting
Rate limiting for LLM APIs is doubly important: not only do you need to protect your service from abuse, but every excess request costs you real money in inference charges. A single runaway script can burn through hundreds of dollars in minutes. Unlike traditional APIs where excess requests are just CPU cycles, LLM requests have direct cost implications. LLM APIs need two dimensions of rate limiting: request count (how many calls) and token count (how much compute). A user making 10 requests with 100K tokens each is very different from 10 requests with 100 tokens each, even though the request count is identical.API Versioning
API versioning is inevitable for LLM applications. Models improve, response formats evolve, new capabilities (tool use, structured outputs, vision) get added. If you don’t version from day one, you’ll either break every existing client integration or freeze your API forever. Neither is acceptable. Use URL-path versioning (/v1/, /v2/) for LLM APIs. It’s the most visible, most cacheable, and easiest to route at the load balancer level. Header-based versioning is theoretically cleaner but makes debugging, logging, and documentation harder in practice.
Request Validation
Request validation in LLM APIs serves a different purpose than in traditional APIs. Yes, you’re catching malformed input — but more importantly, you’re preventing expensive mistakes. A validation miss that lets through a 500K character message means you just burned $5-10 on a single API call that was probably a mistake. Validate aggressively at the boundary; it’s the cheapest place to catch errors.Health and Status Endpoints
Health endpoints are not optional for production LLM APIs — they’re how your load balancer, Kubernetes, and monitoring systems know whether your service is alive and ready to serve traffic. The distinction between “alive” (liveness) and “ready” (readiness) matters: a service can be alive but not ready if it’s still loading model weights, warming caches, or waiting for a downstream provider to come back online.API Response Standards
A consistent response envelope makes life dramatically easier for API consumers. Every response should follow the same shape: a success flag, data, an optional error, and metadata. This means clients don’t need to guess whether a 200 response containsresult, data, output, or response — it’s always in the same place.
API Design Checklist
Interview Deep-Dive
Why would you choose streaming SSE over WebSockets for an LLM API, and when might WebSockets be the better choice?
Why would you choose streaming SSE over WebSockets for an LLM API, and when might WebSockets be the better choice?
EventSource API handles it natively). WebSockets add bidirectional capability you rarely need during token streaming, and they require sticky sessions or special load balancer configuration.Choose WebSockets when you need the client to send signals mid-stream — for example, a “stop generating” button, real-time editing where the user is typing while the model is responding, or multi-turn conversational interfaces where latency of establishing new connections matters. The tradeoff is operational complexity: WebSocket connections are stateful, harder to load-balance, and don’t work through all corporate proxies.The key insight is that SSE gives you 90% of the UX benefit with 30% of the operational cost. Most production LLM APIs (OpenAI, Anthropic, Google) chose SSE for exactly this reason.How would you design rate limiting for a multi-tenant LLM API where different customers have different pricing tiers?
How would you design rate limiting for a multi-tenant LLM API where different customers have different pricing tiers?
X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers on every response so clients can implement client-side throttling proactively. Return 429 with a Retry-After header when limits are exceeded.Your LLM API is experiencing 5-second cold starts when a new model version is deployed. How do you handle this without impacting users?
Your LLM API is experiencing 5-second cold starts when a new model version is deployed. How do you handle this without impacting users?
maxUnavailable: 0 and maxSurge to ensure you always have capacity.For the API layer itself, implement a circuit breaker pattern: if error rates on the new version exceed a threshold within the first few minutes, automatically roll back traffic to the previous version. Track latency percentiles (p50, p99), not just averages, because cold starts affect tail latency disproportionately.How do you handle idempotency in an async LLM job API where network failures can cause duplicate submissions?
How do you handle idempotency in an async LLM job API where network failures can cause duplicate submissions?
Idempotency-Key: abc-123). On the server side, before processing a job, check if that key already exists in your store. If it does, return the existing result without reprocessing.Implementation details that matter: store the idempotency key with its result in Redis or a database with a TTL (typically 24-48 hours). The key must be stored before processing begins, not after — otherwise, two concurrent identical requests will both start processing. Use an atomic “set if not exists” operation. If the job is still processing when a duplicate arrives, return the job status (pending/processing) rather than starting a new one.The edge case people miss: what if the initial request fails halfway through? You need to distinguish between “request received but processing failed” (should retry) and “request completed” (return cached result). Store the idempotency record with a status field, and only return cached results for completed successes.Walk through how you would design the error handling strategy for a production LLM API that wraps multiple model providers.
Walk through how you would design the error handling strategy for a production LLM API that wraps multiple model providers.