Menu

API Engineering Fundamentals

Learn API Engineering Fundamentals for API Engineer for free: roadmap, examples, subskills, and a skill exam.

Published: January 21, 2026 | Updated: January 21, 2026

Why this skill matters for API Engineers

API Engineering Fundamentals are the backbone of building reliable, secure, and easy-to-use services. Mastering HTTP semantics, REST principles, robust contracts, and consistent error handling lets you ship features faster, reduce breaking changes, and keep partners and internal teams productive.

  • Unlocks tasks like designing new endpoints, shaping data contracts, and integrating with downstream services.
  • Reduces on-call load through predictable behavior, observability, and clear error models.
  • Improves team velocity via API-first design and mock-driven development.

What you'll be able to do

  • Model resources and design RESTful endpoints that are consistent and versionable.
  • Define stable data contracts with JSON Schema/OpenAPI and validate requests/responses.
  • Handle pagination, filtering, and sorting at scale with safe defaults.
  • Implement idempotency, retries, and consistency patterns for resilient APIs.
  • Return standardized errors with correlation IDs for easy debugging.

Who this is for

  • Aspiring API Engineers who want a strong foundation before moving to advanced topics.
  • Backend developers transitioning from monoliths to service-based or public APIs.
  • Full-stack engineers who need to design stable interfaces for frontend/mobile clients.

Prerequisites

  • Basic programming in any backend-friendly language (e.g., JavaScript/TypeScript, Python, Go, Java).
  • Comfort with JSON and using curl or an HTTP client.
  • Intro-level understanding of HTTP requests/responses.

Learning path (practical roadmap)

  1. HTTP Core: Methods (GET/POST/PUT/PATCH/DELETE), status codes (2xx/4xx/5xx), headers, content negotiation.
  2. REST & Resource Modeling: Nouns over verbs, URI patterns, statelessness, caching signals.
  3. API-First Design: Write the contract first (OpenAPI/JSON Schema), mock responses, iterate with consumers.
  4. Data Contracts: Schemas, required/optional fields, versioning, backward-compatible changes.
  5. Reliability: Idempotency, retries, timeouts, eventual consistency, 202 async patterns.
  6. Errors & Observability: Problem+JSON style, correlation IDs, structured logging.
  7. Pagination/Filtering/Sorting: Offset vs cursor, filter operators, sort stability and defaults.
Mini tasks to reinforce each milestone
  • Create two endpoints for a resource (e.g., /v1/books): GET list and POST create. Return correct status codes.
  • Draft a simple OpenAPI spec for /v1/books with request/response schemas. Generate a mock server or sample responses.
  • Add pagination with limit and cursor, and document default limits and max caps.
  • Implement idempotency for POST using an Idempotency-Key header and a persistence table.
  • Return errors in a consistent envelope with a correlation ID; log it on the server.

Worked examples

1) Resource design and correct status codes

Scenario: Create a Book API with list and create endpoints.

POST /v1/books
Content-Type: application/json

{
  "title": "Clean Architecture",
  "author": "Robert C. Martin"
}

201 Created
Location: /v1/books/9f2c
Content-Type: application/json

{
  "id": "9f2c",
  "title": "Clean Architecture",
  "author": "Robert C. Martin",
  "createdAt": "2026-01-21T10:00:00Z"
}

Rules applied:

  • 201 Created for successful creation.
  • Location header points to the new resource.
  • Server assigns ID; request schema validates required fields.

2) Idempotent create with retries

Scenario: The client might retry a POST due to a timeout. Use an idempotency key.

POST /v1/payments
Idempotency-Key: 8b4f5e3c-42a1-4caa-8a61-a4d7
Content-Type: application/json

{
  "amount": 1999,
  "currency": "USD",
  "source": "tok_123"
}

201 Created
Idempotency-Replayed: false

-- client retries --

POST /v1/payments
Idempotency-Key: 8b4f5e3c-42a1-4caa-8a61-a4d7
Content-Type: application/json

{ "amount": 1999, "currency": "USD", "source": "tok_123" }

200 OK
Idempotency-Replayed: true

Implementation hint: persist the key, request hash, and result. If the same key comes with the same payload, return the stored response.

3) Standardized error response (Problem+JSON style)

GET /v1/books/does-not-exist

404 Not Found
Content-Type: application/problem+json
X-Correlation-ID: 7c1b2a9f0

{
  "type": "https://errors.example.com/not-found",
  "title": "Resource not found",
  "status": 404,
  "detail": "Book with id 'does-not-exist' was not found.",
  "instance": "/v1/books/does-not-exist",
  "correlationId": "7c1b2a9f0"
}

Keep the response envelope stable and log correlationId server-side.

4) Cursor pagination with stable sort

Scenario: Large lists should avoid offset/limit for performance.

GET /v1/books?limit=3&cursor=eyJpZCI6Ijk5OWYifQ==

200 OK
{
  "items": [ {"id":"9a"}, {"id":"9b"}, {"id":"9c"} ],
  "nextCursor": "eyJpZCI6IjlkIn0="
}

-- SQL-esque approach --
SELECT * FROM books
WHERE id > :last_id
ORDER BY id ASC
LIMIT :limit_plus_one;

Return nextCursor only if more items exist. Choose a unique, monotonically ordered key to ensure deterministic results.

5) Backward-compatible schema change

Scenario: Add an optional field without breaking existing clients.

// v1 response
{
  "id": "9f2c",
  "title": "Clean Architecture",
  "author": "Robert C. Martin"
}

// Add optional 'subtitle' later
{
  "id": "9f2c",
  "title": "Clean Architecture",
  "author": "Robert C. Martin",
  "subtitle": "A Craftsman's Guide"
}

Do: additive changes with optional fields. Avoid: renaming/removing fields in the same version. Use new fields with defaults, or increment the version if breaking.

Drills & exercises

  • [ ] For a resource of your choice, list endpoints and expected status codes for success and common failures.
  • [ ] Write a minimal OpenAPI snippet for one POST and one GET endpoint.
  • [ ] Define a JSON Schema for your POST body and validate a few payloads.
  • [ ] Implement cursor pagination for your list endpoint and test with 3 different limits.
  • [ ] Add idempotency to your POST; simulate retries and verify responses are stable.
  • [ ] Implement a problem+json error response with correlation IDs and verify logs include the ID.

Common mistakes and debugging tips

Using POST for everything

Prefer GET for reads, PUT/PATCH for updates, DELETE for deletions. This enables caching, safe retries, and clearer client semantics.

Ambiguous or incorrect status codes

Example: returning 200 for creation. Prefer 201 with Location for a new resource; 202 for accepted async work; 204 for no-content success.

Breaking contracts silently

Never remove or rename fields in the same version. Add new optional fields and communicate changes. Use contract tests to catch regressions.

Inconsistent error shapes

Always return the same envelope. Include a correlation ID and a machine-readable type. This speeds up debugging and client handling.

Offset pagination on huge datasets

Offset/limit can be slow and inaccurate under heavy writes. Prefer cursor-based pagination with a stable ordering key.

Missing idempotency for non-idempotent writes

Network retries can double-charge or duplicate records. Use idempotency keys and store request fingerprints.

Mini project: Orders API in ~90 minutes

Build a small Orders API that demonstrates fundamentals.

  1. Design resources: orders, order-items. Define URIs: /v1/orders, /v1/orders/{id}.
  2. API-first: Draft OpenAPI for POST /v1/orders and GET /v1/orders. Include schemas.
  3. Create order: Implement POST with 201 and Location. Add Idempotency-Key support.
  4. List orders: Implement cursor pagination (limit default 25, max 100). Add createdAt sort.
  5. Error model: Return problem+json with correlationId for 400/404/409.
  6. Consistency: If fulfillment is async, return 202 and expose GET /v1/orders/{id}/status.
Example snippets (Node/Express)
app.post('/v1/orders', async (req, res) => {
  const key = req.get('Idempotency-Key');
  const fingerprint = JSON.stringify(req.body);
  const cached = await store.findByKey(key);
  if (cached && cached.fingerprint === fingerprint) {
    res.set('Idempotency-Replayed', 'true');
    return res.status(cached.status).json(cached.body);
  }
  const order = await createOrder(req.body);
  const response = { id: order.id, ...order };
  await store.save(key, fingerprint, 201, response);
  res.location(`/v1/orders/${order.id}`).status(201).json(response);
});
Example snippets (Python/FastAPI)
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel

app = FastAPI()
store = {}

class OrderIn(BaseModel):
    items: list
    customerId: str

@app.post('/v1/orders', status_code=201)
async def create_order(order: OrderIn, idempotency_key: str | None = Header(default=None)):
    fp = order.model_dump_json()
    if idempotency_key and idempotency_key in store and store[idempotency_key]['fp'] == fp:
        return store[idempotency_key]['body']
    new = { 'id': 'o_123', **order.model_dump() }
    if idempotency_key:
        store[idempotency_key] = { 'fp': fp, 'body': new }
    return new

Subskills

  • HTTP Methods And Status Codes
  • REST Principles
  • API First Design
  • Data Contracts And Schemas
  • Idempotency And Consistency
  • Error Handling Standards
  • Pagination Filtering Sorting

Next steps

  • Take the skill exam below to validate your understanding. Anyone can take it; only logged-in users have progress saved.
  • Apply fundamentals to a small internal or hobby API. Ship it, instrument it, and iterate.
  • Move on to the next skill once you pass the exam.

API Engineering Fundamentals — Skill Exam

This exam checks your understanding of API fundamentals: HTTP semantics, REST design, contracts, reliability, errors, and pagination. Score 70%+ to pass. Anyone can take the exam; only logged-in users have progress saved for later.

14 questions70% to pass

Have questions about API Engineering Fundamentals?

AI Assistant

Ask questions about this tool