HTTP for REST APIs

Fundamentals Beginner 20 min Jan 12, 2026

Audience

This guide is for developers who work with REST APIs and want to understand HTTP properly:

  • Backend developers building APIs who want to use HTTP correctly
  • Frontend developers consuming APIs who need to understand response semantics
  • API designers making decisions about methods, status codes, and headers
  • Anyone who has been confused by why an API uses PUT vs PATCH, or 400 vs 422

You should already understand what an API is. If not, start with How a REST API Works.

Goal

After reading this guide, you’ll understand:

  • Why HTTP is the foundation of REST APIs (not just a transport)
  • How to choose the right HTTP method for each operation
  • What status codes mean and when to use them
  • Which headers matter and why
  • What breaks clients when HTTP is misused

This guide builds criteria—the ability to make correct HTTP decisions confidently.

1. HTTP Is Not Just Transport

Many developers treat HTTP as a “pipe” to send JSON back and forth. This is a mistake.

HTTP Has Semantics

HTTP is not just a way to move bytes. It’s a semantic protocol with built-in meaning:

  • Methods tell the server what action to perform
  • Status codes tell the client what happened
  • Headers carry metadata about the request and response
  • Caching, retries, and proxies all depend on these semantics

When you ignore HTTP semantics, you lose:

graph LR
    A[Ignoring HTTP Semantics] --> B[No automatic caching]
    A --> C[Unsafe retries]
    A --> D[Broken proxy behavior]
    A --> E[Confusing error handling]
    A --> F[Poor tooling support]
    style A fill:#ffccbc

The Cost of Getting It Wrong

If you use POST for everything:

  • Browsers and CDNs won’t cache responses
  • Retry logic becomes dangerous (might duplicate actions)
  • API documentation tools can’t infer behavior
  • Developers have to read docs for every endpoint

If you return 200 OK for errors:

  • Monitoring systems can’t detect failures
  • Load balancers can’t route around unhealthy servers
  • Clients have to parse the body to know if it worked

HTTP semantics exist for a reason. Use them.

2. HTTP Methods: What They Really Mean

HTTP defines several methods. Each has a specific meaning and expected behavior.

The Core Methods

MethodPurposeHas Body?Idempotent?Safe?
GETRetrieve a resourceNoYesYes
POSTCreate a resource or trigger an actionYesNoNo
PUTReplace a resource entirelyYesYesNo
PATCHUpdate part of a resourceYesNo*No
DELETERemove a resourceNoYesNo
HEADGet headers only (no body)NoYesYes
OPTIONSGet allowed methodsNoYesYes

*PATCH can be idempotent if designed carefully.

GET: Retrieve Without Side Effects

GET requests should never modify data. They’re “safe”—you can call them repeatedly without changing anything.

GET /users/123 HTTP/1.1
Host: api.example.com

What GET guarantees:

  • Safe to cache
  • Safe to retry
  • Safe for browsers to prefetch
  • Safe for crawlers to follow

What breaks when you violate this:

# WRONG: GET with side effects
GET /users/123/delete HTTP/1.1

If a crawler follows this link, it deletes the user. If a browser prefetches it, data disappears. Never use GET for destructive operations.

POST: Create or Trigger Actions

POST is the “do something” method. It’s not idempotent—calling it twice might create two resources.

POST /users HTTP/1.1
Content-Type: application/json

{
  "name": "Alice",
  "email": "[email protected]"
}

Use POST when:

  • Creating a new resource
  • Triggering an action that isn’t idempotent
  • Submitting data that shouldn’t be in a URL

Why POST is dangerous to retry:

If a payment fails with a network timeout, did it actually process? Retrying POST /payments might charge the customer twice. This is why idempotency matters (covered in advanced courses).

PUT: Replace Entirely

PUT replaces the entire resource. Send the complete representation, not just changes.

PUT /users/123 HTTP/1.1
Content-Type: application/json

{
  "name": "Alice Smith",
  "email": "[email protected]",
  "phone": "+1-555-1234"
}

PUT is idempotent: Calling it 10 times with the same data has the same effect as calling it once. The resource ends up in the same state.

Common mistake:

# WRONG: Sending only changed fields
PUT /users/123 HTTP/1.1

{
  "name": "Alice Smith"
}

This would set email and phone to null (or whatever your API does with missing fields). For partial updates, use PATCH.

PATCH: Update Partially

PATCH updates specific fields without replacing the entire resource.

PATCH /users/123 HTTP/1.1
Content-Type: application/json

{
  "name": "Alice Smith"
}

Only name changes. Other fields remain unchanged.

PUT vs PATCH decision:

flowchart TD
    A[Need to update resource] --> B{Sending complete
representation?} B -->|Yes| C[Use PUT] B -->|No| D{Sending partial
changes?} D -->|Yes| E[Use PATCH] D -->|No| F[Reconsider your design] style C fill:#e8f5e9 style E fill:#e3f2fd

DELETE: Remove a Resource

DELETE removes the resource at the given URL.

DELETE /users/123 HTTP/1.1

DELETE is idempotent: Deleting a resource that doesn’t exist should return success (usually 204 or 404, depending on your design). The end state is the same: the resource is gone.

Common question: Should DELETE have a body?

Technically allowed, but many proxies and clients don’t support it. Avoid DELETE with a body.

Methods You’ll Rarely Use

  • HEAD: Like GET but returns only headers. Used for checking if a resource exists or getting metadata without downloading the body.
  • OPTIONS: Returns allowed methods for a resource. Used by CORS preflight requests.

3. Status Codes: Telling Clients What Happened

Status codes are not just numbers—they’re a communication protocol between server and client.

The Five Families

RangeCategoryMeaning
1xxInformationalRequest received, continuing process
2xxSuccessRequest succeeded
3xxRedirectionFurther action needed
4xxClient ErrorClient made a mistake
5xxServer ErrorServer failed to fulfill valid request

Success Codes (2xx)

200 OK — The request succeeded.

Use for: GET that returns data, PUT/PATCH that returns updated resource, POST that returns created resource.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 123,
  "name": "Alice"
}

201 Created — A new resource was created.

Use for: Successful POST that creates a resource. Include Location header with the new resource URL.

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

{
  "id": 123,
  "name": "Alice"
}

204 No Content — Success, but nothing to return.

Use for: DELETE that succeeded, PUT/PATCH when you don’t need to return the updated resource.

HTTP/1.1 204 No Content

202 Accepted — Request accepted for processing, but not yet completed.

Use for: Async operations where the result isn’t immediately available.

HTTP/1.1 202 Accepted
Location: /jobs/456

{
  "jobId": 456,
  "status": "processing"
}

Client Error Codes (4xx)

These say: “The client did something wrong.”

400 Bad Request — The request is malformed or invalid.

Use for: Invalid JSON, missing required fields, validation errors.

HTTP/1.1 400 Bad Request

{
  "error": "Validation failed",
  "details": [
    {"field": "email", "message": "Invalid email format"}
  ]
}

401 Unauthorized — Authentication required or failed.

Use for: Missing token, invalid credentials, expired session.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"

{
  "error": "Authentication required"
}

403 Forbidden — Authenticated, but not authorized.

Use for: User is logged in but doesn’t have permission.

HTTP/1.1 403 Forbidden

{
  "error": "You don't have permission to access this resource"
}

401 vs 403:

flowchart TD
    A[Request with
protected resource] --> B{Token present?} B -->|No| C[401 Unauthorized] B -->|Yes| D{Token valid?} D -->|No| C D -->|Yes| E{Has permission?} E -->|No| F[403 Forbidden] E -->|Yes| G[200 OK] style C fill:#ffccbc style F fill:#ffccbc style G fill:#c8e6c9

404 Not Found — The resource doesn’t exist.

Use for: Requested resource not found at this URL.

HTTP/1.1 404 Not Found

{
  "error": "User not found"
}

405 Method Not Allowed — The method is not supported for this resource.

Use for: POST to a read-only endpoint, DELETE when deletion isn’t allowed.

HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD

{
  "error": "DELETE not allowed on this resource"
}

409 Conflict — The request conflicts with current state.

Use for: Duplicate creation, concurrent modification, state conflicts.

HTTP/1.1 409 Conflict

{
  "error": "User with this email already exists"
}

422 Unprocessable Entity — Request is well-formed but semantically invalid.

Use for: Valid JSON but business logic rejection.

HTTP/1.1 422 Unprocessable Entity

{
  "error": "Cannot transfer negative amount"
}

429 Too Many Requests — Rate limit exceeded.

Use for: Client is sending too many requests.

HTTP/1.1 429 Too Many Requests
Retry-After: 60

{
  "error": "Rate limit exceeded",
  "retryAfter": 60
}

Server Error Codes (5xx)

These say: “The server failed.”

500 Internal Server Error — Something went wrong on the server.

Use for: Unhandled exceptions, bugs, unexpected failures.

HTTP/1.1 500 Internal Server Error

{
  "error": "An unexpected error occurred",
  "requestId": "abc123"
}

502 Bad Gateway — Upstream server returned invalid response.

Use for: API gateway received bad response from backend.

503 Service Unavailable — Server temporarily unavailable.

Use for: Overload, maintenance, temporary outage.

HTTP/1.1 503 Service Unavailable
Retry-After: 300

{
  "error": "Service temporarily unavailable"
}

504 Gateway Timeout — Upstream server didn’t respond in time.

Use for: Backend timeout.

Why 4xx vs 5xx Matters

The distinction is critical for retries:

  • 4xx errors: Don’t retry automatically—the request is broken
  • 5xx errors: May retry—the server might recover
flowchart TD
    A[Request Failed] --> B{Status Code?}
    B -->|4xx| C[Client Error]
    B -->|5xx| D[Server Error]
    C --> E[Fix the request
Don't retry] D --> F[May retry
with backoff] style C fill:#fff9c4 style D fill:#ffccbc

4. Headers That Matter

Headers carry metadata. Some are essential for REST APIs.

Request Headers

Content-Type — Format of the request body.

Content-Type: application/json

Without this, the server might not know how to parse your body.

Accept — Format you want in the response.

Accept: application/json

Tells the server you want JSON (not HTML, XML, etc.).

Authorization — Authentication credentials.

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Carries the access token for authenticated requests.

User-Agent — Client identification.

User-Agent: MyApp/1.0.0 (iOS 16.0)

Helps with debugging and analytics.

Response Headers

Content-Type — Format of the response body.

Content-Type: application/json; charset=utf-8

Tells the client how to parse the response.

Location — URL of a created or redirected resource.

Location: /users/123

Used with 201 Created and 3xx redirects.

Cache-Control — Caching instructions.

Cache-Control: max-age=3600, public

Tells clients and proxies how to cache the response.

Retry-After — When to retry after rate limiting or errors.

Retry-After: 60

Used with 429 and 503. Value in seconds.

Headers for API Metadata

X-Request-Id — Unique identifier for the request.

X-Request-Id: abc123-def456

Essential for debugging and log correlation.

X-RateLimit-* — Rate limit information.

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1609459200

Tells clients their quota status.

5. What Breaks Clients

Understanding what breaks clients helps you avoid common mistakes.

Breaking Pattern 1: Using GET for Side Effects

# Server code
GET /[email protected]

What breaks:

  • Browser prefetching sends emails
  • Crawlers trigger actions
  • Caching returns old response but action already happened

Fix: Use POST for actions with side effects.

Breaking Pattern 2: Returning 200 for Errors

HTTP/1.1 200 OK

{
  "success": false,
  "error": "User not found"
}

What breaks:

  • Monitoring systems think everything is fine
  • HTTP error handlers don’t trigger
  • Retry logic doesn’t work

Fix: Use appropriate 4xx/5xx status codes.

Breaking Pattern 3: Inconsistent Content-Type

# Successful response
HTTP/1.1 200 OK
Content-Type: application/json

{"user": {...}}
# Error response (suddenly HTML!)
HTTP/1.1 500 Internal Server Error
Content-Type: text/html

<html><body>Error occurred</body></html>

What breaks:

  • JSON parsing fails on errors
  • Error handling becomes complex
  • Clients can’t reliably process responses

Fix: Always return the same format (JSON for APIs).

Breaking Pattern 4: Missing Content-Type

HTTP/1.1 200 OK

{"user": {...}}

No Content-Type header!

What breaks:

  • Clients may guess the wrong format
  • Some HTTP clients require explicit Content-Type
  • Browsers might try to “sniff” the content type incorrectly

Fix: Always include Content-Type header.

Breaking Pattern 5: Wrong Status Codes

# Wrong: 200 for validation error
POST /users HTTP/1.1

{"email": "invalid"}

---

HTTP/1.1 200 OK
{"error": "Invalid email"}

What breaks:

  • Clients think the request succeeded
  • Error handling logic isn’t triggered
  • Monitoring shows false positives

Fix: Use 400 for validation errors, 4xx/5xx for failures.

Breaking Pattern 6: Changing Behavior Without Notice

# Before: PUT replaces resource
PUT /users/123
{"name": "Alice"}
# Other fields remain

# After: PUT now clears missing fields
PUT /users/123
{"name": "Alice"}
# email and phone are now null!

What breaks:

  • Existing clients lose data
  • Integrations silently corrupt data

Fix: Document behavior clearly. Version APIs for breaking changes.

6. Quick Reference

Method Selection

You want to…Use
Get a resourceGET
Create a resourcePOST
Replace a resource entirelyPUT
Update part of a resourcePATCH
Delete a resourceDELETE
Check if resource existsHEAD
Get allowed methodsOPTIONS

Status Code Selection

SituationUse
Success with data200 OK
Created new resource201 Created
Success, no data to return204 No Content
Async processing started202 Accepted
Invalid request format400 Bad Request
Not authenticated401 Unauthorized
Not authorized403 Forbidden
Resource doesn’t exist404 Not Found
Method not supported405 Method Not Allowed
Conflict with current state409 Conflict
Valid format, invalid data422 Unprocessable Entity
Too many requests429 Too Many Requests
Server bug500 Internal Server Error
Upstream failure502 Bad Gateway
Temporarily unavailable503 Service Unavailable

Essential Headers

HeaderDirectionPurpose
Content-TypeBothBody format
AcceptRequestDesired response format
AuthorizationRequestAuthentication
LocationResponseNew resource URL
Cache-ControlResponseCaching rules
Retry-AfterResponseWhen to retry

What’s Next

This guide covered HTTP semantics at the criteria level—how to think about HTTP correctly.

For deeper topics like:

  • Edge cases in status code selection
  • Idempotency keys and retry strategies
  • Timeout handling and circuit breakers
  • Advanced caching strategies

See the upcoming course: HTTP for REST APIs in Production.


Deepen your understanding: