PUT vs POST: What's the Difference? (2026)
Clear explanation of HTTP PUT and POST methods with real examples
Start Building with Hypereal
Access Kling, Flux, Sora, Veo & more through a single API. Free credits to start, scale to millions.
No credit card required • 100k+ developers • Enterprise ready
PUT vs POST: What's the Difference? (2026)
The difference between HTTP PUT and POST is one of the most commonly misunderstood concepts in web development. Both methods send data to a server, but they have fundamentally different semantics, behaviors, and use cases. Understanding when to use each one is essential for building correct REST APIs.
This guide explains the differences clearly with practical examples, comparison tables, and real-world scenarios.
The Core Difference
PUT replaces a resource at a specific URL. If the resource exists, it is fully replaced. If it does not exist, it may be created.
POST submits data to a resource for processing. The server decides what to do with the data -- typically creating a new resource.
PUT /api/users/123 → Replace user 123 with the provided data
POST /api/users → Create a new user (server assigns the ID)
Think of it this way:
- PUT = "Put this data at this exact location"
- POST = "Process this data however you see fit"
Side-by-Side Comparison
| Characteristic | PUT | POST |
|---|---|---|
| Purpose | Replace/update a resource | Create a new resource or trigger processing |
| URL target | Specific resource (/users/123) |
Collection or endpoint (/users) |
| Idempotent | Yes | No |
| Request body | Complete resource representation | Partial data or payload |
| Response code (success) | 200 OK or 204 No Content | 201 Created |
| Safe | No | No |
| Cacheable | No | Rarely |
| Client knows URL | Yes (specifies exact resource) | No (server determines URL) |
Idempotency: The Key Difference
The most important technical difference is idempotency. An operation is idempotent if performing it multiple times produces the same result as performing it once.
PUT Is Idempotent
Sending the same PUT request 10 times has the same effect as sending it once:
# All three produce the exact same result
PUT /api/users/123 {"name": "Alice", "email": "alice@example.com"}
PUT /api/users/123 {"name": "Alice", "email": "alice@example.com"}
PUT /api/users/123 {"name": "Alice", "email": "alice@example.com"}
# User 123 is {"name": "Alice", "email": "alice@example.com"} regardless
POST Is NOT Idempotent
Sending the same POST request 3 times creates 3 different resources:
# Each request creates a new user
POST /api/users {"name": "Alice", "email": "alice@example.com"} → Created user 101
POST /api/users {"name": "Alice", "email": "alice@example.com"} → Created user 102
POST /api/users {"name": "Alice", "email": "alice@example.com"} → Created user 103
# Now you have 3 duplicate users
This distinction matters in real applications. If a network error occurs and the client retries the request, a PUT retry is safe (same result), but a POST retry may create duplicates.
Code Examples
Express.js REST API
const express = require('express');
const app = express();
app.use(express.json());
const users = new Map();
let nextId = 1;
// POST - Create a new user
// Client does NOT specify the ID
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
// Server assigns the ID
const id = nextId++;
const user = { id, name, email, createdAt: new Date() };
users.set(id, user);
// 201 Created with Location header pointing to the new resource
res.status(201)
.location(`/api/users/${id}`)
.json(user);
});
// PUT - Replace an existing user
// Client specifies the exact resource URL
app.put('/api/users/:id', (req, res) => {
const id = parseInt(req.params.id);
const { name, email } = req.body;
// Full replacement - all fields must be provided
const user = { id, name, email, updatedAt: new Date() };
users.set(id, user);
if (users.has(id)) {
// 200 OK if updating existing resource
res.status(200).json(user);
} else {
// 201 Created if resource was newly created
res.status(201).json(user);
}
});
app.listen(3000);
Python (Flask) REST API
from flask import Flask, request, jsonify
from datetime import datetime
app = Flask(__name__)
users = {}
next_id = 1
# POST - Create a new user
@app.route('/api/users', methods=['POST'])
def create_user():
global next_id
data = request.get_json()
user = {
'id': next_id,
'name': data['name'],
'email': data['email'],
'created_at': datetime.now().isoformat()
}
users[next_id] = user
next_id += 1
return jsonify(user), 201, {'Location': f'/api/users/{user["id"]}'}
# PUT - Replace a user
@app.route('/api/users/<int:user_id>', methods=['PUT'])
def replace_user(user_id):
data = request.get_json()
# PUT requires the complete resource representation
user = {
'id': user_id,
'name': data['name'],
'email': data['email'],
'updated_at': datetime.now().isoformat()
}
status_code = 200 if user_id in users else 201
users[user_id] = user
return jsonify(user), status_code
Calling with Fetch / Axios
// POST - Create a new user
const newUser = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Alice',
email: 'alice@example.com'
})
});
// Response: 201 Created, { id: 1, name: "Alice", ... }
// PUT - Update user 1
const updatedUser = await fetch('/api/users/1', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Alice Smith', // Updated name
email: 'alice@example.com' // Must include ALL fields
})
});
// Response: 200 OK, { id: 1, name: "Alice Smith", ... }
// With Axios
import axios from 'axios';
// POST
const { data: created } = await axios.post('/api/users', {
name: 'Bob',
email: 'bob@example.com'
});
// PUT
const { data: updated } = await axios.put(`/api/users/${created.id}`, {
name: 'Bob Johnson',
email: 'bob.johnson@example.com'
});
PUT vs POST vs PATCH
PATCH is often confused with PUT. Here is how all three differ:
| Method | Purpose | Body Contains | Idempotent |
|---|---|---|---|
| POST | Create new resource | Data for new resource | No |
| PUT | Replace entire resource | Complete resource | Yes |
| PATCH | Partially update resource | Only changed fields | Not guaranteed |
// The user currently is: { id: 1, name: "Alice", email: "alice@example.com", role: "user" }
// PUT - Must send ALL fields (replaces entire resource)
PUT /api/users/1
{
"name": "Alice Smith",
"email": "alice@example.com",
"role": "user"
}
// If you omit "role", it gets removed!
// PATCH - Send only what changed
PATCH /api/users/1
{
"name": "Alice Smith"
}
// Only "name" changes. "email" and "role" stay the same.
This is a common gotcha: PUT replaces the entire resource. If you omit a field in a PUT request, that field is removed (or set to null). Use PATCH when you only want to update specific fields.
When to Use PUT
Use PUT when:
| Scenario | Example |
|---|---|
| Client controls the resource URL | PUT /api/configs/theme |
| Full resource replacement | Updating a user profile form |
| Upsert operations | Create if missing, replace if exists |
| File uploads to specific paths | PUT /api/files/report-2026.pdf |
| Idempotent updates | Any update that should be safe to retry |
// Good PUT use cases
// 1. User settings (client knows the URL, full replacement)
app.put('/api/users/:id/settings', (req, res) => {
const settings = req.body; // Complete settings object
db.settings.replace(req.params.id, settings);
res.json(settings);
});
// 2. Upload a file to a known path
app.put('/api/documents/:filename', (req, res) => {
storage.write(req.params.filename, req.body);
res.status(204).end();
});
// 3. Upsert a configuration
app.put('/api/configs/:key', (req, res) => {
db.configs.upsert(req.params.key, req.body.value);
res.json({ key: req.params.key, value: req.body.value });
});
When to Use POST
Use POST when:
| Scenario | Example |
|---|---|
| Server assigns the resource ID | Creating a new order |
| Non-idempotent operations | Processing a payment |
| Complex processing | Running a search query |
| Triggering actions | Sending an email, starting a job |
| Data submission | Form submissions, file uploads |
// Good POST use cases
// 1. Create a resource (server assigns ID)
app.post('/api/orders', (req, res) => {
const order = db.orders.create(req.body);
res.status(201).json(order);
});
// 2. Trigger an action
app.post('/api/emails/send', (req, res) => {
emailService.send(req.body);
res.status(202).json({ message: 'Email queued' });
});
// 3. Process a payment (definitely not idempotent!)
app.post('/api/payments', (req, res) => {
const result = paymentGateway.charge(req.body);
res.status(201).json(result);
});
// 4. Complex queries that don't fit in a URL
app.post('/api/search', (req, res) => {
const results = searchEngine.query(req.body);
res.json(results);
});
Common Mistakes
Mistake 1: Using POST for Updates
// Bad - using POST to update
app.post('/api/users/123/update', (req, res) => { ... });
// Good - using PUT (or PATCH) for updates
app.put('/api/users/123', (req, res) => { ... });
Mistake 2: Using PUT Without Sending Complete Data
// Bad - PUT with partial data (this removes the email field!)
await axios.put('/api/users/1', { name: 'New Name' });
// Good - PUT with complete data
await axios.put('/api/users/1', {
name: 'New Name',
email: 'existing@email.com',
role: 'user'
});
// Or use PATCH for partial updates
await axios.patch('/api/users/1', { name: 'New Name' });
Mistake 3: Making POST Idempotent Expectations
// Bad - retrying POST without idempotency protection
try {
await axios.post('/api/payments', paymentData);
} catch (error) {
// This might create a duplicate payment!
await axios.post('/api/payments', paymentData);
}
// Good - use an idempotency key
await axios.post('/api/payments', paymentData, {
headers: { 'Idempotency-Key': 'unique-request-id-12345' }
});
Decision Flowchart
Use this to decide between PUT and POST:
Does the client know the exact resource URL?
├── Yes → Is this a full replacement?
│ ├── Yes → Use PUT
│ └── No (partial update) → Use PATCH
└── No → Is this creating a new resource?
├── Yes → Use POST
└── No (triggering an action) → Use POST
REST API Best Practices Summary
| Operation | Method | URL Pattern | Response |
|---|---|---|---|
| List all | GET | /api/users |
200 + array |
| Get one | GET | /api/users/123 |
200 + object |
| Create | POST | /api/users |
201 + object |
| Replace | PUT | /api/users/123 |
200 + object |
| Partial update | PATCH | /api/users/123 |
200 + object |
| Delete | DELETE | /api/users/123 |
204 No Content |
Wrapping Up
PUT and POST serve different purposes: PUT replaces a resource at a known URL and is idempotent, while POST creates new resources or triggers processing and is not idempotent. Use PUT when the client controls the resource location and can provide the complete resource. Use POST when the server determines the resource location or when the operation has side effects that should not be repeated.
If you are building REST APIs that integrate AI-powered media generation -- images, videos, or talking avatars -- try Hypereal AI free -- 35 credits, no credit card required. Our API follows REST best practices with clear PUT and POST semantics for all endpoints.
Related Articles
Start Building Today
Get 35 free credits on signup. No credit card required. Generate your first image in under 5 minutes.
