12 REST API Best Practices That Hold Up in 2026
REST API best practices come up in every API review, but the list that actually matters is shorter than most “essential best practices” articles suggest. Twelve patterns hold up across language, scale, and use case, and the rest are usually variations or implementation details. This guide walks through each, with the 2026 additions that account for AI agent traffic and MCP-mediated consumption.
For the broader strategic decisions that shape an API’s design, see our API design principles guide. This post focuses on the concrete patterns developers apply at the design and implementation level.
1. Use nouns, not verbs, in endpoint paths
/orders, not /getOrders. The HTTP method is the verb; the URL identifies the resource. Stripe’s /v1/charges, GitHub’s /repos, and OpenAI’s /v1/chat/completions all follow this. Putting the verb in the URL (POST /createUser) duplicates what the method already says and breaks the consistency every REST consumer expects.
2. Pluralize collections, singularize items
/orders for the list, /orders/42 for one order. The exception is singletons (/me, /health) where there is only ever one instance. Mixing /order/42 with /orders is the most common naming inconsistency and the first thing developers complain about in API reviews.
3. Nest resources only when relationships are containment
/users/123/orders reads cleanly when an order belongs to a single user. /companies/1/users/2/orders/3/items/4 does not. Two levels of nesting is the practical maximum; beyond that, switch to a top-level resource with filter parameters (/items?orderId=3).
4. Map HTTP methods to CRUD cleanly
Five methods cover almost every REST endpoint:
| Method | Action | Idempotent? |
|---|---|---|
| GET | Retrieve | Yes |
| POST | Create | No |
| PUT | Replace | Yes |
| PATCH | Partial update | Sometimes |
| DELETE | Remove | Yes |
The discipline that compounds: use the verb that matches the action, and do not invent endpoints like POST /createUser. The HTTP method says “create”; the URL identifies the resource.
5. Return the right HTTP status code
200 OK for success on GET/PUT/PATCH, 201 Created (with a Location header) for POST that creates a resource, 204 No Content for DELETE. 400 for malformed requests, 401 for missing or invalid auth, 403 for permission denied, 404 for missing resources, 429 for rate limit, 5xx for server-side errors. The full reference lives in our HTTP status codes guide.
The mistake to avoid: returning 200 OK with {"error": "not found"} instead of a real 404. The status code is the contract; do not overload the body to compensate for the wrong code.
6. Return a structured error envelope
Every error response should include three things: a machine-readable code, a human-readable message, and (for validation errors) the field that failed. A pattern most production APIs converge on:
{
"error": {
"code": "invalid_email",
"message": "Email address is not valid.",
"field": "email"
}
}
Consumers parse the code to handle errors programmatically; they do not string-match the message, which is fragile across translations and minor wording changes.
7. Use query parameters for filtering, sorting, and pagination
Path parameters identify resources (/orders/42). Query parameters modify the request (/orders?status=paid&limit=20). The conventional shape:
- Pagination:
?limit=50&offset=100or?cursor=abc123for cursor-based. - Filtering: named query parameters per filterable field:
?status=paid&customer_id=42. - Sorting:
?sort=created_ator?sort=-created_at(the minus sign for descending). - Field selection:
?fields=id,name,emaillets clients trim payload.
The deeper rules and tradeoffs are in our query parameters guide.
8. Version deliberately and publish a deprecation policy
URI versioning (/v1/orders) is the most common and easiest to debug. Header versioning is cleaner but less consumer-friendly. Whichever you pick, the part that compounds is the deprecation policy: how much notice you give before removing a version. Standard is 12 months for paid public APIs, 6 months for free public APIs, 90 days for internal APIs. State it on day one.
Use the IETF-standard Sunset (RFC 8594) and Deprecation (RFC 9745) HTTP response headers to communicate deprecation programmatically. Well-behaved SDKs warn developers at build time when they see them.
9. Authenticate every endpoint, encrypt every transport
Two non-negotiables for any production API:
- TLS 1.2 or higher on every endpoint, including ones marked “internal”. HTTP traffic leaks credentials and payloads to anyone on the network path.
- Authenticate every endpoint. API keys for server-to-server, OAuth 2.0 / OIDC for user-acting flows, mTLS for high-security partner integrations. The choice depends on audience; the rule is that no endpoint should accept unauthenticated production traffic.
Never put credentials in URL query strings. They leak to server logs, Referer headers, browser history, and CDN logs. Use Authorization headers.
10. Rate limit at the gateway
Every public endpoint needs a rate limit. When the limit is hit, return 429 Too Many Requests with a Retry-After header telling the client how long to wait. Pair this with X-RateLimit-Remaining and X-RateLimit-Reset headers on every response so well-behaved clients can self-throttle.
The algorithm choice (token bucket, sliding window, fixed window) matters less than picking one and applying it consistently. Token bucket is one of the most common choices for public APIs.
11. Cache where the response is cacheable
Cache-Control and ETag headers let HTTP caches (CDNs, reverse proxies, browser caches) reuse responses across calls. For read-heavy endpoints with cacheable responses, this can meaningfully reduce backend load without any client-side change. For mutation endpoints, set Cache-Control: no-store explicitly to prevent accidental caching.
For conditional requests, support If-None-Match (with ETags) and return 304 Not Modified when the resource hasn’t changed since the client’s cached copy. The bandwidth savings show up immediately.
12. Document with OpenAPI and keep documentation in sync
A REST API without documentation is a guessing game. The discipline that compounds is generating documentation from the same source-of-truth artifact your code uses, so docs cannot drift from the implementation.
The OpenAPI specification (formerly Swagger) is the de facto standard for REST API documentation in 2026. Almost every API platform reads it: code generators produce SDKs from it, gateway plugins enforce policies against it, MCP server generators expose it to agents, and developer portals render it as interactive docs. If your API does not have an OpenAPI spec, you are duplicating work every time you ship.
The practical patterns:
- Generate the spec from code, not vice versa. FastAPI, NestJS, and Spring with springdoc-openapi all generate the OpenAPI spec from controller annotations and type hints. This keeps the spec accurate by construction; if the code changes, the spec changes.
- Write descriptions for every endpoint, parameter, and response. The
descriptionandsummaryfields in OpenAPI are not optional. Agents read them literally. Humans read them in the developer portal. Empty descriptions mean both audiences are guessing. - Publish example requests and responses. OpenAPI’s
examplesfield powers the “try it” experience in modern documentation portals and gives developers a starting point. Examples are also the fastest way for a reviewer to spot a wrong response shape. - Version your spec alongside your code. Check the OpenAPI YAML/JSON into git in the same repo as the API. Tag spec versions with the API version. Treat spec changes the way you treat code changes: PR-reviewed, tested, and deployed together.
- Lint the spec. Spectral (Stoplight) is the standard OpenAPI linter; it catches missing descriptions, inconsistent naming, and other issues at PR time. Pair it with a governance pre-commit hook so spec quality cannot regress.
The downstream investment that pays off: an OpenAPI spec that is genuinely accurate makes SDK generation, developer-portal hosting, contract testing, and MCP exposure essentially free. The teams that treat the spec as a chore end up writing the same documentation three times (in code, in markdown docs, and in PDF reference material) and keeping none of them in sync.
For the developer-experience layer that sits on top of the spec, see our developer portal guide.
13. REST API best practices in 2026: agent-readiness
This is the part of the list that did not exist five years ago and that matters for any API expecting AI agent traffic.
Three additions to the standard list:
- Idempotency keys on POST and PATCH. Accept an
Idempotency-Keyrequest header and cache the response keyed on it. Agents retry aggressively; without idempotency, retries create duplicate orders and duplicate charges. The convention was popularized by Stripe’s API and is now widely used across payments and infrastructure APIs. The IETF httpapi working group has been progressing an Internet-Draft (“The Idempotency-Key HTTP Header Field”) aimed at standardizing the convention. - Agent-readable OpenAPI specs. Treat
operationId,summary, anddescriptionas user-facing copy. Agents read these literally when deciding which endpoint to call from your spec. Vague descriptions cause wrong tool selection. - MCP exposure for agent consumers. The Model Context Protocol exposes existing REST APIs to agent runtimes in a richer form. Platforms like the WSO2 AI Gateway auto-generate an MCP server from your OpenAPI spec, so the agent surface stays in sync with the REST surface without a separate build.
Most production teams in 2026 have at least the first of these three. The third is increasingly common among teams shipping AI-facing products.
14. Monitor what you ship (the bonus practice)
Best practices are design-time decisions. Whether they hold up in production is an observability question. Track per-endpoint and per-customer:
- Status code distribution (a spike in 4xx for one customer is an integration problem; a spike in 5xx for one endpoint is a server problem)
- Latency percentiles (p50, p95, p99) by endpoint and region
- Per-customer call volume (capacity planning, churn signals, abuse detection)
- Auth failure rate (security signal)
- Error catalog frequency (which validation errors customers hit the most)
Moesif instruments these out of the box across any gateway (WSO2, Kong, AWS, Azure, Envoy). Pair the design-time best practices in this guide with runtime observability and the loop closes: pick the right pattern at design time, return the right behavior at runtime, observe it actually working in production.
Client-side considerations: designing for the consumer
The best-practice list above is mostly server-side. The other half of the contract is how clients consume the API; the patterns that make integration painless and the ones that turn it into a maintenance project.
Provide SDKs in the languages your consumers actually use. A REST API with no SDKs forces every consumer to write HTTP-call boilerplate, handle auth manually, parse errors by hand, and retry on their own. SDKs in 3-5 dominant languages (TypeScript, Python, Go, Java, Ruby) compress integration time meaningfully. OpenAPI Generator, Stainless, and Speakeasy generate SDKs from your OpenAPI spec; for high-volume APIs the hand-tuned SDK is worth it, but for everything else a generated SDK beats no SDK by a wide margin.
Support pagination cursors that survive across page loads. A cursor-based pagination scheme is the only one that survives concurrent writes. If a client retrieves page 1 (offset 0, limit 50), and the underlying collection gains 5 new records before they fetch page 2 (offset 50, limit 50), 5 records will appear on both pages. Cursor-based pagination ties the next-page token to the position of the last item, so concurrent inserts do not cause duplicates.
Document retry behavior explicitly. Clients need to know which errors are retryable and which are not. The convention: 4xx errors (except 408 and 429) are not retryable because the request is wrong; 429 and 5xx errors are retryable with exponential backoff. State this in the SDK and in the API docs.
Expose webhook delivery guarantees clearly. If your API delivers events via webhooks, document the retry schedule, the signing scheme, and what consumers should do on duplicate deliveries (idempotency keys on the webhook payload, just like the request side).
Provide a sandbox environment. Developers should be able to try the API against a test environment without affecting production data or being billed. Stripe’s test mode, Twilio’s trial accounts, and OpenAI’s free credits are all variations on this. APIs without a sandbox lose developers at the “try it before integrating” step.
Handle CORS correctly for browser-based consumers. If any of your consumers will call the API from a browser, the Access-Control-Allow-Origin configuration is part of the API contract, not an afterthought. Set the allowlist explicitly, handle preflights at the gateway, and cache preflights with Access-Control-Max-Age to reduce round-trips.
Surface request IDs in every response. Include a X-Request-Id (or similar) header on every response. When a customer reports an issue, the request ID is what your support team uses to find the call in logs. Without it, every support ticket starts with “can you reproduce it?”
Common REST API mistakes to avoid
A short catalog of the design patterns that consistently cause pain in production.
Mixing CRUD and RPC styles. A REST API that has POST /createUser next to POST /users is inconsistent. Pick one style and apply it across every endpoint. RPC-style APIs are legitimate (gRPC is RPC) but should not be half-mixed with REST.
Returning 200 OK for errors. A response that returns 200 OK with {"status": "error", "code": "not_found"} forces every consumer to parse the body to know if the call succeeded. Use the HTTP status code as the primary success/failure signal; the body is for detail.
Inconsistent ID formats. Some endpoints return integer IDs ({"id": 42}), others return UUIDs ({"id": "abc-123-..."}), others return prefixed strings ({"id": "user_abc123"}). Pick a convention (the Stripe pattern of typed prefixed IDs scales well) and apply it everywhere. Mixed ID formats are a permanent paper cut for SDK authors.
Breaking changes without a version bump. Adding a new required field to a request, removing a response field, or changing a response type is a breaking change. Bump the version, run both versions in parallel, and follow your deprecation policy. Silent breaking changes are how trust evaporates.
Endpoints that return inconsistent shapes for the same resource. GET /orders/{id} returns one shape; GET /orders returns each item in a slightly different shape. Pick one canonical shape and use it everywhere. Use OpenAPI’s $ref to enforce it.
Stateless that is not really stateless. Pagination cursors that encode database offsets work until you change the database. Tokens that work only on the server that issued them break when you scale horizontally. State that lives anywhere except the request itself is leaky and surfaces as flaky tests and weird production bugs.
Authentication that varies across endpoints. Some endpoints accept API keys in headers; others want them in query strings; others want OAuth. Pick one pattern (API key in Authorization: Bearer ... is the modern default) and apply it uniformly. Mixed auth is a security review nightmare.
Returning huge responses without pagination. GET /transactions that returns 10,000 rows on every call ends in tears. Paginate every list endpoint from day one, and document the default and maximum page size.
Ignoring rate limits in your own client code. If your own SDK retries aggressively without checking Retry-After headers, you accelerate the rate-limit problem the gateway is supposed to solve. Make every client (SDK, internal service-to-service, agent-mediated) respect the same backoff signals.
Treating the OpenAPI spec as an afterthought. A spec written by hand after the implementation, and never updated, is worse than no spec because consumers trust it. Generate the spec from code, validate it on every commit, and treat regressions to the spec as production bugs.
Next steps
The twelve practices above are the parts of REST API design that consistently pay off. They cost very little to apply at design time and a lot to retrofit later.
If you want per-endpoint, per-customer visibility into whether your API is actually following these practices in production, start a 14-day Moesif free trial. No credit card required.
Frequently asked questions
What are the most important REST API best practices? Use nouns not verbs in URLs, pluralize collections, map HTTP methods to CRUD cleanly, return correct status codes, return structured error envelopes, authenticate every endpoint, rate limit at the gateway, version deliberately. Everything else builds on these.
Should I use camelCase or snake_case in JSON? Pick one and use it across the entire API. Both are widely used across modern public APIs (Stripe and OpenAI use snake_case; many JavaScript-default APIs use camelCase). The discipline matters more than the choice.
Is REST still the best choice in 2026? REST is still the default for public APIs because clients in every language understand it. GraphQL is better for client-driven data fetching; gRPC is better for service-to-service inside a mesh. For external developer APIs, REST is still the safe choice.
How do I version a REST API? URI versioning (/v1/orders) is the most common and easiest for consumers to debug. Header versioning is cleaner but harder to use. Publish a deprecation policy on day one.
What HTTP status code should I return on success? 200 OK for GET/PUT/PATCH, 201 Created (with Location header) for POST, 204 No Content for DELETE.
Do these best practices apply to AI agent traffic? Yes, with three additions: idempotency keys on writes, agent-readable OpenAPI fields, and MCP exposure for agent runtimes. The fundamentals stay the same.