What is a REST API? Resources, verbs and the rules that matter
By Uxair Ijaz · 2026-11-06 · 11 min read
Every developer meets REST on day one, yet most API mistakes — verbs in URLs, status codes as an afterthought, sessions pasted into requests — come from never being taught what REST actually says. This guide explains it from first principles, with a full worked example, and ends with the parts of the doctrine professionals keep and the parts they quietly drop.
The one-sentence definition
REST (Representational State Transfer) is a style of API where URLs name resources, HTTP methods act on them, and every request carries everything the server needs. That last part — no server-side session — is what "stateless" means, and it is the constraint everything else follows from.
It comes from Roy Fielding's 2000 dissertation, which described how the web itself works: browsers fetch representations (HTML, JSON) of resources (a page, a photo) using a uniform interface (GET, POST). REST applies the same architecture to APIs.
Resources: URLs are nouns
A resource is a thing your API talks about. The URL identifies it; the method says what to do; the body carries the details.
GET /orders → the collection of orders
GET /orders/42 → one order
POST /orders → create an order
PUT /orders/42 → replace order 42
PATCH /orders/42 → partially update order 42
DELETE /orders/42 → delete order 42
GET /orders/42/items → order 42's line items
The conventions professionals keep:
- Plural nouns (
/orders, never/orderor/getOrder).GET /orders/42reads as "order 42 from the orders collection". - No verbs in URLs. The method is the verb.
POST /orders/42/cancelis the accepted exception for actions that are genuinely not CRUD — or model it as a resource:POST /orders/42/cancellation. - Nest for ownership, two levels maximum.
/customers/7/orderssays "orders belonging to customer 7". Beyond that, flatten:/orders/42/items, not/customers/7/orders/42/items/9/days. - IDs are opaque. Users never parse them; if a client needs a date range it uses query parameters, not ID arithmetic.
The methods, and what they promise
Each method carries a contract that clients and caches rely on:
| Method | Meaning | Safe (no side effects) | Idempotent (same result if repeated) |
|---|---|---|---|
| GET | read | yes | yes |
| PUT | replace | no | yes |
| PATCH | partially update | no | not guaranteed |
| POST | create / process | no | no |
| DELETE | remove | no | yes |
Idempotency is the property network code is built on: a client that times out on PUT can simply retry. A retried POST may create two orders — which is why payment APIs issue idempotency keys (see our guide to designing API responses).
PATCH deserves one note: a body of {"name": "X"} means "change the name", but the JSON Patch standard means "apply this operation". Both exist in the wild; document which one your API speaks.
Statelessness: the request carries everything
The server keeps no session between requests. Every call presents its own credentials, and every call is understandable alone:
curl https://api.example.com/orders/42 \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
-H "Accept: application/json"
That token is usually a JWT (what a JWT is, or try one in our JWT decoder). Statelessness is why REST scales horizontally: any server behind the load balancer can answer any request, because none of them is holding a conversation.
The professional exception: nobody stores credentials in URLs, and nobody passes "user_id" as a parameter and trusts it. Identity comes from the token; a user_id parameter is a filter, never who you are.
A complete worked example
Creating an order, start to finish:
Request
POST /orders HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOi...
Content-Type: application/json
Idempotency-Key: 8f3e-unique-per-attempt
{ "items": [ { "sku": "tee-blue-m", "qty": 2 } ], "ship_to": "addr_91" }
Response — success
HTTP/1.1 201 Created
Location: /orders/42
Content-Type: application/json
{ "id": "42", "status": "placed", "total": 54.00, "currency": "USD",
"items": [ { "sku": "tee-blue-m", "qty": 2 } ] }
Response — validation failure
HTTP/1.1 422 Unprocessable Content
Content-Type: application/json
{ "error": "validation_failed",
"message": "2 fields are invalid.",
"fields": { "items.sku": "Unknown SKU tee-blue-L" } }
Read the exchange like a professional: the status code alone tells monitoring whether anything went wrong; Location tells the client where the new resource lives; the 422 body says exactly which field failed and why; the client can retry the whole thing safely because of the idempotency key. Status codes are a language of their own — we cover every code worth using in its own guide.
Cross-cutting conventions
Filtering and paging use query parameters: GET /orders?status=placed&limit=20&cursor=eyJpZCI6NDF9. Page numbers break when rows are inserted; cursors do not — the trade-offs are in the response design guide.
Versioning belongs in the path (/v1/orders) for almost every team: visible in logs, trivially routable, and honest about the fact that API versions are deployments, not headers. GitHub's /v3/, /v4/ split is the pattern people recognise.
Errors are structured, never a 200 with {"error": "..."} inside — that breaks every monitoring tool, cache and retry library ever written.
Documentation is OpenAPI. A machine-readable spec (openapi.yaml) generates docs, SDKs and mocks; professionals treat it as the contract, not an afterthought. If a handler drifts from the spec, the spec wins.
What REST is not
- Not a protocol. REST has no envelope, no mandated headers, no registry — which is its strength and why two "REST" APIs can feel so different.
- Not RPC with JSON.
POST /getOrders,POST /createUserandPOST /deleteOrder?id=42are remote procedure calls wearing REST URLs. They work, but they forfeit caching, idempotency and every tool built on the uniform interface. - Not necessarily HATEOAS. The textbook demands that clients navigate purely by hyperlinks in responses. Almost no public API does this fully; Stripe, GitHub and Stripe-likes ship a
urlfield per object, which is the pragmatic 80%.
What professionals choose in 2026
- REST as the default for public and third-party APIs: cacheable, debuggable with curl, universally understood.
- JSON, not XML; camelCase or snake_case fields, picked once (naming conventions).
- OpenAPI-documented, versioned in the path, bearer-token authenticated.
- gRPC for internal service-to-service traffic where performance and typed contracts beat debuggability.
- Hypermedia only where clients are unknown and long-lived — the rarest case.
If SOAP is on your desk instead, that is a different conversation with different history: see SOAP APIs in depth.
Checklist: your first REST endpoint, done right
- Plural resource noun, kebab-case path (
/invoice-templates), no verbs. - Correct method for the intent; GET never changes anything.
- Correct status code: 201 +
Locationon create, 422 with field details on bad input. - Bearer token for identity; request carries everything (stateless).
- Idempotency key accepted on POST.
- Errors structured, documented in OpenAPI, consistent across the API.
- Pagination and filtering via query parameters from day one.
Get those seven right and your API will feel familiar to everyone who has ever used Stripe, GitHub or Shopify — which is the entire point of REST.