PUT replaces the whole resource with what you send. PATCH changes part of it and leaves the rest alone. PUT is idempotent by definition; PATCH may or may not be, depending on what the patch says.
PUT
- Body is the complete new representation.
- Fields you omit are removed.
- Idempotent: same request twice, same result.
- Can create the resource at that URL if it does not exist.
- RFC 9110 §9.3.4.
PATCH
- Body describes a change, in a format the server documents.
- Fields you omit are untouched.
- Not idempotent in general (but a merge patch usually is).
- Requires the resource to exist.
- RFC 5789.
The same edit, both ways
A user record has name, email and phone. You want to change the email.
PUT /users/42
Content-Type: application/json
{
"name": "Ada Lovelace",
"email": "[email protected]",
"phone": "+44 20 7946 0000"
}PATCH /users/42
Content-Type: application/merge-patch+json
{
"email": "[email protected]"
}If the PUT body had left out phone, a correct server would delete the phone number. That is the property that makes PUT safe to repeat and dangerous to send from a client that has stale data: two people editing different fields will overwrite each other unless the API uses ETags and If-Match.
PATCH formats
PATCH does not define what the body means; the Content-Type does. Two standard formats exist.
JSON Merge Patch (RFC 7396)
Send a JSON object with only the fields to change. null deletes a field. Nested objects are merged recursively; arrays are replaced whole. Simple, readable, and what most APIs mean by PATCH even when they do not name the format.
Content-Type: application/merge-patch+json
{"email": "[email protected]", "phone": null, "address": {"city": "London"}}
JSON Patch (RFC 6902)
Send an array of operations: add, remove, replace, move, copy, test, each with a JSON Pointer path. More verbose, but it can edit inside arrays and can be conditional.
Content-Type: application/json-patch+json
[
{"op": "replace", "path": "/email", "value": "[email protected]"},
{"op": "remove", "path": "/phone"},
{"op": "add", "path": "/tags/-", "value": "vip"},
{"op": "test", "path": "/version", "value": 7}
]
Idempotency, precisely
Idempotent means repeating the request leaves the server in the same state as sending it once. PUT always has this property: the body is the whole state. A merge patch such as {"email": "x"} is idempotent too. A JSON Patch with {"op": "add", "path": "/tags/-"} is not: applied twice, it appends twice. Because PATCH is not idempotent by contract, HTTP clients and proxies will not retry it automatically after a network failure, whereas they may retry PUT.
If you need retry-safe partial updates, either use a merge patch (and document that it is idempotent) or accept an idempotency key header the way payment APIs do.
Which to offer in an API
- Offer PATCH with JSON Merge Patch for the common “update a few fields” case. It is what clients want to send and it minimises accidental data loss.
- Offer PUT when clients hold the complete representation anyway: configuration documents, file-like resources, settings pages that submit the whole form. Combine it with ETags so a stale PUT gets 412 instead of overwriting.
- Avoid PUT for partial updates. Some frameworks tolerate a partial PUT body and merge it; that makes the method mean PATCH, and breaks clients that rely on the specification.
- Validate the whole object after applying a patch, not just the fields sent. A patch that removes a required field should fail with 422.
Responses
Both methods return 200 with the updated representation, or 204 with no body. PUT that creates a new resource returns 201. PATCH to a missing resource returns 404. A patch document that cannot be applied (a test operation failed, a path does not exist) returns 409 or 422; a body in the wrong format returns 415.
In code
// fetch
await fetch("/users/42", {
method: "PATCH",
headers: { "Content-Type": "application/merge-patch+json", "If-Match": etag },
body: JSON.stringify({ email: "[email protected]" }),
});
# curl
curl -X PUT -H 'Content-Type: application/json' -d @user.json https://api.example.com/users/42
curl -X PATCH -H 'Content-Type: application/merge-patch+json' -d '{"email":"[email protected]"}' https://api.example.com/users/42
The properties of every method are summarised in the HTTP methods reference.