Skip to main content

REST API Design Checklist

A practical reference covering every decision point when designing a REST API — from URL structure and HTTP semantics to versioning, pagination, and error shapes. Use this as a review checklist before shipping.

✓ Resource naming conventions ✓ HTTP methods & status codes ✓ Versioning & pagination ✓ Filtering, sorting & HATEOAS

Resource URL Structure

Follow these rules for every endpoint you design:

# Collections — plural nouns
GET    /users              # list all users
POST   /users              # create a new user

# Specific resource — ID in path segment
GET    /users/42           # get user 42
PUT    /users/42           # full replace
PATCH  /users/42           # partial update
DELETE /users/42           # delete

# Nested resource (max 2 levels)
GET    /users/42/orders    # orders belonging to user 42
GET    /users/42/orders/7  # specific order for user 42

# WRONG — verbs in URL
POST   /createUser         # bad
GET    /users/getAll       # bad
POST   /users/42/delete    # bad

HTTP Method Semantics

Method Idempotent Safe Use for Success code
GETYesYesRead resource or collection200
POSTNoNoCreate resource201 + Location
PUTYesNoFull replace (all fields)200 or 204
PATCHNo*NoPartial update (only sent fields)200 or 204
DELETEYesNoRemove resource204
HEADYesYesCheck existence / headers only200 or 404
OPTIONSYesYesCORS preflight, allowed methods200

*PATCH is only idempotent if using JSON Patch format (RFC 6902). Merge-patch operations are not guaranteed idempotent.

Status Code Quick Reference

200 OK              — Successful GET, PUT, PATCH with response body
201 Created         — Successful POST; include Location: /users/42
204 No Content      — Successful DELETE or PUT/PATCH with no body
304 Not Modified    — GET with conditional If-None-Match / If-Modified-Since
400 Bad Request     — Malformed JSON, wrong types, missing required fields
401 Unauthorized    — No auth token, expired token, invalid signature
403 Forbidden       — Valid token but insufficient permissions
404 Not Found       — Resource does not exist (or hidden for security)
405 Method Not Allowed — PUT on a collection, etc.
409 Conflict        — Duplicate unique field, optimistic lock version mismatch
410 Gone            — Resource permanently deleted (use over 404 for caching)
422 Unprocessable   — Syntactically valid but semantically wrong (validation)
429 Too Many Requests — Rate limited; include Retry-After header
500 Internal Error  — Server-side failure; return trace_id, log full error
503 Service Unavailable — Dependency down; include Retry-After header

Filtering, Sorting & Pagination

# Filtering — query params per field
GET /orders?status=pending&created_after=2024-01-01

# Range filters with operators
GET /products?price[gte]=10&price[lte]=100
GET /products?price_min=10&price_max=100  # simpler alternative

# Sorting — comma-separated, prefix minus for desc
GET /users?sort=-created_at,name    # newest first, then by name asc

# Cursor pagination (recommended for large data)
GET /posts?limit=20
# Response includes:
# { "data": [...], "meta": { "next_cursor": "eyJpZCI6MjB9", "has_more": true } }
GET /posts?cursor=eyJpZCI6MjB9&limit=20

# Field selection (sparse fieldsets)
GET /users?fields=id,name,email     # only return requested fields

Consistent Error Response Shape

// 422 Validation error
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Request validation failed",
    "details": [
      { "field": "email", "issue": "must be a valid email address" },
      { "field": "age",   "issue": "must be a positive integer" }
    ],
    "trace_id": "req_01hxyz..."
  }
}

// 404 Not found
{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "No user found with id 42",
    "trace_id": "req_01habc..."
  }
}

// 429 Rate limited
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests",
    "retry_after": 30
  }
}
// Also set headers: X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After

Versioning Strategy

# Option 1: URL versioning (most common, cache-friendly)
GET /v1/users
GET /v2/users

# Option 2: Request header versioning
GET /users
X-API-Version: 2

# Option 3: Content-type negotiation (RFC 6838)
Accept: application/vnd.myapi.v2+json

# Deprecation lifecycle headers
Deprecation: Tue, 01 Jan 2025 00:00:00 GMT
Sunset: Mon, 01 Jul 2025 00:00:00 GMT
Link: <https://devdocs.example.com/v2/migration>; rel="successor-version"

HATEOAS Basics

// Minimal HATEOAS — self link + available actions
{
  "id": 42,
  "status": "pending",
  "total": 199.99,
  "_links": {
    "self":    { "href": "/orders/42",        "method": "GET" },
    "cancel":  { "href": "/orders/42/cancel", "method": "POST" },
    "invoice": { "href": "/orders/42/invoice","method": "GET" }
  }
}

// Paginated collection with navigation links
{
  "data": [...],
  "_links": {
    "self":  { "href": "/posts?page=3" },
    "next":  { "href": "/posts?page=4" },
    "prev":  { "href": "/posts?page=2" },
    "first": { "href": "/posts?page=1" },
    "last":  { "href": "/posts?page=12" }
  },
  "meta": { "total": 234, "per_page": 20 }
}

Pre-Ship Checklist

Area Checklist Item
URLsPlural nouns, no verbs, kebab-case, max 2 nesting levels
MethodsGET safe, PUT/DELETE idempotent, POST only for creation
Status codes201 + Location on create, 204 on delete, 422 for validation
ErrorsConsistent envelope, machine-readable code, trace_id
PaginationCursor-based, max limit enforced, meta with has_more
VersioningStrategy chosen and documented, sunset headers on old versions
SecurityAuth on all non-public routes, 401 vs 403 used correctly
DocsOpenAPI spec published, examples for every endpoint

Frequently Asked Questions

What is the correct way to name REST API resources?▼

Always use plural nouns for collections: /users, /orders, /products. Never use verbs in the URL — the HTTP method conveys the action. Nest resources only when the child truly cannot exist without the parent, and limit nesting to two levels deep: /users/42/orders is fine, /users/42/orders/7/items/3/reviews gets unwieldy. Use kebab-case for multi-word segments: /blog-posts, not /blogPosts or /blog_posts. IDs go in the path for specific resources: GET /users/42, not GET /users?id=42.

Which HTTP status codes should every API return?▼

At minimum: 200 OK (successful GET/PUT/PATCH), 201 Created (successful POST that creates a resource, include Location header pointing to new resource), 204 No Content (successful DELETE or PUT with no body), 400 Bad Request (invalid input — include error details in body), 401 Unauthorized (missing or invalid authentication), 403 Forbidden (authenticated but lacks permission), 404 Not Found (resource does not exist), 409 Conflict (optimistic locking collision, duplicate key), 422 Unprocessable Entity (validation errors with field-level details), 429 Too Many Requests (rate limited), 500 Internal Server Error (never expose stack traces).

What are the best API versioning strategies?▼

URL versioning (/v1/users) is the most widely used and cache-friendly approach — clients can see the version at a glance and proxy caches work correctly. Header versioning (Accept: application/vnd.api+json;version=2) keeps URLs clean but is harder to test in a browser. Avoid query param versioning (?v=1) because it mixes routing concerns with filtering. Whatever strategy you choose, publish a sunset policy: deprecate at least 6 months before removing a version and return a Sunset response header.

How should REST APIs handle pagination?▼

Cursor-based pagination is recommended for large or frequently-changing datasets. Return a next_cursor (opaque base64 token encoding the last seen ID + timestamp) and use it as a query param: GET /posts?cursor=eyJpZCI6MTAwfQ&limit=20. Avoid offset pagination for large tables — OFFSET 10000 causes a full index scan. For simpler cases, return total_count, page, per_page, and links (self, next, prev) in a meta object. Always cap maximum page size (e.g., limit=100) and default to a safe value (limit=20).

What is HATEOAS and do I actually need it?▼

HATEOAS (Hypermedia as the Engine of Application State) means embedding links in API responses that describe available actions, so clients discover the API dynamically without hard-coding URLs. A fully HATEOAS response includes _links: &#123; self: &#123; href: '/orders/42' &#125;, cancel: &#123; href: '/orders/42/cancel', method: 'POST' &#125; &#125;. In practice, most public APIs skip full HATEOAS. A pragmatic middle ground: include a self link on every resource and a next/prev on paginated collections. Full HATEOAS is most valuable for machine-to-machine APIs where clients must be decoupled from server URL structure.

How do I design consistent error responses?▼

Adopt a standard error envelope and apply it everywhere. A good structure: &#123; error: &#123; code: 'VALIDATION_FAILED', message: 'Human-readable summary', details: [ &#123; field: 'email', issue: 'must be a valid email address' &#125; ] &#125; &#125;. The code field is a machine-readable string constant your clients can switch on. Never return different error shapes for different endpoints. For 500 errors, log the full stack server-side but return only a generic message and a trace_id clients can quote in support tickets.

Share this tool: X / Twitter LinkedIn
✓ 60,000+ developers use DevPlaybook ★★★★★ 4.8 / 5 on Gumroad ✓ MIT licensed · Instant download ✓ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

OpenAPI Validator HTTP Status Code Reference API Request Builder API Response Mock Generator HTTP Status Code Reference HTTP Status Code Lookup HTTP Status Code Lookup API Response Time Tester HTTP API Tester HTTP Methods Reference OpenAPI Schema Validator OpenAPI Mock Generator API Test Case Generator API Response Formatter & Analyzer HTTP Status Code Reference API Mock Server Config Generator REST API Request Builder API Response Mocker HTTP Headers Analyzer API Rate Limit Calculator HTTP Headers Inspector HTTP Code Snippet Generator Interview Question Generator System Design Cheatsheet HTTP Mock Response Builder LLM Cost Calculator Haproxy Config Validator HTTP Cache-Control Builder HTTP Header Analyzer AI Prompt Cost Calculator HTTP/2 Server Push Headers Builder Mock Service Worker (MSW) API Mocking Guide HTTP Security Headers Checker LLM Prompt Cost Calculator AI Rate Limit Calculator HTTP Cache Header Builder API Key Generator AI Doc Generator — Pro Tool curl Command Builder HTTP Security Headers Generator JSON Diff Viewer JSON Diff Developer Utilities Rate Limiter Tester VLSM Calculator API Changelog Generator Rate Limit Calculator Webhook Tester CORS Policy Builder Caddy Caddyfile Generator HAR Diff Analyzer Vue Component Generator LLM Context Window Calculator LLM Token Counter SSL Certificate Checker Prompt Token Estimator Responsive Breakpoint Tester Pulumi Modern IaC Guide AI Pipeline DAG Builder HTML5 Canvas API Playground Token Context Calculator Multi-Agent Orchestration Planner OpenAI Function Calling Generator Token Counter & Cost Estimator AI API Rate Limit Calculator

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • ⚡ AI Code Review, Doc Generator & SQL Builder
  • ⚡ All premium templates & early access
  • ⚡ Member discounts on Gumroad products
Start Free Trial →

$9/mo after trial · Cancel anytime

Want the full toolkit?

Get DevPlaybook Pro

Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro — $79 →

MIT licensed · Instant download · No subscription

See what's included → Browse all deals →