API Principles
Luigi’s Box provides HTTP-based APIs for two main purposes: serving search results to users (Public) and managing your data/configuration (Private).
Service reference and endpoints
Section titled “Service reference and endpoints”Public API (unauthenticated)
Section titled “Public API (unauthenticated)”Auth: Requires only your public tracker_id.
Usage: Safe to call from Frontend (Browsers, Mobile Apps).
| Category | Method | Base URL | Endpoint | Description |
|---|---|---|---|---|
| Search | GET/POST | live.luigisbox.com | /search | Full-text search & facets. |
| Autocomplete | GET | live.luigisbox.com | /autocomplete/v2 | Prefix-based suggestions. |
| Recommender | POST | live.luigisbox.com | /v1/recommend | Product recommendations. |
| Shopping Assistant | POST | live.luigisbox.com | /v1/assistant | AI-powered structured dialogue that helps customers discover products. |
| Top items | GET | live.luigisbox.com | /v1/top_items | Most popular items. |
| Trending queries | GET | live.luigisbox.com | /v2/trending_queries | Trending search terms. |
| Analytics | POST | api.luigisbox.com | /v1 | Events API. Ingest user interactions. |
Private API (authenticated)
Section titled “Private API (authenticated)”Auth: Requires HMAC Signature using your Private Key. Usage: Backend Server ONLY.
Indexing hub (content management)
Section titled “Indexing hub (content management)”Manage your product catalog.
| Method | Base URL | Endpoint | Description |
|---|---|---|---|
POST | live.luigisbox.com | /v1/content | Create/Replace. Add new products. |
PATCH | live.luigisbox.com | /v1/content | Partial Update. Update specific fields. |
DELETE | live.luigisbox.com | /v1/content | Delete. Remove products. |
PATCH | live.luigisbox.com | /v1/update_by_query | Batch Update. Update items matching a query. |
GET | live.luigisbox.com | /v1/content_export | Export. Download your current index data. |
Recommender customization
Section titled “Recommender customization”Manage pinning rules and batch recommendations.
| Method | Base URL | Endpoint | Description |
|---|---|---|---|
POST | live.luigisbox.com | /v1/recommender/pin/{ID}/scopes | Define pinning scopes (rules). |
GET | live.luigisbox.com | /v1/recommender/pin/{ID}/summary | Get pinning configuration summary. |
POST | live.luigisbox.com | /v1/recommender/batching/{ID}/users | Trigger batch recommendation generation for users. |
* | live.luigisbox.com | /v1/recommender/pin/* | Various CRUD endpoints for managing pins. |
Authentication (HMAC)
Section titled “Authentication (HMAC)”Only the Private API endpoints require a custom HMAC-SHA256 signature.
Credentials
Section titled “Credentials”You will need your API credentials found in Luigi’s Box App > Settings > Integration Settings:
- Tracker_ID (Public Key): Identifies your account.
- Secret Key (Private Key): Used to sign requests.
The signing flow
Section titled “The signing flow”To authenticate, you must construct a specific “String to Sign,” hash it, and attach it as a header.
flowchart TD
A["HTTP method"]
B["Content-Type"]
C["Date header"]
D["Path"]
E["Canonical string"]
F{"Hash function"}
G["Raw bytes"]
H["Final signature"]
I["Authorization header"]
J["API server"]
Key["Private secret"]
A --> E
B --> E
C --> E
D --> E
E -->|HMAC SHA-256| F
Key -.-> F
F -->|Binary output| G
G -->|Base64 encode| H
H -->|Format string| I
I -->|Send| JDetailed specification
Section titled “Detailed specification”Step 1: Required headers
Section titled “Step 1: Required headers”Every authenticated request must include these headers. The values used here must match the values used in the signature generation byte-for-byte.
| Header | Description |
|---|---|
Date | The current timestamp in HTTP Date format (e.g., Thu, 29 Jun 2017 12:11:16 GMT). Note: Our server clock tolerance is ±5 seconds. You must regenerate the date and signature for every request. |
Content-Type | Describes your payload (e.g., application/json; charset=utf-8). |
Authorization | The authentication string. Format: AppName [tracker_id]:[signature] |
Step 2: Canonical string construction
Section titled “Step 2: Canonical string construction”Concatenate the following four strings, separated by a newline character (\n).
- HTTP Verb: Uppercase (e.g.,
POST,PUT,DELETE). - Content-Type: Exactly as sent in the header (e.g.,
application/json; charset=utf-8). - Date: Exactly as sent in the header.
- Resource Path: The absolute path excluding the query string (e.g.,
/v1/content, not/v1/content?foo=bar).
Step 3: Signature calculation
Section titled “Step 3: Signature calculation”- Compute the HMAC-SHA256 digest of the Canonical String using your Secret Key.
- Encode the binary digest into a Base64 string.
Example implementations
Section titled “Example implementations”require 'time'require 'openssl'require 'base64'
def digest(key, method, endpoint, date) content_type = 'application/json; charset=utf-8'
data = "#{method}\n#{content_type}\n#{date}\n#{endpoint}"
dg = OpenSSL::Digest.new('sha256') Base64.strict_encode64(OpenSSL::HMAC.digest(dg, key, data)).stripend
date = Time.now.httpdatedigest("secret", "POST", "/v1/content", date)<?php
function digest($key, $method, $endpoint, $date) { $content_type = 'application/json; charset=utf-8';
$data = "{$method}\n{$content_type}\n{$date}\n{$endpoint}";
$signature = trim(base64_encode(hash_hmac('sha256', $data, $key, true)));
return $signature;}
$date = gmdate('D, d M Y H:i:s T');digest("secret", "POST", "/v1/content", $date);// This configuration and code work with the Postman tool// https://www.getpostman.com///// Start by creating the required HTTP headers in the "Headers" tab// - Content-Type: application/json; charset=utf-8// - Authorization: {{authorization}}// - date: {{date}}//// The {{variable}} is a postman variable syntax. It will be replaced// by values precomputed by the following pre-request script.
var privateKey = "your-secret";var publicKey = "your-tracker-id";var contentType = "application/json; charset=utf-8";
var requestUri = request.url.replace(/^.*\/\/[^\/]+/, "").replace(/\?.*$/, "");var timestamp = new Date().toUTCString();var signature = [request.method, contentType, timestamp, requestUri].join("\n");
var encryptedSignature = CryptoJS.HmacSHA256(signature, privateKey).toString( CryptoJS.enc.Base64,);
pm.request.headers.add({ key: "Content-Type", value: contentType,});pm.request.headers.add({ key: "Authorization", value: "ApiAuth " + publicKey + ":" + encryptedSignature,});pm.request.headers.add({ key: "Date", value: timestamp,});#!/bin/bash
digest() { KEY=$1 METHOD=$2 CONTENT_TYPE="application/json; charset=utf-8" ENDPOINT=$3 DATE=$4
DATA="$METHOD\n$CONTENT_TYPE\n$DATE\n$ENDPOINT"
printf "$DATA" | openssl dgst -sha256 -hmac "$KEY" -binary | base64}
date=$(env LC_ALL=en_US date -u '+%a, %d %b %Y %H:%M:%S GMT')echo $(digest "secret" "GET" "/v1/content" "$date")User identification
Section titled “User identification”Luigi’s Box relies on a unified user identity model across analytics and live APIs (Search, Autocomplete, Product Listing, and Recommender) to deliver effective personalization.
The platform tracks two distinct identity concepts:
- Anonymous / Device user: A stable anonymous identifier for the current browser, device, or app install. It is not tied to a logged-in account. For integrations using the DataLayer collector, this value is typically available as the
_lbcookie value. For fully custom API integrations, your application must generate, persist, and reuse this value across analytics events and live API requests. - Authenticated / Logged-in user: A stable identifier of the logged-in user in your application. This should be your own durable account or customer identifier, not a temporary request identifier.
Because the Events API (analytics) and Live APIs (Search, Autocomplete, etc.) refer to these concepts using different parameter names, it is essential to map them correctly.
Events API (Analytics)
Section titled “Events API (Analytics)”When sending events to the Events API (api.luigisbox.com), you use the following parameters:
client_id: Carries the Anonymous / Device user identifier. You must always includeclient_id, and it must stay the same for the same browser or device, even after the user logs in or logs out.customer_id: Carries the Authenticated / Logged-in user identifier. Send this alongsideclient_idwhen the user is logged in. Do not overwriteclient_idwith the customer identifier. See the Events API common parameters and Identity rules.consent_granted: Set this totruewhen the user has accepted personalization cookies or equivalent consent. This is a consent flag, not an identifier, but it tells the Events API that the event can be used for personalization.
Live APIs
Section titled “Live APIs”When querying live services (live.luigisbox.com) like Search, Autocomplete, Product Listing, or Recommender, the identity parameters map slightly differently to support personalization:
client_id: Passed to maintain the Anonymous / Device user identity. This should match theclient_idsent in analytics. For integrations using the DataLayer collector, this value is typically available as the_lbcookie value.user_id: Carries the identifier Luigi’s Box should personalize on for that request. It is not a third identity system. Its value must be one of the two identities defined above:- For anonymous users, set this to the
client_id. - For authenticated users, set this to the
customer_id. - Omit
user_identirely for non-personalized requests.
- For anonymous users, set this to the
Mapping reference
Section titled “Mapping reference”The following table summarizes the parameter mapping across the two API systems based on the user’s state:
In this table, Personalized describes the Live API behavior, while Consent Granted describes the Events API consent state that allows personalization.
| User state | Events API | Live APIs |
|---|---|---|
| Anonymous (Personalized / Consent Granted) | client_id = device ID(e.g., browser-123) consent_granted = true | client_id = device IDuser_id = device ID |
| Authenticated (Personalized / Consent Granted) | client_id = device IDcustomer_id = account ID (e.g., user-42)consent_granted = true | client_id = device IDuser_id = account ID |
| Non-personalized | client_id = device IDcustomer_id = account ID (if authenticated)consent_granted omitted or false | client_id = device IDuser_id omitted |
Rate limiting (throttling)
Section titled “Rate limiting (throttling)”We enforce rate limits to ensure stability. Limits are applied per Tracker ID (Account level) and per IP Address.
| Endpoint | Account Limit | IP Limit |
|---|---|---|
| Content Updates | 500 req / min, 5 concurrent reqs | - |
| Autocomplete | 800 req / min | 30 req / 5s |
| Search | 350 req / min | 30 req / 5s |
| Recommender | 60 req / 5s | 30 req / 5s |
Handle 429 errors
Section titled “Handle 429 errors”If you exceed a limit, you will receive a 429 Too Many Requests status. Your application must handle this gracefully.
We provide a Retry-After header indicating how many seconds you must wait.
sequenceDiagram participant Client participant API Client->>API: POST content request API-->>Client: 429 Retry-After 5 seconds Note over Client: Wait 5 seconds Client->>API: Retry content request API-->>Client: 200 OK
Recommended Strategy:
- Realtime APIs (Search, Autocomplete): Do not retry immediately. Discard the request.
- Batch APIs (Content Updates): Use an exponential backoff strategy (wait
Retry-After, then retry).
Error handling
Section titled “Error handling”You should design your integration to be resilient to network failures.
| Code | Reason | Type | Action |
|---|---|---|---|
| 401 | Auth Failed | Fatal | Check your Signature generation. The response body contains the string we expected vs. what you sent. |
| 408 | Timeout | Transient | Safe to retry once. |
| 429 | Throttling | Transient | Wait and retry (see above). |
| 500 | Server Error | Fatal | Log the error and contact support. |
| 502 | Bad Gateway | Transient | Usually a deploy or network blip. Retry after 1s. |
| 503 | Service Unavailable | Transient | Check luigisboxstatus.com. |
Development mode
Section titled “Development mode”To ensure your integration is robust, we may enable Development Mode on your account during the integration phase.
What it does:
- A small percentage of your requests will deliberately fail with various error codes (408, 429, 500).
- These errors are marked in the JSON body as
development_mode: true.
Why: This forces your developers to write proper error handling and retry logic before you go to production.
Was this page helpful?
Thanks.