PUT vs PATCH: REST Methods Explained
Quick Answer
PUT replaces a resource entirely — you send the complete new representation — while PATCH applies a partial update, sending only the fields that change. PUT is idempotent by definition; PATCH is not guaranteed to be. Use PUT when the client owns the full object, and PATCH for partial edits like toggling a status.
PUT and PATCH are the two HTTP methods REST APIs use to update an existing resource, and choosing the wrong one is a common source of subtle bugs. The short version: PUT replaces a resource entirely, while PATCH applies a partial modification. This guide explains how each works, why the distinction matters, and how to decide between them.
The core difference
The HTTP specifications define both methods in terms of what the request body represents. PUT is specified in RFC 9110, while PATCH has its own document, RFC 5789. With PUT, the request body is the complete, desired state of the resource at the target URL. The server takes that representation and stores it as-is, creating the resource if it does not already exist or replacing it if it does.
With PATCH, the request body is a set of instructions describing how to change the resource. The server reads those instructions and applies them to the existing stored representation. PATCH does not assume the body is the full resource; it only carries the delta.
A useful mental model: PUT says "make the resource look exactly like this," and PATCH says "change these specific things and leave the rest alone."
How PUT works
Because PUT carries the entire representation, any field you omit from the body is treated as absent. A correct PUT implementation will set omitted fields to their default or null, not preserve their previous values. That is the most frequent PUT mistake: sending only the changed fields and assuming the rest stay intact. They do not, if the server follows the spec.
Consider a user resource that currently looks like this:
PUT /users/42
Content-Type: application/json
{
"name": "Ada Lovelace",
"email": "ada@example.com",
"role": "admin"
}
This replaces the whole user record. If the stored user previously had a phone field and your PUT body omits it, the phone is gone after the request. To change only the email with PUT, you must still send name, role, and every other field.
How PATCH works
PATCH sends only what changes. Updating just the email looks like this:
PATCH /users/42
Content-Type: application/merge-patch+json
{ "email": "ada@new.example.com" }
The server merges that field into the existing record and leaves everything else untouched. There are two common formats for the PATCH body. JSON Merge Patch (RFC 7396) is the simpler one: the body is a partial object, and setting a field to null means "delete this field." JSON Patch (RFC 6902) is more expressive, using an array of operations such as add, remove, replace, and move, each targeting a path:
PATCH /users/42
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/email", "value": "ada@new.example.com" },
{ "op": "remove", "path": "/phone" }
]
Whichever you pick, set the Content-Type header accurately so the server knows how to interpret the body. Mixing formats is a frequent cause of silent failures.
Idempotency: the property that trips people up
An idempotent method produces the same server state whether you send the request once or many times. PUT is idempotent. Sending the same full representation ten times leaves the resource in the same state as sending it once, which makes PUT safe to retry after a network timeout.
PATCH is not guaranteed to be idempotent. Whether it is depends entirely on the operations. A replace on a field is idempotent, but an operation like "increment the counter by 1" or "append to a list" changes state every time it runs. Because the HTTP spec makes no idempotency promise for PATCH, clients should not blindly retry a failed PATCH the way they can with PUT. If you need retry-safe partial updates, design your PATCH operations to be idempotent or use an idempotency key. Neither PUT nor PATCH is a safe method, meaning both can modify server state, so neither should be cached or prefetched.
Comparison at a glance
| Aspect | PUT | PATCH |
|---|---|---|
| Body represents | Complete resource | Changes to apply |
| Omitted fields | Cleared / reset | Left unchanged |
| Idempotent | Yes | Not guaranteed |
| Can create a resource | Yes, at a known URL | Generally no |
| Bandwidth on large objects | Higher (full payload) | Lower (delta only) |
| Typical body format | Full JSON object | Merge Patch or JSON Patch |
When to use each
Reach for PUT when the client knows and controls the complete state of the resource, when you want create-or-replace semantics at a specific URL (uploading a file to a known path, for example), or when you want the simplicity of idempotent retries. PUT pairs naturally with optimistic concurrency using an If-Match header and an ETag to avoid clobbering someone else's update.
Reach for PATCH when resources are large and you only need to change a few fields, when partial updates from a UI form are the norm, or when you want to express richer mutations like removing a field or reordering a list. Most CRUD-style APIs that let users edit one field at a time are better served by PATCH.
You can explore the full set of verbs in the HTTP Methods Reference, and if you are building a broader API, the basics in what is an API give helpful context.
Status codes and common pitfalls
A successful update typically returns 200 OK (with the updated representation in the body) or 204 No Content (when the body is empty). A PUT that creates a brand-new resource should return 201 Created. If a PATCH body references a field or path that does not exist, or uses an operation the server rejects, return 422 Unprocessable Content. A malformed body earns 400 Bad Request. The full list is in the HTTP Status Codes reference and the HTTP status codes guide.
Watch out for these traps. First, do not use PATCH semantics under a PUT verb: if your "PUT" merges partial data, clients relying on replacement behavior will be surprised, and you have broken idempotency expectations. Second, validate PATCH operations before applying any of them so a request never leaves the resource half-updated; apply the change set atomically. Third, guard against lost updates on concurrent edits with ETags and conditional requests rather than last-write-wins. Finally, document which PATCH format your API accepts, because Merge Patch and JSON Patch are not interchangeable.
When testing these requests from the command line, the cURL to Code Converter turns a working curl call into client code in your language, and if your update endpoints are write-heavy you may also want to review rate limit strategies for APIs.
Frequently Asked Questions
PUT replaces the entire resource with the representation in the request body, so any omitted field is reset. PATCH applies only a partial change and leaves unmentioned fields untouched. PUT is for full replacement; PATCH is for partial updates.
Not necessarily. The HTTP spec does not guarantee PATCH idempotency. A replace operation is idempotent, but operations like 'increment by 1' or 'append to a list' change state on every call. PUT, by contrast, is always idempotent, which makes it safe to retry.
Yes. If the client knows the target URL, PUT has create-or-replace semantics: the server creates the resource when it is absent and returns 201 Created, or replaces it when it already exists and returns 200 or 204. PATCH is generally used only to modify resources that already exist.
JSON Merge Patch (RFC 7396) sends a partial object, where a field set to null means delete it. JSON Patch (RFC 6902) sends an array of operations such as add, remove, replace, and move that each target a path. Merge Patch is simpler; JSON Patch is more expressive. Set the matching Content-Type so the server interprets the body correctly.
Return 200 OK when you send the updated resource back, or 204 No Content when the response body is empty. A PUT that creates a new resource returns 201 Created. Use 400 for a malformed body and 422 when a PATCH operation references a nonexistent path or is otherwise unprocessable.