Update by Query (PATCH)
Perform asynchronous bulk updates on objects that match a specific set of criteria using the PATCH /v1/update_by_query endpoint.
This endpoint requires HMAC authentication. Refer to the Authentication documentation for details.
Overview
This endpoint allows you to perform bulk updates on objects that match a specific set of criteria. It is an asynchronous operation: the API call initiates a background job, and you must poll a status URL to check its progress. This is ideal for large-scale changes, such as applying a sale to an entire brand, without requiring a list of individual identities.
https://live.luigisbox.com/v1/update_by_query
Request Structure
The request body consists of two main parts: a search
object to define the update criteria and an update
object to specify the changes.
Request Body Structure
Parameter | Type | Required | Description |
---|---|---|---|
search |
Object | ✓ | An object containing rules to select which items will be updated. |
update |
Object | ✓ | An object specifying the field changes to apply to all matching items. |
search
Object Parameters
Parameter | Type | Required | Description |
---|---|---|---|
types |
Array | ✓ | An array of strings specifying the object types to include in the search (e.g., ["item"]). |
partial |
Object | ✓ | An object containing the fields to match. Currently, only fields is supported within partial . |
update
Object Parameters
Parameter | Type | Required | Description |
---|---|---|---|
fields |
Object | ✓ | An object specifying the fields to update and their new values. |
Example: request body (JSON)
{
"search": {
"types": [
"product"
],
"partial": {
"fields": {
"color": "olive"
}
}
},
"update": {
"fields": {
"color": "green"
}
}
}
How to Initiate an Update Job
The process involves two steps. First, you send a PATCH
request with your search
and update
rules to start the background job.
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,
please refer to our main API Authentication guide.
Your Private Key is a secret and should never be exposed in client-side code (like frontend JavaScript). It should only be used on a secure server.
Your API Keys can be found in the Luigi's Box application under "General settings" > "Integration settings".
The search
criteria work on a partial match principle for array values and are case-sensitive. For example, a search for color: 'olive'
will match a product with color: ['olive', 'red']
. However, a search for category: 'jeans'
will not match a product with category: 'Jeans'
.
Important: The update operation is a replacement for the specified fields, not an incremental change.
Example: Initiate Job
# --- Ruby Example for PATCH /v1/update_by_query ---
# Assumes 'faraday' gem is installed (gem install faraday)
require 'faraday'
require 'json'
require 'time'
require 'openssl'
require 'base64'
# Helper function for authentication
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" # Keep secure!
LUIGISBOX_HOST = 'https://live.luigisbox.com'
ENDPOINT_PATH = '/v1/update_by_query'
update_request = {
search: {
types: ["product"],
partial: { fields: { color: "olive" } }
},
update: { fields: { color: "green" } }
}
request_body_json = update_request.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
puts response.body
<?php
// PHP Example for PATCH /v1/update_by_query
// 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"; // Keep secure!
$LUIGISBOX_HOST = 'https://live.luigisbox.com';
$ENDPOINT_PATH = '/v1/update_by_query';
$update_request = [
'search' => [
'types' => ['product'],
'partial' => ['fields' => ['color' => 'olive']]
],
'update' => [
'fields' => ['color' => 'green']
]
];
$request_body = $update_request;
$http_method = 'PATCH';
$content_type = 'application/json; charset=utf-8';
// Create HTTP-date string
// (e.g., "Tue, 22 May 2025 14:32:00 GMT")
$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
]
);
echo $response->getBody();
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();
}
// Configuration
const YOUR_PUBLIC_KEY = "your_public_api_key";
const YOUR_PRIVATE_KEY = "your_private_api_key"; // Keep secure!
const LUIGISBOX_HOST = 'https://live.luigisbox.com';
const ENDPOINT_PATH = '/v1/update_by_query';
// Product data
const updateRequest = {
search: {
types: ["product"],
partial: { fields: { color: "olive" } }
},
update: { fields: { color: "green" } }
};
const requestBody = updateRequest;
const httpMethod = 'PATCH';
const contentType = 'application/json; charset=utf-8';
// Create current date in HTTP date format
const currentDate = new Date().toUTCString();
const signature = generateLuigisBoxDigest(
YOUR_PRIVATE_KEY, httpMethod, ENDPOINT_PATH,
currentDate, contentType
);
const authorizationHeader =
"ApiAuth " + YOUR_PUBLIC_KEY + ":" + signature;
// Make the HTTP request with Axios
axios({
method: httpMethod,
url: LUIGISBOX_HOST + ENDPOINT_PATH,
headers: {
'Content-Type': contentType,
'Date': currentDate,
'Authorization': authorizationHeader
},
data: requestBody
})
.then(response => console.log(response.data))
.catch(error => console.error(error.response.data));
Check Job Status
After a successful PATCH
request, the API will respond with a status_url
. You must then make a GET
request to this URL to check the status of the background job. You may need to poll this endpoint until the status
field changes from "in_progress"
to "complete"
.
Status Check Request:
GET https://live.luigisbox.com/v1/update_by_query?job_id=12345
The response will show the current status of your bulk update operation.
Initial PATCH Response
{
"status_url": "/v1/update_by_query?job_id=12345"
}
Job Completion Response
Once the job is finished, the GET
request to the status URL will return a summary of the results, including the number of successful updates and any failures.
Response Fields
Field | Type | Description |
---|---|---|
tracker_id |
String | Your tracker identifier |
status |
String | Job status: "in_progress", "complete", or "failed" |
updates_count |
Integer | Number of objects successfully updated |
failures_count |
Integer | Number of objects that failed to update |
failures |
Object | Details about any failures (empty if none) |
Complete Job Response
{
"tracker_id": "YOUR-TRACKER-ID",
"status": "complete",
"updates_count": 5,
"failures_count": 0,
"failures": {}
}
Error Handling
HTTP Status | Description |
---|---|
400 Bad Request | The request structure is invalid, JSON is malformed, or some objects failed validation. Check response body for details. |
403 Forbidden | The request is not allowed for your site in Luigi's Box. |
405 Method Not Allowed | The request method is not supported for the specified resource. |
413 Payload Too Large | The request exceeds 5 MB (10 MB if compressed). Reduce batch size or enable compression. |
Example: Job Failure Response
{
"tracker_id": "YOUR-TRACKER-ID",
"status": "complete",
"updates_count": 4,
"failures_count": 1,
"failures": {
"/products/1": {
"type": "data_schema_mismatch",
"reason": "failed to parse [attributes.price]",
"caused_by": {
"type": "number_format_exception",
"reason": "For input string: \"wrong sale price\""
}
}
}
}
Related Endpoints
- Content Update - Create or replace objects
- Partial Content Update - Update specific fields only
- Content Removal - Delete objects from index