---
title: Partial Content Update (PATCH)
description: Update specific fields of existing objects without sending the full payload using the PATCH /v1/content endpoint.
slug: indexing/api/v1/partial-update
docKind: endpoint
hub: indexing
tableOfContents: true
---
import ApiSection from "../../../../../components/ApiSection.astro";
import ApiEndpoint from "../../../../../components/ApiEndpoint.astro";
import ApiCodeTabs from "../../../../../components/ApiCodeTabs.astro";
import { Aside } from "@astrojs/starlight/components";

<Aside type="note">
  This endpoint requires HMAC authentication. Refer to the [Authentication](/platform-foundations/api-principles/#authentication) documentation for details.
</Aside>

<ApiSection>
  <div slot="code">
    <ApiEndpoint method="PATCH" url="https://live.luigisbox.com/v1/content" />
  </div>
## Overview

This endpoint is ideal for making small, frequent updates to existing objects, such as changing a product price, description, or availability. You only need to send the `identity` of the object and the specific `fields` you want to add or modify.

### Key behaviour

- This operation is **incremental** and does not affect fields you omit from the request.
- It **cannot** be used to create new objects. If an `identity` is not found, the request returns an error.
- You cannot change an object's `type`.

### Common use cases

- Updating prices or stock levels.
- Adding a promotional label or updating a product description.
- Changing `availability` status.
- Synchronizing real-time data from your database to the search index without a full reindex.

The batch limit for this endpoint is 300 objects per request.

<Aside type="danger">
  For adding new products or completely overwriting existing ones, use the [Content Update API](/indexing/api/v1/content-update/) instead.
</Aside>
</ApiSection>

<ApiSection>
## Request parameters

The root of your request body must be a JSON object containing a single key, `objects`.

### Top-level Request Structure

| Parameter | Type | Required | Description |
| :-- | :-- | :-- | :-- |
| `objects` | Array | ✓ | An array of one or more objects to be partially updated. |

### Object structure

Each object in the `objects` array must contain an `identity` and the fields you want to change.

| Parameter | Type | Required | Description |
| :-- | :-- | :-- | :-- |
| `identity` | String | ✓ | The unique identifier of the object you want to update. |
| `fields` | Object |  | A key-value map of attributes to add or modify. |
| `nested` | Array |  | An array of nested objects. Sending this key replaces the entire existing `nested` array for the object. |

  <div slot="code">
    <h4 class="code-section-title">Example: Request Body (JSON)</h4>

```json
{
  "objects": [
    {
      "identity": "product-001",
      "fields": {
        "price": "EUR 44.99"
      }
    },
    {
      "identity": "product-002",
      "fields": {
        "description": "Now with an updated, more detailed description!"
      }
    }
  ]
}
```

  </div>
</ApiSection>

<ApiSection>
## How to Send a Partial Update Request

The examples show how to prepare and send a request to the API endpoint.

Authentication is required for all requests. The code demonstrates how to construct the necessary headers, including `Date` and `Authorization`. The `Authorization` header requires a dynamically generated HMAC signature. For a detailed explanation of how to create this signature, refer to the main API Authentication guide.

<Aside type="danger">
  Your **Private Key** is a secret and should never be exposed in client-side code, such as frontend JavaScript. It should only be used on a secure server.
</Aside>

<Aside type="tip">
  Your API Keys can be found in the Luigi's Box application under "General settings" > "Integration settings".
</Aside>

  <div slot="code">
    <h4 class="code-section-title">Code Examples</h4>
  <ApiCodeTabs syncKey="indexing-partial-update-request">
    <div slot="ruby">

```ruby
# --- Ruby Example for PATCH /v1/content ---
# Assumes 'faraday' gem is installed (gem install faraday)

require 'faraday'
require 'json'
require 'time'
require 'openssl'
require 'base64'

def generate_luigisbox_digest(private_key, http_method, endpoint_path, date_header, content_type_header)
  data = "#{http_method}\n#{content_type_header}\n#{date_header}\n#{endpoint_path}"
  dg = OpenSSL::Digest.new('sha256')
  Base64.strict_encode64(OpenSSL::HMAC.digest(dg, private_key, data)).strip
end

YOUR_PUBLIC_KEY = "your_public_api_key"
YOUR_PRIVATE_KEY = "your_private_api_key"
LUIGISBOX_HOST = 'https://live.luigisbox.com'
ENDPOINT_PATH = '/v1/content'

product_data = [
  {
    identity: "product-001",
    fields: {
      price: "EUR 44.99"
    }
  },
  {
    identity: "product-002",
    fields: {
      description: "Now with an updated, more detailed description!"
    }
  }
]
request_body_json = { objects: product_data }.to_json

http_method = 'PATCH'
content_type = 'application/json; charset=utf-8'
current_date = Time.now.httpdate

signature = generate_luigisbox_digest(YOUR_PRIVATE_KEY, http_method, ENDPOINT_PATH, current_date, content_type)
authorization_header = "ApiAuth #{YOUR_PUBLIC_KEY}:#{signature}"

connection = Faraday.new(url: LUIGISBOX_HOST) do |conn|
  conn.use FaradayMiddleware::Gzip
end

response = connection.patch(ENDPOINT_PATH) do |req|
  req.headers['Content-Type'] = content_type
  req.headers['Date'] = current_date
  req.headers['Authorization'] = authorization_header
  req.body = request_body_json
end

response_body = JSON.parse(response.body)
if response.success? && response_body['ok_count'] && response_body['ok_count'] > 0
  puts "Product successfully updated:"
  puts JSON.pretty_generate(response_body)
else
  puts "Error updating product: HTTP Status #{response.status}, Response: #{response.body}"
end
```
    </div>
    <div slot="php">

```php
<?php
// PHP Example for PATCH /v1/content
// Assumes Guzzle is installed:
// composer require guzzlehttp/guzzle

require 'vendor/autoload.php';

use GuzzleHttp\Client;

function generateLuigisboxDigest($privateKey, $httpMethod, $endpointPath, $dateHeader, $contentTypeHeader) {
    $data = "{$httpMethod}\n{$contentTypeHeader}\n{$dateHeader}\n{$endpointPath}";
    $hash = hash_hmac('sha256', $data, $privateKey, true);
    return trim(base64_encode($hash));
}

$YOUR_PUBLIC_KEY  = "your_public_api_key";
$YOUR_PRIVATE_KEY = "your_private_api_key";
$LUIGISBOX_HOST   = 'https://live.luigisbox.com';
$ENDPOINT_PATH    = '/v1/content';

$product_data = [
    [
        'identity' => 'product-001',
        'fields' => [
            'price' => 'EUR 44.99',
        ]
    ],
    [
        'identity' => 'product-002',
        'fields' => [
            'description' => 'Now with an updated, more detailed description!',
        ]
    ]
];

$request_body = ['objects' => $product_data];
$http_method  = 'PATCH';
$content_type = 'application/json; charset=utf-8';
$current_date = gmdate('D, d M Y H:i:s') . ' GMT';

$signature = generateLuigisboxDigest($YOUR_PRIVATE_KEY, $http_method, $ENDPOINT_PATH, $current_date, $content_type);
$authorization_header = "ApiAuth {$YOUR_PUBLIC_KEY}:{$signature}";

$client = new GuzzleHttp\Client();
$response = $client->request(
    $http_method,
    "{$LUIGISBOX_HOST}{$ENDPOINT_PATH}",
    [
        'headers' => [
            'Accept-Encoding' => 'gzip, deflate',
            'Content-Type' => $content_type,
            'Date' => $current_date,
            'Authorization' => $authorization_header,
        ],
        'json' => $request_body
    ]
);

$response_body = json_decode($response->getBody(), true);

if ($response->getStatusCode() == 200 && isset($response_body['ok_count']) && $response_body['ok_count'] > 0) {
    echo "Product successfully updated:" . PHP_EOL;
    echo json_encode($response_body, JSON_PRETTY_PRINT);
} else {
    echo "Error updating product: HTTP Status " . $response->getStatusCode() . "\nResponse: " . $response->getBody();
}
```
    </div>
    <div slot="javascript">

```javascript
const axios = require('axios');
const crypto = require('crypto');

function generateLuigisBoxDigest(privateKey, httpMethod, endpointPath, dateHeader, contentTypeHeader) {
  const data = `${httpMethod}\n${contentTypeHeader}\n${dateHeader}\n${endpointPath}`;
  const hmac = crypto.createHmac('sha256', privateKey);
  hmac.update(data);
  return hmac.digest('base64').trim();
}

const YOUR_PUBLIC_KEY = "your_public_api_key";
const YOUR_PRIVATE_KEY = "your_private_api_key";
const LUIGISBOX_HOST = 'https://live.luigisbox.com';
const ENDPOINT_PATH = '/v1/content';

const productData = [
  {
    identity: "product-001",
    fields: {
      price: "EUR 44.99"
    }
  },
  {
    identity: "product-002",
    fields: {
      description: "Now with an updated, more detailed description!"
    }
  }
];

const requestBody = { objects: productData };
const httpMethod = 'PATCH';
const contentType = 'application/json; charset=utf-8';
const currentDate = new Date().toUTCString();

const signature = generateLuigisBoxDigest(YOUR_PRIVATE_KEY, httpMethod, ENDPOINT_PATH, currentDate, contentType);
const authorizationHeader = `ApiAuth ${YOUR_PUBLIC_KEY}:${signature}`;

axios({
  method: httpMethod,
  url: LUIGISBOX_HOST + ENDPOINT_PATH,
  headers: {
    'Content-Type': contentType,
    'Date': currentDate,
    'Authorization': authorizationHeader
  },
  data: requestBody
})
  .then((response) => {
    const responseBody = response.data;
    if (response.status === 200 && responseBody.ok_count && responseBody.ok_count > 0) {
      console.log("Product successfully updated:", JSON.stringify(responseBody, null, 2));
    } else {
      console.error("Error updating product: HTTP Status " + response.status + ", Response: " + JSON.stringify(responseBody));
    }
  })
  .catch((error) => {
    console.error("Exception:", error.message);
  });
```
    </div>
  </ApiCodeTabs>

  </div>
</ApiSection>

<ApiSection>
## Error handling

| HTTP Status | Description |
| :-- | :-- |
| **400 Bad Request** | The request structure is invalid, the JSON is malformed, or an object was not found. |
| **413 Payload Too Large** | The request exceeds 5 MB, or 10 MB if compressed. Reduce batch size or enable compression. |
| **429 Too Many Requests** | The rate limit has been exceeded. Check the `Retry-After` header and see the [Throttling docs](/platform-foundations/api-principles/#throttling). |

  <div slot="code">
    <h4 class="code-section-title">Example: Error Response</h4>

```json
{
  "ok_count": 0,
  "errors_count": 1,
  "errors": {
    "product-001": {
      "type": "not_found",
      "reason": "Identity not in catalog"
    }
  }
}
```

  </div>
</ApiSection>

## Related endpoints

- [Content Update](/indexing/api/v1/content-update/) creates or replaces objects.
- [Content Removal](/indexing/api/v1/content-removal/) deletes objects from the index.
- [Update by Query](/indexing/api/v1/query-update/) performs bulk updates based on search criteria.
