REST API Design: Mistakes I've Made So You Don't Have To
I’ve designed, built, and maintained enough REST APIs to have made most of the classic mistakes. Here’s what I wish someone had told me upfront.
1. Versioning Early
Don’t wait until you need it. Version your API from day one, either in the URL (/v1/users) or via the Accept header. Retrofitting versioning onto an unversioned API is painful for everyone involved.
My preference: URL-based versioning. It’s visible, impossible to forget, and trivially cacheable.
2. Pagination by Default
Every list endpoint should be paginated. Every. Single. One.
{
"data": [...],
"pagination": {
"cursor": "eyJpZCI6MTIzfQ==",
"has_more": true,
"total": 847
}
}I’ve moved from offset-based to cursor-based pagination almost everywhere. Cursors handle real-time data changes gracefully and perform better on large datasets.
3. Error Responses Should Be Consistent
A predictable error shape means less client-side code:
{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Account balance too low for this transaction",
"details": {
"required": 29.99,
"available": 12.50
}
}
}Never expose stack traces in production. Never leak internal IDs without intent.
4. Idempotency Keys
For any mutating endpoint, support an Idempotency-Key header. Your payment team will thank you, and so will your users when a retry doesn’t double-charge them.
5. Think in Terms of Deprecation
Add a Deprecation and Sunset header to old endpoints:
Deprecation: true
Sunset: Sat, 01 Aug 2026 00:00:00 GMTGive consumers time to migrate, then actually follow through on removal. Keeping deprecated endpoints around “just in case” is technical debt with interest.
Good API design is mostly about empathy for the consumer. Ask yourself: would you enjoy integrating with this?