API Design Best Practices
BeginnerGood API design uses consistent naming, proper HTTP methods, pagination, versioning, idempotency keys, and clear error responses. A well-designed API is the contract that holds distributed systems together.
Overview
APIs are the contracts between services and between your system and external clients. A well-designed API is intuitive, consistent, and evolvable. Key principles include: use nouns for resources and HTTP verbs for actions (GET /orders, POST /orders, DELETE /orders/123); use proper HTTP status codes (200, 201, 400, 404, 500); implement pagination for list endpoints (cursor-based for large datasets); version your API (URL path /v1/ or header-based); use idempotency keys for non-idempotent operations (POST with Idempotency-Key header); return structured error responses with error codes and messages; rate-limit to protect your service; document with OpenAPI/Swagger. API design mistakes are expensive — once published, breaking changes affect all consumers.
RESTful Resource Design
Use nouns for URLs, HTTP verbs for actions, and proper status codes. Keep URLs hierarchical and predictable.
// Good REST API design
// Resources as nouns, HTTP verbs for actions
GET /api/v1/users → 200 (list users)
GET /api/v1/users/42 → 200 (get user) / 404
POST /api/v1/users → 201 (create user)
PUT /api/v1/users/42 → 200 (full update)
PATCH /api/v1/users/42 → 200 (partial update)
DELETE /api/v1/users/42 → 204 (no content)
// Nested resources for relationships
GET /api/v1/users/42/orders → user's orders
POST /api/v1/users/42/orders → create order for user
// Filtering, sorting, pagination via query params
GET /api/v1/orders?status=SHIPPED&sort=-created_at&page=2&limit=20
// HTTP status codes
// 200 OK, 201 Created, 204 No Content
// 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found
// 409 Conflict, 422 Unprocessable Entity, 429 Too Many Requests
// 500 Internal Server Error, 503 Service UnavailablePagination, Versioning & Idempotency
Cursor-based pagination scales better than offset-based. Version APIs to evolve without breaking clients. Idempotency keys prevent duplicate operations for non-idempotent methods.
// Cursor-based pagination (better for large datasets)
GET /api/v1/orders?limit=20&cursor=eyJpZCI6MTAwfQ==
// Response:
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTIwfQ==",
"has_more": true
}
}
// Why: offset-based (OFFSET 10000) gets slower as offset grows
// API versioning strategies
// 1. URL path (most common): /api/v1/users, /api/v2/users
// 2. Header: Accept: application/vnd.myapi.v2+json
// 3. Query param: /api/users?version=2
// Idempotency key — prevent duplicate charges
POST /api/v1/payments
Headers:
Idempotency-Key: "pay-req-abc-123"
Body: { "amount": 99.99, "currency": "INR" }
// Server: if key seen before, return cached response (no double charge)
// Stripe, Razorpay, and most payment APIs support thisError Handling & Documentation
Return structured error responses with machine-readable codes and human-readable messages. Document with OpenAPI (Swagger) for auto-generated client SDKs and interactive docs.
// Structured error response
{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Account balance is below the required amount.",
"details": {
"required": 99.99,
"available": 50.00,
"currency": "INR"
},
"request_id": "req-abc-123",
"documentation_url": "https://docs.example.com/errors/INSUFFICIENT_BALANCE"
}
}
// OpenAPI spec (auto-generates docs + client SDKs)
openapi: 3.0.3
paths:
/api/v1/orders:
get:
summary: List orders
parameters:
- name: status
in: query
schema:
type: string
enum: [PENDING, SHIPPED, DELIVERED]
- name: cursor
in: query
schema: { type: string }
responses:
'200':
description: Paginated list of orders
'401':
description: UnauthorizedKey Points to Remember
- 1Use nouns for resources, HTTP verbs for actions, and proper status codes.
- 2Cursor-based pagination scales better than offset-based for large datasets.
- 3Version APIs from day one — URL path versioning (/v1/) is the most common approach.
- 4Idempotency keys prevent duplicate operations for non-idempotent methods (POST payments).
- 5Always return structured error responses with machine-readable codes and request IDs.
Interview Questions
Sign in to ask AriaWhat makes a REST API "well-designed"?
Why is cursor-based pagination better than offset-based?
How do you handle API versioning without breaking existing clients?
What is an idempotency key and why is it important for payment APIs?
Design the API layer for a ride-sharing application like Uber.
Ask Aria about API Design Best Practices
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.