Client πŸ’» GET Server πŸ–₯️ 200 OK Response GET POST PUT PATCH DELETE
Backend

Understanding HTTP Methods and Status Codes

Mayur Dabhi
Mayur Dabhi
March 22, 2026
20 min read

Every time you browse a website, submit a form, or fetch data from an API, HTTP (Hypertext Transfer Protocol) is working behind the scenes. Understanding HTTP methods and status codes is fundamental to web developmentβ€”whether you're building REST APIs, debugging network issues, or optimizing your application's communication with servers.

In this comprehensive guide, we'll explore all the HTTP methods you need to know, decode status codes like a pro, and provide practical examples that you can use in your projects today. By the end, you'll speak HTTP fluently and handle any request-response scenario with confidence.

What You'll Learn
  • All HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) with real examples
  • Status codes decoded: 1xx, 2xx, 3xx, 4xx, 5xx and when to use each
  • RESTful API design patterns and best practices
  • Idempotency, safety, and cacheability of methods
  • Practical code examples in JavaScript, Node.js, and cURL
  • Common mistakes and how to avoid them

The HTTP Request-Response Cycle

Before diving into methods and codes, let's understand the fundamental cycle. Every HTTP interaction involves a request from the client and a response from the server. The method tells the server what action to perform, and the status code tells the client what happened.

HTTP Request-Response Cycle CLIENT 🌐 Browser / App Mobile / Server SERVER πŸ–₯️ Web Server API Endpoint REQUEST Method + URL + Headers + Body RESPONSE Status Code + Headers + Body GET /users 200 OK

The HTTP cycle: Client sends a request with a method, server returns a response with a status code

HTTP Methods Explained

HTTP methods (also called "verbs") tell the server what operation to perform on the specified resource. While HTTP defines many methods, these are the most commonly used in web development:

HTTP Methods & CRUD Operations GET Read / Retrieve πŸ“– Read data Safe & Cacheable POST Create / Submit βž• Create new Not Idempotent PUT Update / Replace πŸ”„ Replace all Idempotent PATCH Partial Update ✏️ Modify part Not Always Idempotent DELETE Remove / Delete πŸ—‘οΈ Delete data Idempotent CRUD Operations Mapping C POST R GET U PUT / PATCH D DELETE

HTTP methods map to CRUD operations: Create (POST), Read (GET), Update (PUT/PATCH), Delete (DELETE)

GET - Retrieve Data

GET Method Details

The GET method requests data from a specified resource. It's the most common HTTP method and should only be used to retrieve dataβ€”never to modify it.

  • Safe: Does not modify server state
  • Idempotent: Multiple identical requests produce the same result
  • Cacheable: Responses can be cached by browsers and CDNs
  • No body: Data sent via URL parameters (query strings)
GET Request Examples
// JavaScript Fetch API
const response = await fetch('/api/users');
const users = await response.json();

// With query parameters
const response = await fetch('/api/users?page=1&limit=10');

// Get single resource
const user = await fetch('/api/users/123');

// cURL
curl -X GET "https://api.example.com/users" -H "Accept: application/json"

POST - Create Data

POST Method Details

The POST method submits data to be processed by the server, typically resulting in a new resource being created or a state change on the server.

  • Not Safe: Modifies server state
  • Not Idempotent: Multiple requests may create multiple resources
  • Not Cacheable: By default (can be made cacheable with headers)
  • Has body: Data sent in request body (JSON, form data, etc.)
POST Request Examples
// JavaScript Fetch API - Create a user
const response = await fetch('/api/users', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        name: 'John Doe',
        email: 'john@example.com'
    })
});

const newUser = await response.json();
// Response: { id: 124, name: 'John Doe', email: 'john@example.com' }

// cURL
curl -X POST "https://api.example.com/users" \
  -H "Content-Type: application/json" \
  -d '{"name":"John Doe","email":"john@example.com"}'

PUT - Replace Data

PUT Method Details

The PUT method replaces the entire resource with the provided data. If the resource doesn't exist, PUT may create it (though this depends on server implementation).

  • Not Safe: Modifies server state
  • Idempotent: Multiple identical requests produce the same result
  • Replaces entire resource: All fields must be provided
  • Can create: If resource doesn't exist (server-dependent)
PUT Request Example
// Replace entire user resource
const response = await fetch('/api/users/123', {
    method: 'PUT',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        id: 123,
        name: 'John Updated',
        email: 'john.updated@example.com',
        role: 'admin'  // Must include ALL fields
    })
});

PATCH - Partial Update

PATCH Method Details

The PATCH method applies partial modifications to a resource. Unlike PUT, you only send the fields that need to be updated.

  • Not Safe: Modifies server state
  • Not Always Idempotent: Depends on implementation
  • Partial update: Only changed fields are sent
  • More efficient: Smaller payloads than PUT
PATCH Request Example
// Update only the email field
const response = await fetch('/api/users/123', {
    method: 'PATCH',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        email: 'new.email@example.com'  // Only send what changes
    })
});

DELETE - Remove Data

DELETE Method Details

The DELETE method removes the specified resource from the server.

  • Not Safe: Modifies server state
  • Idempotent: Deleting twice has same effect as once
  • Usually no body: Resource identified by URL
  • Returns: 200 OK, 202 Accepted, or 204 No Content
DELETE Request Example
// Delete a user
const response = await fetch('/api/users/123', {
    method: 'DELETE'
});

if (response.status === 204) {
    console.log('User deleted successfully');
}

// cURL
curl -X DELETE "https://api.example.com/users/123"

Other HTTP Methods

HEAD, OPTIONS, TRACE, CONNECT

HEAD - Same as GET but returns only headers, not the body. Useful for checking if a resource exists or getting metadata.

curl -I "https://api.example.com/users/123"

OPTIONS - Returns the allowed HTTP methods for a resource. Used in CORS preflight requests.

curl -X OPTIONS "https://api.example.com/users" -i
# Response Header: Allow: GET, POST, PUT, DELETE, OPTIONS

TRACE - Echoes the received request for debugging. Usually disabled for security reasons.

CONNECT - Establishes a tunnel to the server, typically used for HTTPS through HTTP proxies.

Method Properties Comparison

Understanding the properties of each method helps you choose the right one for your use case:

Method Safe Idempotent Cacheable Request Body
GET βœ… Yes βœ… Yes βœ… Yes ❌ No
POST ❌ No ❌ No ⚠️ Conditional βœ… Yes
PUT ❌ No βœ… Yes ❌ No βœ… Yes
PATCH ❌ No ⚠️ Not guaranteed ❌ No βœ… Yes
DELETE ❌ No βœ… Yes ❌ No ⚠️ Optional
Understanding Idempotency

Idempotent means that making the same request multiple times produces the same result as making it once. This is crucial for handling network retries safely:

  • GET /users/123 β†’ Always returns the same user (idempotent)
  • DELETE /users/123 β†’ First deletes, subsequent calls return 404 (same final state = idempotent)
  • POST /users β†’ Each call creates a new user (NOT idempotent)

HTTP Status Codes Decoded

Status codes are three-digit numbers that tell you exactly what happened with your request. They're grouped into five categories:

HTTP Status Code Categories 1xx Informational Continue Processing πŸ’­ "Hold on..." 2xx Success 200 OK 201 Created 204 No Content βœ… "All good!" 3xx Redirection 301 Moved 302 Found 304 Not Modified β†ͺ️ "Go here instead" 4xx Client Error 400 Bad Request 401 Unauthorized 404 Not Found πŸ™ˆ "Your bad" 5xx Server Error 500 Internal Error 502 Bad Gateway 503 Unavailable πŸ”₯ "Our bad"

The five categories of HTTP status codes and what they mean

2xx Success Codes

2xx Success - Request Succeeded

200 OK - The request succeeded. Response depends on the method used.
201 Created - A new resource was created. Usually returned after POST.
202 Accepted - Request accepted but processing not complete. Good for async operations.
204 No Content - Success but no content to return. Common for DELETE requests.

3xx Redirection Codes

3xx Redirection - Further Action Needed

301 Moved Permanently - Resource has permanently moved. Browsers cache this.
302 Found - Temporary redirect. Resource is temporarily at another URL.
304 Not Modified - Cached version is still valid. No body returned.
307 Temporary Redirect - Like 302 but preserves the HTTP method.
308 Permanent Redirect - Like 301 but preserves the HTTP method.

4xx Client Error Codes

4xx Client Error - Problem with Request

400 Bad Request - Invalid syntax, malformed JSON, missing required fields.
401 Unauthorized - Authentication required. No valid credentials provided.
403 Forbidden - Authenticated but not authorized. You don't have permission.
404 Not Found - Resource doesn't exist at this URL.
405 Method Not Allowed - HTTP method not supported for this resource.
409 Conflict - Request conflicts with current state (e.g., duplicate email).
422 Unprocessable Entity - Valid syntax but semantic errors (validation failed).
429 Too Many Requests - Rate limit exceeded. Slow down!
401 vs 403: Know the Difference
  • 401 Unauthorized: "Who are you?" - No authentication provided or invalid credentials
  • 403 Forbidden: "I know who you are, but no." - Authenticated but lacking permission

5xx Server Error Codes

5xx Server Error - Something Went Wrong

500 Internal Server Error - Generic server error. Check your logs!
501 Not Implemented - Server doesn't support the requested functionality.
502 Bad Gateway - Server acting as gateway got invalid response upstream.
503 Service Unavailable - Server overloaded or down for maintenance.
504 Gateway Timeout - Upstream server didn't respond in time.

RESTful API Design Patterns

Now that you understand HTTP methods and status codes, let's see how they come together in RESTful API design:

RESTful API Endpoints - Users Resource
// Standard REST endpoints for a Users resource

GET    /api/users          β†’ 200 OK (list all users)
GET    /api/users/123      β†’ 200 OK (get single user) | 404 Not Found
POST   /api/users          β†’ 201 Created (create user) | 400/422 (validation error)
PUT    /api/users/123      β†’ 200 OK (replace user) | 404 Not Found
PATCH  /api/users/123      β†’ 200 OK (update fields) | 404 Not Found
DELETE /api/users/123      β†’ 204 No Content (deleted) | 404 Not Found

// Nested resources
GET    /api/users/123/posts       β†’ 200 OK (user's posts)
POST   /api/users/123/posts       β†’ 201 Created (create post for user)

// Actions (non-CRUD operations)
POST   /api/users/123/activate    β†’ 200 OK (activate user)
POST   /api/users/123/deactivate  β†’ 200 OK (deactivate user)

Complete API Example

Express.js REST API
const express = require('express');
const app = express();
app.use(express.json());

let users = [
    { id: 1, name: 'John', email: 'john@example.com' }
];

// GET all users
app.get('/api/users', (req, res) => {
    res.status(200).json({ data: users });
});

// GET single user
app.get('/api/users/:id', (req, res) => {
    const user = users.find(u => u.id === parseInt(req.params.id));
    if (!user) {
        return res.status(404).json({ error: 'User not found' });
    }
    res.status(200).json({ data: user });
});

// POST create user
app.post('/api/users', (req, res) => {
    const { name, email } = req.body;
    
    if (!name || !email) {
        return res.status(400).json({ error: 'Name and email required' });
    }
    
    const newUser = { id: users.length + 1, name, email };
    users.push(newUser);
    res.status(201).json({ data: newUser });
});

// PATCH update user
app.patch('/api/users/:id', (req, res) => {
    const user = users.find(u => u.id === parseInt(req.params.id));
    if (!user) {
        return res.status(404).json({ error: 'User not found' });
    }
    
    Object.assign(user, req.body);
    res.status(200).json({ data: user });
});

// DELETE user
app.delete('/api/users/:id', (req, res) => {
    const index = users.findIndex(u => u.id === parseInt(req.params.id));
    if (index === -1) {
        return res.status(404).json({ error: 'User not found' });
    }
    
    users.splice(index, 1);
    res.status(204).send();
});

app.listen(3000);
Client-Side API Calls
// API wrapper with proper error handling
class ApiClient {
    constructor(baseUrl) {
        this.baseUrl = baseUrl;
    }
    
    async request(method, endpoint, data = null) {
        const options = {
            method,
            headers: { 'Content-Type': 'application/json' }
        };
        
        if (data && ['POST', 'PUT', 'PATCH'].includes(method)) {
            options.body = JSON.stringify(data);
        }
        
        const response = await fetch(`${this.baseUrl}${endpoint}`, options);
        
        // Handle different status codes
        if (response.status === 204) return null;
        
        const json = await response.json();
        
        if (!response.ok) {
            throw new Error(json.error || `HTTP ${response.status}`);
        }
        
        return json.data;
    }
    
    getUsers() { return this.request('GET', '/users'); }
    getUser(id) { return this.request('GET', `/users/${id}`); }
    createUser(data) { return this.request('POST', '/users', data); }
    updateUser(id, data) { return this.request('PATCH', `/users/${id}`, data); }
    deleteUser(id) { return this.request('DELETE', `/users/${id}`); }
}

// Usage
const api = new ApiClient('/api');
const users = await api.getUsers();
const newUser = await api.createUser({ name: 'Jane', email: 'jane@example.com' });
Robust Error Handling
async function fetchWithRetry(url, options, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            const response = await fetch(url, options);
            
            // Handle different status code categories
            switch (true) {
                case response.status >= 200 && response.status < 300:
                    return response; // Success!
                
                case response.status === 401:
                    // Refresh token and retry
                    await refreshAuthToken();
                    continue;
                
                case response.status === 429:
                    // Rate limited - wait and retry
                    const retryAfter = response.headers.get('Retry-After') || 5;
                    await delay(retryAfter * 1000);
                    continue;
                
                case response.status >= 500:
                    // Server error - retry with backoff
                    if (i < retries - 1) {
                        await delay(Math.pow(2, i) * 1000);
                        continue;
                    }
                    throw new Error(`Server error: ${response.status}`);
                
                default:
                    // Client errors (4xx) - don't retry
                    const error = await response.json();
                    throw new Error(error.message || `HTTP ${response.status}`);
            }
        } catch (err) {
            if (i === retries - 1) throw err;
        }
    }
}

const delay = ms => new Promise(r => setTimeout(r, ms));

Quick Reference Chart

Here's a handy reference for the most common status codes you'll use:

200
OK - Success
201
Created
204
No Content
301
Moved Permanently
304
Not Modified
400
Bad Request
401
Unauthorized
403
Forbidden
404
Not Found
422
Validation Error
500
Server Error
503
Unavailable

Common Mistakes to Avoid

Don't Do These
  • Using GET for mutations: GET /api/users/123/delete β€” Use DELETE instead
  • Returning 200 for errors: Always use appropriate error codes (4xx/5xx)
  • Using 404 for authorization: Use 403 Forbidden for permission issues
  • Ignoring idempotency: POST should create; PUT should be repeatable
  • Generic 500 for everything: Use specific codes (400, 422, 409) for client errors
Best Practices
  • Use nouns, not verbs: /api/users not /api/getUsers
  • Consistent error format: { "error": "message", "code": "ERROR_CODE" }
  • Include Location header: With 201 Created, point to the new resource
  • Use 202 Accepted: For async operations that will complete later
  • Document your API: Use OpenAPI/Swagger for clear documentation

Conclusion

HTTP methods and status codes are the foundation of web communication. By mastering them, you can:

Remember: methods tell the server what to do, status codes tell you what happened. Use them correctly, and your APIs will be a joy to work with.

Key Takeaways

  • GET for reading, POST for creating, PUT/PATCH for updating, DELETE for removing
  • 2xx = Success, 3xx = Redirect, 4xx = Client error, 5xx = Server error
  • Idempotent methods (GET, PUT, DELETE) are safe to retry
  • Always return appropriate status codesβ€”don't hide errors behind 200 OK
  • Use 401 for "who are you?" and 403 for "you can't do that"
HTTP API REST Status Codes Web Development Backend
Mayur Dabhi

Mayur Dabhi

Full Stack Developer with 5+ years of experience building web applications with Laravel, React, and Node.js. Passionate about clean code and sharing knowledge.