Somewhere past the fiftieth endpoint, you stop thinking about APIs one at a time and start noticing the patterns that make a whole surface coherent — or a mess. Here's what stuck.
Consistency beats cleverness
The single most valuable property of an API surface is predictability. If listing endpoints paginate one way, they should all paginate that way. If errors come back in a shape, every error comes back in that shape. A consumer should be able to guess the next endpoint correctly. Clever, bespoke endpoints feel smart and cost everyone forever.
Design the error responses first
Happy-path JSON is the easy 80%. The APIs that were pleasant to integrate were the ones where I'd decided up front what a validation failure, a conflict, and a not-found looked like — same envelope, useful message, stable codes. Errors are part of your contract, not an afterthought.
Validate at the boundary, ruthlessly
Every endpoint is a door into the system. On platforms that touch real infrastructure, an unvalidated request isn't a 400 — it's a bad write to a live device. Validation at the edge is the cheapest place to stop a problem and the most expensive place to skip.
Make the API tell the truth about state
The best endpoints don't just do a thing; they let the caller understand the state they're now in. Return enough that the client isn't forced into a second round-trip to figure out what just happened.
Name things for the domain
/service-requests/{id}/approvals reads itself. Resist the urge to leak implementation detail into the URL. The API is the part of the system other people live in — it should speak their language, not your database's.
Seventy endpoints in, the lesson is almost boring: an API is a product with users, and its users are developers. Treat their time as the scarce resource it is.