Payment APIs
What's distinctive: high-stakes mutations, regulatory pressure, retry semantics that have to be exactly right because duplicate charges are a real problem.
Patterns that matter most:
- Idempotency keys on every charge endpoint, non-optional. Duplicate charges from retries are unacceptable; the idempotency key contract is how you make retries safe.
- Signed webhooks for payment-event notifications. The receiver verifies the signature on every event; the sender retries with backoff for hours or days.
- Strong authentication, often with separate test and live key prefixes (
sk_test_,sk_live_) so production credentials can't accidentally hit test data. - Strict error envelopes — the difference between "card declined" (don't retry, surface to user) and "transient gateway error" (retry safely) has to be unambiguous.
Real-time collaborative apps
What's distinctive: multiple clients seeing the same state, low-latency updates, conflict resolution when two users edit at once.
Patterns that matter most:
- WebSockets for the real-time channel. Each client opens a long-lived connection; the server pushes updates as they happen.
- Heartbeats every 20-30 seconds to detect dead connections. Without them, "connected" doesn't mean what you think it means.
- Sequence numbers on every event, so clients can detect missed messages on reconnect and request a catch-up.
- Conflict resolution: usually CRDT-based or operational transformation for text editors; simpler last-writer-wins for less-conflicting data.
- REST for the non-real-time operations (initial load, user management, settings). WebSockets carries deltas; REST carries snapshots.
Mobile app backends
What's distinctive: high-latency networks, intermittent connectivity, payload size matters, multiple screens that each need a different cut of data.
Patterns that matter most:
- Either GraphQL (so each screen fetches exactly what it needs in one round trip) or REST with backend-for-frontend (BFF) endpoints that aggregate the right data per screen. Either approach beats raw REST with per-resource endpoints, where mobile clients pay for many round trips.
- Aggressive client-side caching with explicit invalidation. The mobile app should be usable offline against cached data; the cache invalidates on a push from the server (via webhook to the user's device, via FCM/APNS) or on next-fetch staleness check.
- Compact response formats — JSON is fine, but trim unused fields aggressively. Bandwidth costs the user real money on cellular.
- OAuth with refresh tokens for session management. Long-lived refresh tokens mean the user doesn't have to re-login often; short-lived access tokens limit the window when a leaked one is useful.
Third-party SaaS integrations
What's distinctive: two organizations with their own change cadences, asynchronous coordination, no shared trust boundary.
Patterns that matter most:
- OAuth 2.0 for delegated access. Users grant permission for one app to act on their behalf in another; revoking that permission has to be a one-click operation in either direction.
- Webhooks in both directions when events drive coordination — A telling B that something happened, and vice versa. The webhook design page covers the production patterns.
- Versioned APIs with explicit deprecation timelines (12 months minimum). The integrating side has its own release schedule and can't be expected to follow the producing side's surprise breaking changes.
- Sandbox environments for integration testing. Real credentials hitting test data; clear separation from production.
Public read-only APIs
What's distinctive: high traffic, mostly cacheable, abuse is the main operational concern.
Patterns that matter most:
- REST with aggressive caching — both expiration-based (
Cache-Control) for resources that don't change often and validation-based (ETag) for those that change but where bandwidth matters. CDN in front to absorb traffic. - Rate limiting per API key with documented limits per tier. Free tier with low limits, paid tiers with higher. Send the rate-limit headers on every response so well-behaved clients can self-throttle.
- Cursor pagination on all collection endpoints. Offset pagination at scale becomes a denial-of-service primitive.
- API key registration with email verification. Lets you ban abusers and contact integrators about deprecations.
File upload and download
What's distinctive: large payloads, network reliability matters, the API is mostly metadata around an object store.
Patterns that matter most:
- Pre-signed URLs. Don't proxy file bytes through your API. Issue a short-lived signed URL pointing at object storage (S3, GCS); the client uploads or downloads directly. Saves bandwidth, decouples your API from upload speed.
- Multipart upload for large files. Splits a large file into chunks that can be uploaded independently and retried per-chunk. Built into S3 and most object stores.
- Resumable uploads. Tus protocol or the object store's equivalent. A flaky network shouldn't force restarting from byte zero.
- Async processing. Upload finishes; processing (virus scan, transcode, indexing) happens in the background. Status accessed via a follow-up call or webhook.
Search APIs
What's distinctive: high cardinality of queries, latency-sensitive, results that change as data changes underneath.
Patterns that matter most:
- REST for the request shape, with rich query parameters for filters, sorts, facets. JSON-encoded complex filters in the request body if the URL gets unwieldy.
- Cursor pagination with relevance score in the cursor. Offset pagination breaks when the underlying index shifts.
- Strict per-query timeouts. A bad query shouldn't tie up resources indefinitely.
- Per-field sortability and filterability documented explicitly. Arbitrary sort on un-indexed fields is a denial-of-service primitive.
- Result freshness vs latency trade-off documented. Most search APIs are eventually consistent; the staleness window is part of the contract.
Bulk and batch operations
What's distinctive: request size, partial success, processing time longer than a request can wait for.
Patterns that matter most:
- Async submission. POST submits the batch and returns 202 with a job ID. Subsequent GETs check status; the result is available when processing completes. Don't try to do bulk work inside one synchronous request.
- Per-item status in the result. A batch of 1000 items where 5 failed should return per-item statuses, not a global pass/fail. The error handling conventions page covers the partial-success patterns.
- Idempotency keys per logical batch. Retrying a batch submission should not duplicate work.
- Hard caps on batch size. Documented and enforced. "Up to 1000 items per batch" is a contract; beyond that, the client should split into multiple batches.
Internal microservice APIs
What's distinctive: trusted callers, low-latency requirements, often binary protocols, change cadence faster than public APIs.
Patterns that matter most:
- gRPC or REST depending on what your stack uses. gRPC for tight latency budgets and many small calls; REST when humans need to debug it with cURL.
- Service-to-service authentication with mutual TLS or short-lived tokens issued by an internal identity service. Long-lived API keys between internal services accumulate risk over time.
- Versioning is easier — fewer consumers, faster coordination — but still needed. Don't change wire formats without a deprecation cycle.
- Per-call observability with distributed tracing. Internal APIs are where most production debugging happens; tracing pays for itself many times over.
- Circuit breakers between services. The integration guide covers the patterns; for internal calls, the cost of unprotected dependencies cascades faster.
Where to go next
For the underlying patterns referenced throughout this page, see the reference. For long-form articles on the recurring subtopics, see the blog. If your use case isn't here and you wish it was, the contact page has the address.