HTTP Methods, Status Codes, and Headers in API Design
Choose HTTP methods that match the operation, return status codes clients can act on, and use headers for metadata, caching, and safe retries.
HTTP methods, status codes, and headers give API clients consistent signals about requests and their outcomes. This guide explains safe and idempotent method behavior, common success and error codes, and headers for content negotiation, caching, resource locations, and conditional updates. It also shows how idempotency keys and clear retry guidance help prevent duplicate work after timeouts. Use these rules to make an API easier to integrate with and safer to operate.
HTTP Methods, Status Codes, and Headers in API Design
Introduction
An API client needs more than a URL and a JSON body to understand a request. The HTTP method communicates the requested action, the status code reports the outcome, and headers carry metadata that changes how the request or response should be handled. These parts work together: a POST that creates an order should normally return 201 Created and a Location header pointing to the new order.
If these signals are consistent, clients can retry, cache, and recover without endpoint-specific guesswork. The HTTP and HTTPS protocol guide explains the wire protocol in more depth; here we focus on API design decisions.
Methods express intent
Choose a method that describes the operation, then keep its behavior consistent across routes. Safe methods do not ask the server to change business state; idempotent methods have the same intended effect when repeated, even if the response differs.
| Method | Typical use | Safe | Idempotent |
|---|---|---|---|
GET |
Read a resource or collection | Yes | Yes |
POST |
Create a resource or run a command | No | No, unless the API defines deduplication |
PUT |
Replace a resource at a known URL | No | Yes |
PATCH |
Apply a partial update | No | Depends on the patch operation |
DELETE |
Remove a resource | No | Yes |
For example, GET /orders/ord_803 reads an order, while POST /orders creates one. A client can safely repeat a PUT replacement after a timeout, but a repeated POST may create a duplicate unless the API supports an idempotency key.
Status codes help clients decide what to do
Use a small, predictable set. 200 OK fits a successful response with a representation, 201 Created signals creation, and 204 No Content means success with no body. 202 Accepted means work was accepted but is not complete. On errors, distinguish authentication (401), authorization (403), missing resources (404), state conflicts (409), invalid request syntax (400), and semantically invalid fields (422) according to the API’s documented policy. 429 should represent throttling and usually include Retry-After. Reserve 500 for unexpected server faults; do not turn every failure into 200 with { "success": false }.
A status code is not a full error explanation. Pair it with a stable error body containing a machine-readable code and a safe human-readable message. Avoid returning stack traces or internal database details.
Headers carry shared rules
Content-Type tells the receiver how to parse the body. Accept tells the server what representation the caller can consume. Authorization carries credentials; Cache-Control governs reuse; ETag and If-None-Match support conditional requests; Location identifies a newly created resource or redirect target. Use standard headers where they fit instead of inventing X- fields for ordinary HTTP behavior.
POST /api/orders HTTP/1.1
Content-Type: application/json
Accept: application/json
Idempotency-Key: 7da7e4b9-6f33-4ac2-a63d-8cb3e9f7db31
{"sku":"kbd-42","quantity":1}
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/orders/ord_803
Cache-Control: no-store
{"id":"ord_803","status":"pending"}
Request handling flow
sequenceDiagram
participant C as Client
participant A as API
participant D as Data store
C->>A: Method, path, headers, body
A->>A: Authenticate and validate
A->>D: Apply operation
D-->>A: Result or known failure
A-->>C: Status, headers, representation
When to use and when not to use
Use HTTP semantics for public web APIs, browser-facing services, and integrations that benefit from standard clients, proxies, and observability. Do not overload GET to trigger business actions, and do not add custom headers for concepts already covered by HTTP. For a command that does not map cleanly to resource state, a clearly documented action endpoint can still use POST; forcing every action into a misleading PUT is worse than a small exception.
Trade-Off Table
| Choice | Strength | Cost |
|---|---|---|
| Standard status codes | Familiar client behavior | Teams must agree on edge cases |
| Idempotency key on retryable POST | Prevents duplicate effects | Requires key storage, expiry, and request matching |
| Conditional headers and ETags | Saves bandwidth and avoids lost updates | Cache validators add implementation work |
| Custom status or header conventions | Can fit a local system | Every client needs special handling |
Implementation snippet
A handler should choose the status and headers at the point where it knows the outcome. Framework syntax differs, but the contract can stay stable:
async function createOrder(request: Request): Promise<Response> {
const input = await request.json();
const order = await orders.create(validateOrder(input));
return Response.json(order, {
status: 201,
headers: {
Location: `/api/orders/${order.id}`,
"Cache-Control": "no-store",
},
});
}
In production, validate Content-Type before parsing, validate the body, and ensure the idempotency policy is implemented by the service rather than only by the client.
Production failure scenarios and mitigations
A proxy retries a timed-out POST after the first request committed. Use idempotency keys for operations with costly side effects and scope each key to the authenticated caller. A client treats every 4xx as permanent, but the server used 429 for throttling; publish retry guidance and Retry-After. A successful delete returns a JSON body with 204; some clients reject it. Make body behavior match the status and cover it in contract tests.
Observability checklist
- Record method, route template, status class, latency, and request ID; avoid raw query values that may contain secrets.
- Track rates of
4xx,5xx,429, and idempotency replays separately. - Alert on unexpected status shifts and elevated retry volume.
- Preserve
Retry-Afterand correlation IDs in logs so support can trace client reports.
Security and Compliance Notes
Never put bearer tokens or private data in URLs, where logs and referrers can expose them. Treat Origin, forwarding, and cache headers as untrusted input unless a trusted proxy sets them. Ensure shared caches do not store personalized responses; use suitable Cache-Control directives. Require HTTPS for credentials and sensitive data, and redact authorization and idempotency values from logs. For regulated data, document which response fields and audit events are retained, who can access them, and how long they remain available; the applicable rules depend on the data and jurisdiction.
Common Pitfalls / Anti-Patterns
State-changing GET routes can be triggered by crawlers, prefetching, or retries that assume safe methods have no side effects. Returning 200 for errors makes client retry and monitoring logic unreliable. Leaking framework exceptions can expose implementation details, while using 401 when an authenticated user lacks permission (403) sends the wrong recovery signal. Avoid sending a body with 204 No Content, and do not assume a retry is safe unless the operation is idempotent or protected by an idempotency key.
Quick Recap Checklist
- Keep safe methods free of requested state changes and document which methods are idempotent.
- Choose status codes that match whether work completed, failed, or was accepted for later processing.
- Set Content-Type for request and response bodies and use Accept for response negotiation.
- Document cache, authentication, and retry-related headers as part of the API contract.
Interview Questions
PUT is idempotent but not safe, while GET is expected to be both.202 Accepted when the request is accepted for asynchronous processing but the work has not finished. Give the client a way to check progress, such as a status resource or documented callback.Retry-After guidance when present, then retry with bounded backoff. Avoid immediate repeated requests that can extend the rate limit or add load.
Further Reading
- JSON, Content Types, and Serialization — media types and representation formats.
- API Requests, Responses, and Error Shapes — request structure and consistent error bodies.
- RFC 9110: HTTP Semantics — Method properties, status codes, and general HTTP semantics.
- RFC 9111: HTTP Caching — Cache directives and validation behavior.
- MDN: HTTP request methods — Practical method reference with safety and idempotency notes.
Conclusion
HTTP already provides a shared vocabulary for API intent and outcomes. Use it consistently, document the exceptions, and make retry and cache behavior explicit so clients can act safely.
Category
Related Posts
API Request, Response, and Error Shapes Clients Can Trust
Design consistent API request and response envelopes, validation errors, and problem details so client developers can handle success and failure reliably.
API Resource Names, Relationships, and Collections
Model API URLs around stable resources and relationships, with clear collection behavior that clients can navigate without learning server internals.
API Filtering, Sorting, Pagination, and Field Selection
Build collection endpoints that let clients narrow, order, page through, and shape results without slow queries or unstable response behavior.