Toolmingo
Guides8 min read

What Is a REST API? Complete Guide with Examples

Learn what a REST API is, how HTTP methods work, REST constraints, status codes, authentication patterns, and how to design and consume RESTful APIs — with code examples in JS, Python, Go, and PHP.

You've seen the term everywhere — REST API. But what exactly is it, why does every web app use one, and how do you build or consume one correctly?

This guide covers REST from first principles to production patterns.


What Is a REST API?

REST (Representational State Transfer) is an architectural style for building web services. A REST API (or RESTful API) is an interface that lets two systems communicate over HTTP using standard rules.

Key idea: resources (users, products, orders) are identified by URLs, and you interact with them using standard HTTP methods.

GET    /api/users/42      → read user 42
PUT    /api/users/42      → replace user 42
PATCH  /api/users/42      → partially update user 42
DELETE /api/users/42      → delete user 42
POST   /api/users         → create a new user

REST was defined by Roy Fielding in his 2000 PhD dissertation. It's not a protocol or standard — it's a set of constraints that, when followed, produce scalable, predictable APIs.


The 6 REST Constraints

Constraint What It Means
Client–Server Frontend and backend are separate; they communicate only via API
Stateless Each request contains all the information needed; server stores no session state
Cacheable Responses must declare whether they can be cached (via HTTP headers)
Uniform Interface Resources are identified by URLs; interactions happen through representations
Layered System Client doesn't know if it's talking to the real server or a proxy/cache
Code on Demand (optional) Server can send executable code (e.g., JavaScript) to the client

Stateless is the most important constraint. It means every HTTP request must carry all context — authentication token, parameters, body — because the server doesn't remember previous requests.


HTTP Methods (Verbs)

Method Purpose Body? Safe? Idempotent?
GET Read a resource No Yes Yes
POST Create a resource Yes No No
PUT Replace a resource entirely Yes No Yes
PATCH Partially update a resource Yes No Usually
DELETE Delete a resource No No Yes
HEAD Like GET but no body (check if resource exists) No Yes Yes
OPTIONS Discover allowed methods (used in CORS preflight) No Yes Yes

Safe = doesn't change server state. Idempotent = calling it multiple times has the same effect as calling it once.


URL Design (Resource Naming)

Good REST URLs are nouns, not verbs. The HTTP method is the verb.

# Good — nouns, hierarchical
GET    /api/users
GET    /api/users/42
GET    /api/users/42/orders
POST   /api/users
DELETE /api/users/42

# Bad — verbs in URL
GET    /api/getUser?id=42
POST   /api/deleteUser/42
GET    /api/fetchUserOrders/42

Rules of thumb:

  • Use plural nouns: /users, /products, /orders
  • Use lowercase with hyphens: /blog-posts, not /blogPosts
  • Never expose database IDs if they reveal sensitive info — use UUIDs
  • Keep nesting shallow (max 2 levels): /users/42/orders is fine; /users/42/orders/7/items/3/tags is too deep

Request and Response Format

Most REST APIs use JSON. Always set Content-Type and Accept headers.

Request:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...

{
  "name": "Ana Petrović",
  "email": "ana@example.com"
}

Response:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/43

{
  "id": 43,
  "name": "Ana Petrović",
  "email": "ana@example.com",
  "createdAt": "2026-07-13T10:00:00Z"
}

HTTP Status Codes for REST

Code Meaning When to Use
200 OK Success with body GET, PUT, PATCH success
201 Created Resource created POST success; add Location header
204 No Content Success, no body DELETE success
400 Bad Request Invalid input Validation error
401 Unauthorized Not authenticated Missing or invalid token
403 Forbidden Authenticated but no permission Access denied
404 Not Found Resource doesn't exist Wrong ID or URL
409 Conflict State conflict Duplicate email, optimistic lock failure
422 Unprocessable Entity Semantic validation error Valid JSON but invalid business rule
429 Too Many Requests Rate limit exceeded Add Retry-After header
500 Internal Server Error Server bug Never expose stack traces

Consuming a REST API

JavaScript (fetch)

// GET
const res = await fetch('https://api.example.com/users/42', {
  headers: { Authorization: 'Bearer ' + token },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const user = await res.json();

// POST
const res = await fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer ' + token,
  },
  body: JSON.stringify({ name: 'Ana', email: 'ana@example.com' }),
});
const created = await res.json();
console.log(created.id); // 43

Python (requests)

import requests

BASE = "https://api.example.com"
headers = {"Authorization": "Bearer " + token}

# GET
r = requests.get(f"{BASE}/users/42", headers=headers)
r.raise_for_status()  # raises on 4xx/5xx
user = r.json()

# POST
r = requests.post(
    f"{BASE}/users",
    headers=headers,
    json={"name": "Ana", "email": "ana@example.com"},  # sets Content-Type automatically
)
r.raise_for_status()
print(r.json()["id"])  # 43

Go (net/http)

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    // GET
    req, _ := http.NewRequest("GET", "https://api.example.com/users/42", nil)
    req.Header.Set("Authorization", "Bearer "+token)
    resp, err := http.DefaultClient.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))

    // POST
    payload, _ := json.Marshal(map[string]string{
        "name":  "Ana",
        "email": "ana@example.com",
    })
    req2, _ := http.NewRequest("POST", "https://api.example.com/users", bytes.NewReader(payload))
    req2.Header.Set("Content-Type", "application/json")
    req2.Header.Set("Authorization", "Bearer "+token)
    resp2, _ := http.DefaultClient.Do(req2)
    defer resp2.Body.Close()
    fmt.Println(resp2.StatusCode) // 201
}

PHP (cURL)

// GET
$ch = curl_init("https://api.example.com/users/42");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"],
]);
$body = curl_exec($ch);
$user = json_decode($body, true);
curl_close($ch);

// POST
$ch = curl_init("https://api.example.com/users");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode(["name" => "Ana", "email" => "ana@example.com"]),
    CURLOPT_HTTPHEADER => [
        "Content-Type: application/json",
        "Authorization: Bearer $token",
    ],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $result["id"]; // 43

Authentication Patterns

API Key (simplest)

GET /api/data HTTP/1.1
X-API-Key: sk_live_abc123

Good for: server-to-server, public APIs. Bad for: browser apps (key is exposed).

Bearer Token (most common)

GET /api/profile HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...

Token is usually a JWT. Server validates the signature — no database lookup needed.

OAuth 2.0 (delegated access)

Used when your app needs to act on behalf of a user (e.g., "Sign in with Google"). Flow:

  1. Redirect user to authorization server
  2. User grants permission
  3. Your app receives an authorization code
  4. Exchange code for access token
  5. Use access token to call the API

Basic Auth (avoid in production)

Authorization: Basic dXNlcjpwYXNz  # base64("user:pass")

Only acceptable over HTTPS, for internal tools or development.


Pagination

Never return unlimited records. Three common patterns:

Offset pagination (simple, but slow on large datasets):

GET /api/products?page=2&limit=20
{
  "data": [...],
  "total": 500,
  "page": 2,
  "limit": 20,
  "totalPages": 25
}

Cursor pagination (efficient for large datasets, works with real-time data):

GET /api/feed?cursor=eyJpZCI6MTIzfQ&limit=20
{
  "data": [...],
  "nextCursor": "eyJpZCI6MTQzfQ",
  "hasMore": true
}

Keyset pagination (best performance — uses indexed column):

GET /api/events?after_id=143&limit=20

Quick Reference

Task Method URL Response Code
List all users GET /users 200
Get user by ID GET /users/42 200 or 404
Create user POST /users 201 + Location header
Replace user PUT /users/42 200 or 204
Update field PATCH /users/42 200 or 204
Delete user DELETE /users/42 204 or 404
List user's orders GET /users/42/orders 200
Search users GET /users?q=ana&role=admin 200

6 Common REST API Mistakes

  1. Using verbs in URLs (/getUser, /deletePost) — the HTTP method is the verb; the URL is the noun.

  2. Returning 200 for errors — always use the correct status code. 200 with { "error": "not found" } is wrong; use 404.

  3. No versioning — when you break an existing contract, bump the version: /api/v1/, /api/v2/. Add it from day one.

  4. Not validating input — never trust client data. Validate all fields server-side, return 400/422 with clear error messages:

    { "errors": [{ "field": "email", "message": "Invalid email format" }] }
    
  5. Exposing database errors — never return raw SQL errors or stack traces to the client. Log server-side, return generic 500.

  6. No rate limiting — every public API endpoint needs rate limiting. Return 429 Too Many Requests with a Retry-After header.


6 FAQ

Q: What's the difference between REST and SOAP? SOAP is a strict protocol using XML envelopes with a specific message format. REST is an architectural style that's flexible, uses JSON, and relies on HTTP natively. REST is simpler and dominates modern web APIs.

Q: What's the difference between REST and GraphQL? REST has fixed endpoints per resource; GraphQL has one endpoint where you specify exactly what data you need. GraphQL eliminates over-fetching and under-fetching but adds complexity. Use REST for standard CRUD APIs, GraphQL for complex, data-intensive frontends.

Q: Should I use PUT or PATCH? Use PUT to replace a resource entirely (send all fields). Use PATCH to update only specific fields (send only changed fields). In practice, PATCH is more common and safer.

Q: Where should I put the API version — URL or header? URL versioning (/api/v2/) is the most common and easiest to work with (visible in logs, bookmarkable, testable in a browser). Header versioning (Accept: application/vnd.api+json;version=2) is more "pure REST" but harder to use. Pick URL versioning unless you have a specific reason not to.

Q: Is REST the same as HTTP? No. HTTP is the protocol (the transport layer). REST is an architectural style that uses HTTP. You could theoretically implement REST over other protocols, but HTTP is universal in practice.

Q: How do I document a REST API? Use OpenAPI (formerly Swagger). Write a openapi.yaml spec describing all endpoints, parameters, request/response schemas, and authentication. Tools like Swagger UI, Redoc, and Stoplight generate interactive docs from the spec automatically.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools