API Design Best Practices — The Complete Guide
A good API is a promise you can keep for years. This is the map: the conventions, the protocols, and the details that make an API pleasant to use and safe to change.
Key takeaways
- A good API is a promise you can keep for years.
- This is the map: the conventions, the protocols, and the details that make an API pleasant to use and safe to change.
API Design Best Practices — The Complete Guide#
An API is a contract, and the hard part is that you have to keep it for years while everything behind it changes. Good design is what lets you evolve without breaking the clients who depend on you. This guide is the map: the conventions that make an API predictable, the protocols to choose between, and the operational details (versioning, pagination, limits) that decide whether integrating with you is a pleasure or a support ticket.
The one principle underneath all of it: design for the consumer, not the database. An API that mirrors your tables leaks your internals and traps you; an API that models the domain the client actually cares about stays stable while you refactor freely.
Pick the right protocol#
- REST is the default for public and web APIs: resource-oriented, cacheable, universally understood. The conventions that matter are in REST API best practices.
- gRPC wins for internal service-to-service calls that need speed and strict contracts. The trade-offs vs REST are in gRPC vs REST.
- GraphQL shines when clients need flexible, exact-shape queries over a graph of data. When it beats REST (and when it doesn't) is in GraphQL vs REST.
Most systems end up with more than one: REST or GraphQL at the edge, gRPC between services.
| REST | gRPC | GraphQL | |
|---|---|---|---|
| Best for | Public/web APIs | Internal service-to-service | Clients needing flexible, exact-shape queries |
| Payload | JSON, human-readable | Protobuf, binary | JSON |
| Caching | HTTP caching works natively | Needs application-level caching | Needs application-level caching |
| Browser-friendly | Yes | No (needs grpc-web/gateway) | Yes |
| Learning curve | Low | Moderate (schema, codegen) | Moderate (resolvers, N+1 pitfalls) |
Get the contract details right#
These are the details that generate support tickets when they're wrong:
- Versioning: how to change an API without breaking existing clients, in API versioning strategies.
- Pagination: never return an unbounded list; offset vs cursor and their trade-offs are in API pagination.
- Rate limiting: protect the API and communicate limits clearly, in API rate limiting.
- Security: authentication, authorization, and the OWASP API risks are in API security best practices.
The conventions that make an API predictable#
- Use nouns for resources and HTTP methods for actions; return the right status codes (2xx/4xx/5xx meaningfully).
- Be consistent: naming, casing, date formats (ISO 8601), and error shapes should be identical everywhere.
- Return structured, actionable errors (a code, a message, a field) so clients can handle them programmatically.
- Validate input strictly and reject unknown fields; be conservative in what you accept.
- Make writes idempotent where possible (idempotency keys) so retries are safe.
- Document everything with an OpenAPI/schema spec that is the source of truth.
The mental model#
Every API decision is really about change over time: a stable contract, explicit versioning, bounded responses, and clear errors are what let you refactor the implementation freely while clients keep working. Design the contract first (the shape clients see), then build to it. When you must break something, version it and give clients a migration path rather than a surprise.
The call we'd make#
Model the domain the consumer cares about, not your schema. Default to REST for public APIs, reach for gRPC between services and GraphQL when clients need query flexibility, and treat versioning, pagination, rate limiting, and errors as first-class from day one. Publish an OpenAPI spec and keep it honest. Each linked guide goes deep on one decision; start from the protocol choice, then nail the contract details that keep integrations from turning into tickets.
Get the DevOps Troubleshooting Cheat Sheet
Subscribe and get our free one-page reference for the errors that eat an afternoon — CrashLoopBackOff, OOMKilled, Terraform state locks, and more — plus new guides as we publish them.
API Pagination: Offset vs Cursor (and When to Use Each)
Offset pagination is easy until your dataset grows or shifts under load; here's why cursor pagination wins for large, changing, and public APIs.
Event-Driven Architecture — The Complete Guide
Synchronous calls couple your services together and fail together. Events let systems react instead of ask. This is the map: the patterns, the messaging tools, and the delivery guarantees that make it work.
More from DevOps
Explore more articles in this category
GitLab's New Rate Limits: What to Fix Before Oct 19
GitLab is capping unauthenticated API calls at 60 an hour starting October 19, and the preview windows land before most teams will have noticed.
Storm-3068: A CI/CD Pipeline Is a Kubeconfig Exfiltration Machine
Microsoft's Storm-3068 report used zero malware to steal Kubernetes credentials, just a password reset, a pipeline edit, and permissions nobody had scoped down.
Docker Cloud Sandboxes: Why Agents Need MicroVMs, Not Containers
Docker put AI coding agents in hosted microVMs instead of containers, because a container was never the isolation boundary this job needed.
You might have missed
Evergreen posts worth revisiting.