REST API Best Practices — Core Concepts

Why best practices matter

A REST API is a contract between your service and every client that consumes it. Breaking that contract — inconsistent naming, unclear errors, missing pagination — creates downstream bugs that multiply across every consumer. Best practices aren’t academic; they directly reduce support tickets, onboarding time, and integration failures.

Resource-oriented URL design

URLs should represent things (nouns), not actions (verbs). The HTTP method already carries the action.

Instead of thisDo this
POST /createUserPOST /users
GET /getUserById?id=5GET /users/5
POST /deleteOrder/12DELETE /orders/12

Nest resources when they have clear parent-child relationships: /users/5/orders returns orders for user 5. But don’t nest more than two levels deep — /users/5/orders/12/items/3/reviews becomes unnavigable.

HTTP methods and status codes

Use methods consistently:

  • GET — read, never modifies data
  • POST — create a new resource
  • PUT — replace a resource entirely
  • PATCH — update specific fields
  • DELETE — remove a resource

Status codes tell the client what happened without parsing the body:

  • 200 — success
  • 201 — resource created
  • 204 — success, no content to return
  • 400 — client sent bad data
  • 401 — not authenticated
  • 403 — authenticated but not authorized
  • 404 — resource doesn’t exist
  • 409 — conflict (e.g., duplicate entry)
  • 422 — validation failed (FastAPI uses this for Pydantic errors)
  • 429 — rate limited
  • 500 — server error

A common misconception: returning 200 with {"error": "not found"} in the body. This forces every client to parse the response body for errors rather than checking the status code. It breaks caching, breaks middleware, and confuses monitoring tools.

Pagination, filtering, and sorting

Any endpoint that returns a list should support pagination from day one. Adding it later is a breaking change.

Common patterns:

  • Offset-based: ?offset=20&limit=10 — simple but slow for deep pages
  • Cursor-based: ?cursor=abc123&limit=10 — faster for large datasets, used by Slack, Stripe, and Twitter

Filtering goes in query parameters: /orders?status=shipped&created_after=2026-01-01. Sorting: /users?sort=created_at&order=desc.

Error responses

Consistent error shapes save hours of debugging. Pick a format and use it everywhere:

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Email address is invalid",
    "details": [
      {"field": "email", "reason": "must contain @"}
    ]
  }
}

Include a machine-readable code (not just the HTTP status) so clients can branch on specific errors without parsing human-readable strings.

Versioning

APIs evolve. When breaking changes are unavoidable, version explicitly. The two main approaches:

  • URL prefix: /v1/users — simple, visible, used by Stripe and GitHub
  • Header-based: Accept: application/vnd.myapi.v2+json — cleaner URLs but harder to test in a browser

Most Python teams choose URL prefixing because it works with standard routing and is easy to document.

Authentication and rate limiting

Use standard mechanisms: OAuth 2.0 bearer tokens or API keys in headers (never in URLs — those get logged). Return 429 Too Many Requests with a Retry-After header when rate limits are hit.

One thing to remember: The best API is one where a new developer can guess the right endpoint and method on their first try — predictability beats cleverness every time.

pythonwebapisrest

See Also

  • Python Aiohttp Client Understand Aiohttp Client through a practical analogy so your Python decisions become faster and clearer.
  • Python Api Client Design Why building your own API client in Python is like creating a TV remote that only has the buttons you actually need.
  • Python Api Documentation Swagger Swagger turns your Python API into an interactive playground where anyone can click buttons to try it out — no coding required.
  • Python Api Mocking Responses Why testing with fake API responses is like rehearsing a play with stand-ins before the real actors show up.
  • Python Api Pagination Clients Why APIs send data in pages, and how Python handles it — like reading a book one chapter at a time instead of swallowing the whole thing.