What Makes a Good API
The design principles that separate APIs developers love from ones they dread
A good API does one thing: reduces the cognitive load on the developer using it. Every design decision you make either adds to that load or reduces it. The APIs developers love — Stripe, GitHub, Twilio — are not loved because of their technology. They are loved because their design is consistent, predictable, and forgiving.
The Stripe test
Good API design feels like reading plain English
Read a Stripe API call aloud: "POST to /v1/customers, then POST to /v1/payment_intents with amount and currency." A developer who has never used Stripe can guess what these do. That is the goal. If a developer has to re-read your documentation three times to understand one endpoint, the design has failed.
The five properties of a good API
- Consistency — The same patterns everywhere. If users are listed at /users and their orders at /users/:id/orders, then products should be at /products and product reviews at /products/:id/reviews. Never /product-reviews.
- Predictability — A developer who has used two of your endpoints should be able to guess the third. Naming conventions, response shapes, and error formats should be identical across every endpoint.
- Good error messages — When something goes wrong, tell the developer exactly what went wrong and how to fix it. "Validation failed" is not a good error. "The field email is required and must be a valid email address" is.
- Minimal surface area — Every endpoint you add is a commitment. You must maintain it, document it, and version it. Add endpoints deliberately. A small, coherent API is better than a large, inconsistent one.
- Safe to explore — Read operations (GET) should never cause side effects. A developer exploring your API should be able to fire GET requests without fear of creating or deleting data.
REST vs GraphQL vs tRPC — choosing the right shape
- REST — Resource-based, HTTP verbs, JSON. The standard for public APIs and third-party integrations. Every HTTP client in every language can use it.
- GraphQL — Query language where clients specify exactly what data they need. Eliminates over-fetching. Better for complex data graphs with many consumers needing different shapes.
- tRPC — TypeScript-first RPC for full-stack Next.js apps. No manual schema, no REST conventions — end-to-end type safety. Not suitable for public APIs.
Start with REST for any public API
If you are building an API that third parties will consume, use REST. It has the broadest client support, the most established documentation tooling, and the most developer familiarity. GraphQL and tRPC solve specific problems for specific architectures.
Try this
Find a public API you have used before (GitHub, Stripe, or Twilio work well). Spend 10 minutes reading their API reference without following a tutorial. Notice: how do they name resources? What HTTP methods do they use? What does an error response look like? Write down three design decisions that made the API easy to understand. These are your design targets.