Learn· With · Examples
Every time an app checks the weather, posts a tweet, or processes a payment, it’s almost certainly talking to a REST API behind the scenes. This guide breaks REST down from first principles — what makes an API “RESTful,” how HTTP methods map to real actions, and how to read a request and response like a developer — with real, working examples throughout.
Contents
- What is a REST API? — the waiter analogy
- The 6 REST constraints — formal definition
- Interactive: HTTP methods explorer
- Anatomy of a REST request
- Interactive: HTTP status code explorer
- Resource naming — nouns, not verbs
- Interactive: endpoint design gallery
- Statelessness explained
- Worked example — a full request/response cycle
- REST vs SOAP vs GraphQL vs gRPC
- Real-world REST APIs
- Code examples — Python, JavaScript, cURL
- Knowledge quiz
Section 01
What is a REST API? — The Waiter Analogy
Imagine a restaurant. You (the customer) never walk into the kitchen to cook your own food. Instead, you tell a waiter what you want from a menu, the waiter takes that request to the kitchen, and brings back your food. You don’t need to know how the kitchen works internally — you just need to know the menu and how to order.
A REST API (Representational State Transfer, Application Programming Interface) is exactly this waiter. Your app (the client) doesn’t access a company’s database directly. Instead, it sends a structured request to the API, the API talks to the “kitchen” (the server’s internal systems), and sends back a structured response — usually as JSON. REST is simply a set of conventions for how that request and response should be shaped, so any client and any server can understand each other without a custom, one-off arrangement.
💡 The Key Idea
REST is not a technology, a library, or a protocol you install — it’s an architectural style: a set of design rules for building APIs on top of HTTP. If an API follows these rules, it’s called “RESTful.”
Section 02
The 6 REST Constraints — Formal Definition
Roy Fielding defined REST as a set of architectural constraints. An API that satisfies all of them is truly “RESTful” — though in practice, most real-world APIs relax one or two (usually “code on demand,” which is optional anyway).
1. Client-Server
The client (UI) and server (data storage) are separate. Either can change independently as long as the interface between them stays the same.
2. Stateless
Every request must contain all information needed to process it. The server stores no client session state between requests.
3. Cacheable
Responses must explicitly state whether they can be cached, so clients can reuse data and reduce unnecessary requests.
4. Uniform Interface
A consistent way to identify resources (URLs) and interact with them (HTTP methods) — the core idea that makes REST predictable.
5. Layered System
The client can’t tell if it’s talking directly to the server or through intermediaries like load balancers or caches — and doesn’t need to.
6. Code on Demand (optional)
Servers can optionally send executable code (like JavaScript) to extend client functionality. The only constraint that’s optional.
Section 03
Interactive: HTTP Methods Explorer
REST maps standard HTTP methods onto CRUD operations (Create, Read, Update, Delete). Click each method below to see a real request and its typical response for a simple blog post API.
Retrieves data. Read-only — calling it 100 times never changes anything on the server. This “no side effects” property is called safe.
Host: api.example.com
Authorization: Bearer eyJhbGci…
“id”: 42,
“title”: “Understanding REST”,
“published”: true
}
Creates a new resource. Calling it twice creates two separate posts — it is not idempotent. The server decides the new resource’s ID.
Content-Type: application/json
{
“title”: “New Post”,
“body”: “Hello world”
}
{
“id”: 108,
“title”: “New Post”,
“published”: false
}
Replaces a resource entirely. You must send the FULL object — any field you omit gets wiped out. Calling it twice with the same body gives the same result — it is idempotent.
Content-Type: application/json
{
“title”: “Understanding REST (Updated)”,
“body”: “Full new content…”,
“published”: true
}
“id”: 42,
“title”: “Understanding REST (Updated)”,
“published”: true
}
Partially updates a resource — only send the fields you want to change. Everything else stays untouched. Usually idempotent in practice, though not strictly guaranteed by spec.
Content-Type: application/json
{
“published”: true
}
“id”: 42,
“title”: “Understanding REST”,
“published”: true // only this changed
}
Removes a resource. Calling it twice has the same end state (the resource is gone both times) — it is idempotent, even though the second call typically returns 404 instead of 200.
Authorization: Bearer eyJhbGci…
| Method | CRUD action | Safe? | Idempotent? |
|---|---|---|---|
| GET | Read | Yes | Yes |
| POST | Create | No | No |
| PUT | Replace | No | Yes |
| PATCH | Partial update | No | Usually |
| DELETE | Remove | No | Yes |
⚠️ Safe vs Idempotent — Not the Same Thing
Safe means the request causes no side effects at all (read-only). Idempotent means calling it multiple times produces the same end state as calling it once — but it can still have side effects the first time. DELETE isn’t safe (it changes data) but it IS idempotent (deleting an already-deleted item leaves the same end state).
Section 04
Anatomy of a REST Request
Every REST request is built from four parts. Understanding each one is the key to reading any API’s documentation.
Method + URL — what and where
The HTTP method (GET, POST, etc.) says what action to take. The URL identifies which resource — e.g. /api/users/17/orders means “orders belonging to user 17.”
Headers — metadata about the request
Key-value pairs describing the request: Authorization (who’s asking), Content-Type (what format the body is in), Accept (what format you want back).
Query parameters — filtering and options
Appended to the URL after a ?, used for filtering, sorting, and pagination: /api/posts?published=true&limit=10&sort=-date
Body — the actual data (POST/PUT/PATCH only)
The payload being sent, almost always JSON today. GET and DELETE requests typically have no body — everything they need is in the URL and headers.
Section 05
Interactive: HTTP Status Code Explorer
Every response includes a 3-digit status code. The first digit tells you the category at a glance. Click each category to see the codes you’ll actually encounter.
Section 06
Resource Naming — Nouns, Not Verbs
The single most common REST mistake is putting an action inside the URL. REST already has actions — they’re the HTTP methods. URLs should describe what you’re acting on (a noun), not what to do to it (a verb).
✅ Good: POST /api/users ← the verb “create” comes from POST itself
❌ Bad: GET /api/getUserById?id=17
✅ Good: GET /api/users/17 ← the ID is part of the resource path
❌ Bad: POST /api/deletePost/42
✅ Good: DELETE /api/posts/42 ← DELETE method + noun path
✅ Naming Conventions That Scale
Use plural nouns for collections (/users not /user), nest relationships logically (/users/17/orders), use hyphens not underscores in multi-word paths (/blog-posts not /blog_posts), and keep casing consistent — lowercase is the near-universal standard.
Section 07
Interactive: Endpoint Design Gallery
Click through four common REST design decisions to compare a poor approach against the accepted best practice.
Body: {“active”: true}
(returns all 50,000 posts at once)
or GET /api/posts?cursor=eyJpZCI6MTA1
Section 08
Statelessness Explained
Statelessness is the constraint developers misunderstand most often. It does not mean “the app has no persistent data” — a database is state, and REST APIs use them constantly. It means the server keeps no memory of the client between requests.
❌ Stateful (not RESTful)
Request 1: “Log in as Alice” → server remembers “this connection is Alice” in memory.
Request 2: “Get my orders” → server assumes it’s still talking to Alice based on that memory.
If request 2 hits a different server in a load-balanced cluster, it has no idea who you are.
✅ Stateless (RESTful)
Request 1: “Log in as Alice” → server returns a token.
Request 2: “Get my orders” + Authorization: Bearer <token> in the header.
Every request proves who it is on its own. Any server in the cluster can handle it — no shared memory required.
💡 Why It Matters
Stateless APIs scale horizontally with almost no effort — spin up 100 identical servers behind a load balancer, and any of them can handle any request, because no request depends on “remembering” a previous one. This is a major reason REST became the dominant style for cloud-scale web services.
Section 09
Worked Example — A Full Request/Response Cycle
Let’s trace a complete real-world flow: a mobile app updating a user’s profile picture URL.
App builds the request
The app knows the user’s ID (17) and has a stored auth token from login. It decides this is a partial update, so PATCH is the right method.
Request sent over HTTPS
PATCH /api/v1/users/17 with headers Authorization and Content-Type: application/json, body {“avatar_url”: “https://cdn.example.com/img/17.jpg”}.
Server validates the token
The server checks the Authorization header against its token store — this is the “prove who you are on every request” statelessness in action.
Server updates the database
Only the avatar_url field is changed for user 17 — every other field (name, email, bio) stays exactly as it was, because this is PATCH, not PUT.
Server responds
200 OK with the full updated user object in the body, so the app can immediately re-render the UI without a separate GET request.
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9…
Content-Type: application/json
{ “avatar_url”: “https://cdn.example.com/img/17.jpg” }
— response —
HTTP/1.1 200 OK
Content-Type: application/json
{ “id”: 17, “name”: “Alice”, “avatar_url”: “https://cdn.example.com/img/17.jpg”, “updated_at”: “2026-07-27T10:15:00Z” }
Section 10
REST vs SOAP vs GraphQL vs gRPC
| Style | Data format | Best for | Downside |
|---|---|---|---|
| REST | JSON (usually) | Public APIs, general web/mobile backends — simple, cacheable, widely understood | Over/under-fetching — fixed response shape per endpoint |
| SOAP | XML | Enterprise systems needing strict contracts, built-in security standards (banking, healthcare) | Verbose, heavyweight, steep learning curve |
| GraphQL | JSON | Complex UIs needing flexible, precise data shapes in one round trip | Harder to cache; more complex server implementation |
| gRPC | Protocol Buffers (binary) | High-performance internal microservice-to-microservice communication | Not human-readable; harder to debug and test manually |
🎯 Bottom Line
REST remains the default choice for public-facing APIs because of its simplicity and universal HTTP tooling support. GraphQL shines when clients need flexible queries. gRPC dominates internal service-to-service calls where raw speed matters most.
Section 11
Real-World REST APIs
Stripe API
Widely considered a gold-standard REST API — consistent noun-based URLs, excellent status code usage, thorough documentation with live examples.
GitHub API
Fully RESTful with clean resource nesting (/repos/{owner}/{repo}/issues) — a commonly cited teaching example for good API design.
OpenWeatherMap
A simple, classic public REST API — a single GET request with query parameters returns current weather data as JSON.
Instagram Graph API
Uses REST conventions for reading and publishing content, layered with OAuth for authentication and permission scopes.
Spotify Web API
REST endpoints for tracks, playlists, and playback control — used by nearly every third-party Spotify app and integration.
Google Maps API
Geocoding, directions, and places endpoints — all accessed via simple GET requests with query parameters.
Section 12
Code Examples — Python, JavaScript, cURL
Python — using the requests library
import requests # GET — fetch a resource response = requests.get( "https://api.example.com/v1/posts/42", headers={"Authorization": "Bearer YOUR_TOKEN"} ) print(response.status_code) # 200 print(response.json()) # parsed dict # POST — create a resource response = requests.post( "https://api.example.com/v1/posts", headers={"Authorization": "Bearer YOUR_TOKEN"}, json={"title": "New Post", "body": "Hello!"} ) print(response.status_code) # 201 # PATCH — partial update response = requests.patch( "https://api.example.com/v1/posts/42", headers={"Authorization": "Bearer YOUR_TOKEN"}, json={"published": True} )
JavaScript — using fetch
// GET — fetch a resource const res = await fetch("https://api.example.com/v1/posts/42", { headers: { "Authorization": "Bearer YOUR_TOKEN" } }); const data = await res.json(); console.log(res.status, data); // POST — create a resource const created = await fetch("https://api.example.com/v1/posts", { method: "POST", headers: { "Authorization": "Bearer YOUR_TOKEN", "Content-Type": "application/json" }, body: JSON.stringify({ title: "New Post", body: "Hello!" }) });
cURL — quick command-line testing
# GET request curl -X GET https://api.example.com/v1/posts/42 \ -H "Authorization: Bearer YOUR_TOKEN" # POST request with JSON body curl -X POST https://api.example.com/v1/posts \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"title": "New Post", "body": "Hello!"}' # DELETE request curl -X DELETE https://api.example.com/v1/posts/42 \ -H "Authorization: Bearer YOUR_TOKEN"
Section 13
Knowledge Quiz
Click a question to expand it, then pick your answer.

