API principles
Luigi's Box provides several HTTP-based APIs. In general, the APIs that access sensitive data (such as analytics export) or manipulate data in your catalog require HMAC authentication. The APIs that serve the search and recommender data are designed to be called from the browser, provide no sensitive data are callable without authentication.
All APIs are throttled (you cannot call them more often that the throttling limits allow you).
Authentication
Most of the available endpoints use HMAC authentication to restrict access. To use the API you'll need a
-
public key — this is a
tracker_id
assigned to your site in Luigi's Box search analytics. You can see thetracker_id
in every URL in Luigi's Box application once you are logged in. - private key — you can find your private key in the Integration settings section in Luigi's Box application.
If you need help contact our support.
Our API expects you to include these HTTP headers:
HTTP Header | Comment |
---|---|
Content-Type |
e.g., application/json; charset=utf-8 Standard HTTP header. Some endpoints allow you to POST JSON data, while some are purely GET-based |
Authorization |
e.g., ApiAuth 1292-9381:sd73hdh881gfop228 The general format is client tracker_id:digest . The client part is not used, it's best to provide your application name, or a generic name, such as "ApiAuth". You must compute the digest using the method described below. |
date |
e.g., Thu, 29 Jun 2017 12:11:16 GMT Request date in the HTTP format. Note that this is cross-validated on our servers and if your clock is very inaccurate, your requests will be rejected. We tolerate ±5 second inaccuracies. You will be including this timestamp in the digest computation, so what this means in plain terms is that you cannot cache the digest and must recompute it for each request. |
Content-Encoding optional
|
e.g., gzip Optional HTTP header. Content updates endpoint allows you to use gzip or deflate request payload compression methods to send large payloads effectively. |
Digest computation
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)).strip
end
date = Time.now.httpdate
digest("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)
You must use HMAC SHA256 to compute the digest and it must be Base64 encoded.
Payload to be signed is computed as a newline-d concatenation of
- HTTP request method - e.g.,
GET
- Content-type header - e.g.,
application/json; charset=utf-8
- Date (in HTTP-date format) - the same value you are sending in
date
header - path (without query string) - e.g.
/filters
Make sure that you are using the same values for digest computation as for the
actual request. For example, if you compute digest from Content-Type:
application/json; charset=utf-8
, make sure you send the request with the exact same
Content-Type (and not e.g. Content-Type: application/json
).
Most programming languages provide crypto libraries which let you compute the HMAC digest with minimal effort. When the particular endpoint requires HMAC, we provide examples in several languages in the right column in its documentation.
The pseudocode for HMAC computation is:
signature = [request_method, content_type, timestamp, request_path].join("\n")
digest = base64encode(hmacsha256(signature, your_private_key))
Look for examples in the right column. You can find examples for other languages online, however, those were not tested by us. See the following external links for more examples:
Server will return HTTP 401 Not Authorized
if your authentication fails. If
this happens, look inside the response body. We include a diagnostics output
which will tell you what was wrong with your request.
Content-type
header. Some clients (Chrome browser for example) are manipulating this value even if it was explicitly set (changing letter case from application/json;charset=utf-8
to application/json;charset=UTF-8
for no apparent reason)
Error handling
The API may return errors under specific circumstances. The table below summarizes some of the errors that you may encounter and a recommended way to handle them.
HTTP Status | Reason | Transient | Recommended handlingg |
---|---|---|---|
408 Timeout | The request is taking too long to process. This may mean a temporary overload on our side. | Yes | Since this is a transient error, you may safely retry the request. For autocomplete requests, retrying is not necessary, since the user will keep typing and a new request will be made naturally. For search and recommender, retry the request once. Avoid retrying the request more than once. |
429 Too Many Requests | You are breaching one of the throttling limits. | No | The default throttling limits are generous and you should not encounter this error. If you are seeing this error in production, for non-realtime APIs (such as Content Updates), obey the Retry-After header and retry the request. For realtime APIs (autocomplete, search, recommender), fail the request, raise an alarm and get in touch with us to investigate the reason for throttling. |
500 Internal Server Error | This is a bug on our side. | No | Fail the request, raise an alarm and get in touch with us to investigate the problem. |
502 Bad Gateway | Seeing this error may mean an operational incident on our side. | No | Fail the request, raise an alarm and check luigisboxstatus.com to see if there is an active incident and find out more details. |
503 Service Unavailable | Seeing this error may mean an operational incident on our side. | No | Fail the request, raise an alarm and check luigisboxstatus.com to see if there is an active incident and find out more details. |
Development mode
While you are developing the integration, your API connection may be put into a development mode, where a small percentage of requests will return deliberate errors. The goal of the development mode is to expose you to errors that you will normally not see and help you write an error handling code.
These errors will be clearly marked as the development mode errors. Note that we will not enable development mode without notifying you, so unless you received an explicit notification, your API connection is not in the development mode.
To disable the development mode, contact support@luigisbox.com to switch your API connection into the production mode.
Throttling
There are different limits for requests per time period in place for endpoints.
Content Updates
- limit of 500 request per 1 minute and 5 concurrent requests for
tracker_id
- see more about Content Updates API
Autocomplete
- limit of 800 request per 1 minute for
tracker_id
- limit of 30 request per 5 seconds for same
IP address
- see more about Autocomplete
Search
- limit of 350 request per 1 minute for same
tracker_id
- limit of 30 request per 5 seconds for same
IP address
- see more about Search
If you exceed any of the limits, you'll get a 429 Too Many Requests
response for subsequent requests. Check the Retry-After
header if present to see how many seconds to wait before retrying the request.
If you find these limits insufficient for your site, please contact us and we can put exceptions for higher limits in place.