The API That Made Me Rage-Quit
I was integrating a payment API. Sent a request with amount: 1000. Got back 500 Internal Server Error with no details.
After 2 hours of trial and error, I discovered the API expected the amount as a string, not a number. "1000", not 1000.
A proper error would have told me: “Amount must be a string representing cents, received number.”
Good error messages are the difference between a good API and a rage-inducing one.
REST Basics
URLs Should Be Nouns, Not Verbs
❌ GET /getUsers
❌ POST /createUser
✅ GET /users (list users)
✅ POST /users (create user)
✅ DELETE /users/123 (delete user)
The HTTP method is the verb. The URL is the noun.
HTTP Methods
- GET: Retrieve (idempotent)
- POST: Create
- PUT: Update/Replace (idempotent)
- PATCH: Partial Update
- DELETE: Remove (idempotent)
Idempotent means calling it multiple times has the same effect as calling it once.
Resource Nesting
Keep nesting shallow - max 2-3 levels:
✅ GET /users/123/posts
✅ GET /posts/456/comments
❌ GET /users/123/posts/456/comments/789/likes/101
Response Structure
Success Response
{
"data": {
"id": "123",
"name": "John Doe"
}
}
For lists, include pagination:
{
"data": [...],
"pagination": {
"page": 1,
"perPage": 20,
"total": 42
}
}
Error Response
This is the most important part:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"message": "Email is required"
}
],
"requestId": "req_abc123"
}
}
Include a machine-readable error code, human-readable message, field-level details, and a request ID for debugging.
HTTP Status Codes
Use them correctly:
Success (2xx)
- 200 OK: Request succeeded
- 201 Created: Resource created
- 204 No Content: Success, no body
Client Errors (4xx)
- 400 Bad Request: Invalid syntax
- 401 Unauthorized: Authentication required
- 403 Forbidden: Not authorized
- 404 Not Found: Resource doesn’t exist
- 422 Unprocessable: Validation failed
- 429 Too Many Requests: Rate limited
Server Errors (5xx)
- 500 Internal Server Error
- 503 Service Unavailable
Never return 200 with an error message in the body. Use proper status codes.
Pagination
Offset-based: Simple, lets users jump to any page. Good for most cases.
GET /users?page=1&perPage=20
Cursor-based: Better for feeds. Consistent results even as data changes.
GET /posts?cursor=abc123&limit=20
Filtering and Sorting
Support common query patterns:
GET /products?category=electronics&inStock=true&minPrice=100
GET /users?sortBy=createdAt&order=desc
GET /users?search=john
Versioning
Use URL versioning - it’s the clearest:
https://api.example.com/v1/users
https://api.example.com/v2/users
Authentication
JWT Tokens:
Authorization: Bearer eyJhbGc...
API Keys for server-to-server:
X-API-Key: sk_live_abc123
OAuth 2.0 for third-party integrations.
Rate Limiting
Include headers showing limits:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1635724800
When exceeded, return 429 with a clear error message explaining when they can retry.
Validation
Fail fast with clear messages. Validate early and return all errors at once, not one at a time. Tell users exactly what’s wrong and what they should send instead.
API Design Checklist
- URLs use nouns, not verbs
- HTTP methods used correctly
- Consistent response structure
- Descriptive error messages with codes
- Proper HTTP status codes
- Pagination for lists
- Filtering and sorting support
- Versioning strategy in place
- Authentication implemented
- Rate limiting configured
- Input validation
- API documentation
- Tests for all endpoints
Good API design is about empathy. Put yourself in the shoes of the developer using your API.
Would you want to integrate with this? Would the error messages help you debug? Is the documentation clear?
Build APIs you’d want to use.