Documentation
JSON Patch in REST APIs
JSON Patch was designed for HTTP: it is the body of a PATCH request that changes part of a resource. Here is the full protocol picture - media types, headers, concurrency, status codes - plus client code that sends it.
To use JSON Patch against a REST API, send an HTTP PATCH request with the header Content-Type: application/json-patch+json and the array of RFC 6902 operations as the body. The server applies them in order; if one fails, the whole request fails.
A complete request
Request
PATCH /users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json-patch+json
If-Match: "v42"
[
{ "op": "test", "path": "/version", "value": 42 },
{ "op": "replace", "path": "/email", "value": "new@example.com" }
]The body is a JSON Patch document, nothing more. Three headers do the heavy lifting:
Content-Type: application/json-patch+json- the media type registered by RFC 6902. This is what routes the body to patch semantics instead of a generic JSON handler.If-Match- carries theETagfrom your earlier GET, so the request only succeeds if the resource has not changed since.Accept-Patch: application/json-patch+json- sent by servers (for example on a GET or an OPTIONS response) to advertise that the resource accepts JSON Patch. Defined by RFC 5789.
Responses and status codes
Conventions vary between APIs, but these are the common readings:
- 200 with the updated resource, or 204 with no body - the patch applied.
- 400 - the patch document itself is malformed (bad JSON, missing op/path).
- 409 - the request conflicts with the current state of the resource.
- 412 - your If-Match precondition failed: someone changed the resource first.
- 415 - the server does not accept application/json-patch+json bodies.
- 422 - the patch is well-formed but an operation failed against the current document (a missing path, an out-of-bounds index). Our errors guide maps each of those failures to its fix.
Concurrency: ETag + test
The dangerous pattern in REST is read-modify-write: GET a resource, edit it locally, PATCH it back - while another client does the same. Two complementary guards:
If-Match / ETag works at the HTTP layer: the server rejects the request with 412 when the resource changed. The test operation works inside the patch: it fails the whole operation list when a specific field is not what you expect. Many APIs support both; using either beats using neither.
Sending a patch from your code
JavaScript (fetch)
const response = await fetch('/users/123', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json-patch+json',
'If-Match': etag,
},
body: JSON.stringify([
{ op: 'replace', path: '/email', value: 'new@example.com' },
]),
});
if (response.status === 412) {
// resource changed - re-fetch and rebuild the patch
}Python (requests)
import requests
response = requests.patch(
'https://api.example.com/users/123',
json=[{ 'op': 'replace', 'path': '/email', 'value': 'new@example.com' }],
headers={
'Content-Type': 'application/json-patch+json',
'If-Match': etag,
},
)
if response.status_code == 412:
... # re-fetch and rebuildThe patch itself usually comes from a diff between the resource you fetched and your modified copy - see how to generate a JSON Patch - and is executed server-side exactly as in how to apply a JSON Patch.
JSON Patch or JSON Merge Patch?
RFC 7396 Merge Patch is the other PATCH body format: a partial document instead of an operation list, with its own media type (application/merge-patch+json). Many APIs accept both. The trade-offs - arrays, null semantics, test - are in JSON Patch vs JSON Merge Patch.
Common mistakes
Sending the patch with the wrong Content-Type
application/json-patch+json is what tells the server to interpret the body as RFC 6902 operations. With plain application/json many frameworks treat it as a generic JSON body and your operations never run.
Using PUT semantics with a PATCH body
PUT replaces the whole resource; JSON Patch modifies it. Sending a partial document to a PUT endpoint deletes everything you omitted. If the API expects a full document, use JSON Merge Patch (RFC 7396) or PUT - see the comparison guide.
No concurrency guard on read-modify-write
If you GET a resource, change it locally, and PATCH it back, someone else can write in between. Send If-Match with the ETag you received, or include a test operation on a version field.
Treating a 4xx as "the patch format is broken"
400 usually means malformed patch JSON, 409 a conflict with current state, 422 an operation that failed against the current document, 412 a failed If-Match. Read the status before debugging the wrong layer.
Reference
HTTP PATCH and Accept-Patch are defined by RFC 5789, the JSON Patch media type by RFC 6902, section 6, and conditional requests (ETag / If-Match) by RFC 9110, section 13.
Build the patch for your next PATCH call
Paste the resource as you fetched it and as you want it - the generator emits the exact operation list to send.
Open the generator with this example