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 the tracker_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-Encodingoptional 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.

If authentication fails and you are sure that your code works as expected, double check value of actually sent 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.
Note that we are constantly monitoring all types of errors for all customer and are taking proactive measures to respond to any unusual error activity.

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
  • 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.