Menu

Topic 3 of 7

API Design Principles

Learn API Design Principles for free with explanations, exercises, and a quick test (for Backend Engineer).

Published: January 20, 2026 | Updated: January 20, 2026

Why this matters

Well-designed APIs make backend systems easy to use, scale, and maintain. As a Backend Engineer you will:

  • Design endpoints for new features (e.g., user onboarding, orders, payments).
  • Keep existing clients working when evolving your API.
  • Handle errors predictably and securely.
  • Ensure performance with pagination, filtering, caching, and rate limits.

Concept explained simply

An API is a contract between your service and its clients. Good design makes that contract obvious, consistent, and hard to misuse.

Mental model

Think of your API like a public library:

  • Resources: books, authors, patrons. In APIs: users, orders, products.
  • Uniform actions: check out, return. In APIs: standard HTTP methods (GET, POST, PATCH, DELETE).
  • Clear rules: opening hours, fines. In APIs: authentication, rate limits, error codes.
Deep dive: REST vs RPC vs GraphQL

REST: Resource-centric, uses HTTP methods and status codes. Great for broad compatibility and caching.

RPC: Action-centric, often simpler for internal services, but can become ad hoc and harder to cache.

GraphQL: Client specifies fields; reduces over/under-fetching. Requires careful schema and caching strategy.

Pick based on use case, team expertise, and client needs. This lesson focuses on pragmatic REST-style JSON APIs.

Core principles

  • Clarity: Predictable paths (/users, /orders/{id}).
  • Consistency: Naming, casing (prefer snake_case or camelCase; be consistent), plural nouns for collections.
  • Use HTTP semantics: GET (read), POST (create/action), PATCH (partial update), PUT (replace), DELETE (remove).
  • Status codes: 2xx success, 4xx client errors, 5xx server errors. Be specific (201 Created, 400 Bad Request, 404 Not Found, 409 Conflict, 422 Unprocessable Entity).
  • Idempotency: Safe retries for operations (GET, PUT, DELETE are idempotent; POST can be made idempotent with a key).
  • Pagination & filtering: Avoid returning massive lists. Use query params (?page, ?limit, ?cursor) and filters (?status=active).
  • Versioning: Keep clients working. Use URI (/v1/…) or header-based versioning. Avoid breaking changes.
  • Error design: Structured error responses with code, message, and details.
  • Security: Authentication (tokens), authorization (roles/scopes), input validation, rate limits, no sensitive data leakage.
  • Observability: Correlation/request IDs, consistent logs, and standard headers to trace requests.

Worked examples

Example 1: Basic resource design

Goal: CRUD for products.

Design
{
Path:   GET /v1/products                -> list products (with pagination)
        POST /v1/products               -> create product
        GET /v1/products/{product_id}  -> get product
        PATCH /v1/products/{product_id} -> update part of product
        DELETE /v1/products/{product_id} -> delete product

Pagination: GET /v1/products?page=2&limit=25
Filtering:  GET /v1/products?category=books&min_price=10
Status: 201 Created on POST with Location: /v1/products/123
}

Example 2: Idempotent POST (create order)

Problem: Client may retry due to network issues; duplicate orders must be avoided.

Design
{
Client sends: POST /v1/orders
Headers: Idempotency-Key: 7e5c-abc-111
Body: { "items": [{"sku":"A1","qty":2}], "payment_token":"tok_x" }

Server behavior:
- If new key: create order once -> 201 Created, Location: /v1/orders/9001
- If duplicate key: return the same result as first attempt (200/201), no new charge
}

Example 3: Consistent error responses

Design
{
On validation error (422):
Status: 422 Unprocessable Entity
Body: {
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid fields",
    "details": [
      {"field": "email", "issue": "must be a valid email"},
      {"field": "age", "issue": "must be >= 18"}
    ],
    "request_id": "req-5f2a"
  }
}
}

Example 4: Pagination strategy

Offset vs cursor
{
Offset pagination:
GET /v1/users?page=3&limit=50
Pros: simple; Cons: unstable with inserts/deletes at scale.

Cursor pagination:
GET /v1/users?cursor=eyJpZCI6IjEyMyJ9&limit=50
Response includes next_cursor; stable for large datasets.
}

Example 5: Versioning without breaking clients

Design
{
Current: GET /v1/invoices/{id}
We need to add "due_date". Adding fields is backward-compatible.
Breaking change (rename field) requires new version:
- Keep /v1 as-is
- Introduce /v2/invoices/{id} with new field name
- Mark old field in /v1 as deprecated in docs and headers: Deprecation: true
}

Step-by-step design (use for new endpoints)

  1. Define resources and relationships (nouns). Example: users, orders, order_items.
  2. Map operations to HTTP methods (verbs). Default to GET/POST/PATCH/DELETE.
  3. Shape request/response JSON. Decide required/optional fields. Add examples.
  4. Choose pagination and filtering strategy.
  5. Decide authentication/authorization for each route.
  6. Design error model and codes.
  7. Make risky operations idempotent and safe to retry.
  8. Assign status codes and headers (Location, ETag, RateLimit-*).
  9. Plan versioning and deprecation strategy.
  10. Instrument logging and include request IDs in responses.

Production checklist

  • Endpoints named with plural nouns; consistent casing.
  • Every route has method, status codes, example requests/responses.
  • Pagination documented and tested for large data.
  • Idempotency implemented for non-idempotent operations.
  • Authentication and authorization verified per route.
  • Input validation with clear error messages.
  • Rate limiting and sensible timeouts.
  • Observability: request IDs, structured logs, metrics.
  • Backward-compatible changes where possible; version if breaking.
  • Security review: no sensitive data in errors or logs.

Exercises

Try these on your own. Solutions are available below each exercise in the exercises section.

  1. Design a ticketing API: Endpoints for events and tickets (create/list events, purchase ticket with idempotency, refund ticket, list tickets with filters). Include request/response examples, status codes, and headers.
  2. Error model and versioning: Propose a standard error format and plan a non-breaking change where a field is renamed. Show responses and deprecation approach.

Common mistakes and self-check

  • Overloading POST for everything. Self-check: Are you using GET for reads, PATCH for partial updates?
  • Inconsistent naming. Self-check: Do collection and item routes follow the same pattern?
  • Vague errors. Self-check: Do your errors have a code, message, and actionable details?
  • No pagination. Self-check: Can any list return thousands of items? Add pagination.
  • Breaking clients silently. Self-check: Did you remove or rename fields without a version bump?
  • No idempotency for payments/orders. Self-check: Can the client safely retry a request?

Practical projects

  • Build a minimal e-commerce API with products, carts, orders. Include idempotent checkout.
  • Create a blog API with cursor pagination, search filter, and tag-based filtering.
  • Instrument request IDs and structured JSON logs; verify errors include request_id.

Learning path

  • Start here: API design basics and patterns (this page).
  • Then: Data modeling and validation.
  • Next: Authentication/authorization fundamentals (tokens, scopes, roles).
  • Finally: Performance and caching (ETags, conditional requests, cache headers).

Who this is for

  • Aspiring and junior Backend Engineers designing their first services.
  • Full-stack developers who need to create or evolve APIs.
  • Engineers preparing for system design interviews.

Prerequisites

  • Basic HTTP knowledge (methods, headers, status codes).
  • Comfort with JSON and a server-side language.
  • Ability to run and test HTTP requests (CLI or any REST client).

Next steps

  • Complete the exercises below, then take the Quick Test at the end.
  • Note: The Quick Test is available to everyone. Progress is saved only when you are logged in.

Mini challenge

Design one endpoint to archive a project resource. Should it be PATCH or POST? What status code and body will you return? Write your answer, then compare with the guidance below.

Suggested answer

PATCH /v1/projects/{id} with body {"archived": true}. Return 200 OK with updated resource, or 204 No Content if no body. If archiving triggers a long-running job, accept POST /v1/projects/{id}:archive returning 202 Accepted with a job resource.

Practice Exercises

2 exercises to complete

Instructions

You are designing an API for events and tickets.

  • Create endpoints to: (a) create/list events with pagination and filters; (b) purchase a ticket idempotently; (c) refund a ticket; (d) list a user’s tickets with filters.
  • For each endpoint, specify: method, path, required/optional params, status codes, headers, and example request/response bodies.
  • Ensure retries of purchase do not create duplicate tickets or charges.
Expected Output
A small API spec with routes, example requests/responses, headers, and idempotency behavior clearly described.

API Design Principles — Quick Test

Test your knowledge with 10 questions. Pass with 70% or higher.

10 questions70% to pass

Have questions about API Design Principles?

AI Assistant

Ask questions about this tool