REST APIs Explained — How Web Services Talk to Each Other

REST API explained
REST APIs Explained — How Web Services Talk to Each Other, With Real Examples

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.

📖 19 min read 🔀 HTTP methods explorer 🚦 Status code explorer 🎯 Endpoint design gallery ❓ 6-question 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.”

2000
Year REST was defined — in Roy Fielding’s PhD dissertation
83%+
Of public APIs today are REST or REST-like (source: various API registries)
6
Architectural constraints that define “RESTful”
JSON
The dominant data format for REST responses today

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.

  HTTP Methods Explorer — /api/posts
GET /api/posts/42

Retrieves data. Read-only — calling it 100 times never changes anything on the server. This “no side effects” property is called safe.

Request
GET /api/posts/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGci…
Response — 200 OK
{
  “id”: 42,
  “title”: “Understanding REST”,
  “published”: true
}
POST /api/posts

Creates a new resource. Calling it twice creates two separate posts — it is not idempotent. The server decides the new resource’s ID.

Request
POST /api/posts HTTP/1.1
Content-Type: application/json

{
  “title”: “New Post”,
  “body”: “Hello world”
}
Response — 201 Created
Location: /api/posts/108

{
  “id”: 108,
  “title”: “New Post”,
  “published”: false
}
PUT /api/posts/42

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.

Request
PUT /api/posts/42 HTTP/1.1
Content-Type: application/json

{
  “title”: “Understanding REST (Updated)”,
  “body”: “Full new content…”,
  “published”: true
}
Response — 200 OK
{
  “id”: 42,
  “title”: “Understanding REST (Updated)”,
  “published”: true
}
PATCH /api/posts/42

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.

Request
PATCH /api/posts/42 HTTP/1.1
Content-Type: application/json

{
  “published”: true
}
Response — 200 OK
{
  “id”: 42,
  “title”: “Understanding REST”,
  “published”: true // only this changed
}
DELETE /api/posts/42

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.

Request
DELETE /api/posts/42 HTTP/1.1
Authorization: Bearer eyJhbGci…
Response — 204 No Content
(empty body — resource successfully deleted)
MethodCRUD actionSafe?Idempotent?
GETReadYesYes
POSTCreateNoNo
PUTReplaceNoYes
PATCHPartial updateNoUsually
DELETERemoveNoYes

⚠️ 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.

1

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.”

2

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).

3

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

4

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.

https://api.example.com/v1/users/17/orders?status=shipped&limit=5
Protocol → Host → API version → Resource path → Query parameters

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.

  HTTP Status Code Explorer
200
OK
Standard success — request worked, response body contains the result
201
Created
A new resource was successfully created — typically returned by POST
204
No Content
Success, but nothing to return — common for DELETE requests
301
Moved Permanently
The resource has a new permanent URL — update your bookmarks/code
304
Not Modified
Cached version is still valid — used with conditional caching headers
400
Bad Request
The request is malformed — invalid JSON, missing required field
401
Unauthorized
Missing or invalid authentication credentials
403
Forbidden
You’re authenticated, but not allowed to access this resource
404
Not Found
The resource doesn’t exist at this URL
409
Conflict
The request conflicts with the current state — e.g. duplicate entry
429
Too Many Requests
You’ve hit the rate limit — slow down and retry later
500
Internal Server Error
Something broke on the server — not your fault, report it
502
Bad Gateway
A server acting as a gateway got an invalid response upstream
503
Service Unavailable
Server is overloaded or down for maintenance — try again shortly

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).

❌ Bad:  POST /api/createUser
✅ 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 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.

1

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.

2

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”}.

3

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.

4

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.

5

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.

PATCH /api/v1/users/17 HTTP/1.1
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

StyleData formatBest forDownside
RESTJSON (usually)Public APIs, general web/mobile backends — simple, cacheable, widely understoodOver/under-fetching — fixed response shape per endpoint
SOAPXMLEnterprise systems needing strict contracts, built-in security standards (banking, healthcare)Verbose, heavyweight, steep learning curve
GraphQLJSONComplex UIs needing flexible, precise data shapes in one round tripHarder to cache; more complex server implementation
gRPCProtocol Buffers (binary)High-performance internal microservice-to-microservice communicationNot 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

Python
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

JavaScript
// 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

Bash
# 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.

Statelessness means the server doesn’t remember previous requests from a client. Every request must be self-contained — including authentication (like a Bearer token). This is different from having a database, which is perfectly fine in REST.
PATCH is specifically for partial updates — send only the fields you want to change. PUT is for full replacement and requires sending the entire resource; any field you omit typically gets wiped out or reset.
REST URLs should be nouns (resources), and the action comes from the HTTP method. “getUserOrders” duplicates the verb that GET already expresses, and using POST for a read operation breaks caching and violates the “safe” property that reads should have.
GET is both safe and idempotent (read-only, repeatable). DELETE is idempotent but not safe — it changes data (removes a resource) but calling it again on an already-deleted resource leaves the same end state. POST is neither — it has side effects AND repeated calls create multiple new resources.
204 No Content is the standard success response for DELETE — the resource is gone, so there’s nothing meaningful to return in the body. 201 Created is for POST (resource creation), and 404/500 indicate errors, not success.
This is convention, not a hard HTTP rule — but it’s followed almost universally because it makes APIs self-explanatory: GET /users returns a list (the collection), GET /users/17 returns one item from that collection. Consistency here reduces guesswork for anyone integrating with the API.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *