Reporting APIs

Some of the analytics & reporting APIs support segmentation and date filtering. To apply them, use the following query parameters:

  • segment_id - visit the Luigis Analytics Dashboard and switch to desired segmentation. Copy segmentation id from the current URL (a value of parameter apply_segment). For example, for this URL https://app.luigisbox.com/sites/XXX-XXX/searches?apply_segment=999, value of segment_id is 999.
  • from - if given, only sessions starting after midnight of date from will be taken into account. Required format: YYYY-MM-DD (e.g., 2021-12-21)
  • to - if given, only sessions starting before the end of date to will be taken into account. Required format: YYYY-MM-DD (e.g., 2021-12-30)
If you want to filter by date, both from and to must be provided. E.g., if you want data for 30th December 2021, from should be set to 2021-12-30 and to should be set to 2021-12-30, as well.
Please note that a maximum window size given by from/to parameters is capped to 90 days.

Breakdown

GET https://analytics.luigisbox.com/breakdown

The breakdown endpoint returns the high-level search KPIs, the same KPIs that you can see in the Luigi's Box dashboard funnel. All of the attributes in the response should be interpreted as percentages in the interval <0, 1>, e.g. 0.23 means 23%. In attributes sessions and searches you can find absolute numbers that are used to calculate these percentages.

This endpoint requires HMAC authentication. Refer to the Authentication section for details.

Query Parameters

Parameter Description
from ISO 8601 formatted start date of the interval in which the KPIs will be computed.
to ISO 8601 formatted end date of the interval in which the KPIs will be computed.
days Number of past days relative to the beginning of the current day to compute the KPIs from. E.g., setting days=0 will set from to the beginning of the current day and to to the end of the current day. Setting days=1 will set from to the beginning of yesterday and to to the end of the current day. Note that days parameter is mutually exclusive with the from and to parameter.

Request Headers

Consider sending request header of Accept-Encoding as well with values for supported encoding methods of your HTTP client, e.g. gzip or br, gzip, deflate for multiple supported methods. Encodings make the response from Breakdown endpoint considerably smaller and thus faster to transfer.

Sample request

require 'faraday'
require 'faraday_middleware'
require 'json'
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


public_key = "<your-public-key>"
private_key = "<your-private-key>"

date = Time.now.httpdate

connection = Faraday.new(url: 'https://analytics.luigisbox.com') do |conn|
  conn.use FaradayMiddleware::Gzip
end

response = connection.get("/breakdown") do |req|
  req.headers['Content-Type'] = "application/json; charset=utf-8"
  req.headers['Date'] = date
  req.headers['Authorization'] = "faraday #{public_key}:#{digest(private_key, "GET", "/breakdown", date)}"
end

if response.success?
  puts JSON.pretty_generate(JSON.parse(response.body))
else
  puts "Error, HTTP status #{response.status}"
  puts response.body
end

#!/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
}


public_key="<your-public-key>"
private_key="<your-private-key>"

date=$(env LC_ALL=en_US date -u '+%a, %d %b %Y %H:%M:%S GMT')
signature=$(digest "$private_key" "GET" "/breakdown" "$date")

curl -i -XGET --compressed\
  -H "Date: $date" \
  -H "Content-Type: application/json; charset=utf-8" \
  -H "Authorization: curl $public_key:$signature" \
  "https://analytics.luigisbox.com/breakdown"

<?php

// Using Guzzle (http://guzzle.readthedocs.io/en/latest/overview.html#installation)
require 'GuzzleHttp/autoload.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');

$public_key = "<your-public-key>";
$private_key = "<your-private-key>";

$signature = digest($private_key, 'GET', '/breakdown', $date);

$client = new GuzzleHttp\Client();
$res = $client->request('GET', "https://analytics.luigisbox.com/breakdown", [
  'headers' => [
    'Accept-Encoding' => 'gzip, deflate',
    'Content-Type' => 'application/json; charset=utf-8',
    'Date' => $date,
    'Authorization' => "guzzle {$public_key}:{$signature}",
  ],
]);

echo $res->getStatusCode();
echo $res->getBody();

// This configuration and code work with the Postman tool
// https://www.getpostman.com/
//
// Start by creating the required HTTP headers in the "Headers" tab
//  - Accept-Encoding: gzip, deflate
//  - 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 requestPath = '/breakdown'
var timestamp = new Date().toUTCString();
var signature = ['GET', "application/json; charset=utf-8", timestamp, requestPath].join("\n");

var encryptedSignature = CryptoJS.HmacSHA256(signature, privateKey).toString(CryptoJS.enc.Base64);

postman.setGlobalVariable("authorization", "ApiAuth " + publicKey + ":" + encryptedSignature);
postman.setGlobalVariable("date", timestamp);

// This endpoint requires no body

The above command returns JSON structured like this.

{
  "not_used_search_percent": 0.23,
  "not_used_search_converted_percent": 0.20,
  "not_used_search_not_converted_percent": 0.80,
  "used_search_percent": 0.77,
  "search_converted_percent": 0.21,
  "search_not_converted_percent": 0.79,
  "no_results_searches_percent": 0.05,
  "no_click_searches_percent": 0.3,
  "clicked_searches_percent": 0.65,
  "sessions": {
    "total_sessions": 1568,
    "not_used_search_sessions": 360,
    "not_used_search_converted_sessions": 72,
    "not_used_search_not_converted_sessions": 288,
    "used_search_sessions": 1207,
    "search_converted_sessions": 253,
    "search_not_converted_sessions": 953
  },
  "searches": {
    "total_searches": 1500,
    "no_results_searches": 75,
    "no_click_searches": 450,
    "clicked_searches": 975
  }
}

Filters

GET https://analytics.luigisbox.com/filters?pair[]=categories:Laptops

Filters endpoint gives you information about filters used for searching. See the analytics documentation section on Filters for more information.

Filter is a pair of a name and a value. Both the name and value are taken from search analytics data.

When you invoke the endpoint without any parameters you will get an overview of all filters used, with basic statistical information - how many users used the filter and what was its conversion rate. The API returns the filter information in a hierarchy - filter name first, and then its values nested underneath. See the example on the right.

Filters are ordered by users_count attribute on the first level, and then by the filter conversion rate on the values level.

You can also pass an optional pair[] parameter to get information about filters used in conjunction with the filter sent in pair[] parameter. This can be understood as: "What other filters were used together with this filter?". For example, to answer the question: "What were the most used filters in "Laptops" category?", you can make a following request

The recommended way to use this API is to first invoke the endpoint without any parameters to get a list of all filter pairs. Each pair also contains a HATEOAS self link with prepopulated pair[] parameter. We recommend that you use this link, instead of trying to build it yourself.

This endpoint requires HMAC authentication. Refer to the Authentication section for details.
This endpoint supports segmentation and date filtering. Refer to the Analytics APIs section for details.

Query Parameters

Parameter Description
pair[] Filter pair in the name:value format to limit filter usage data to. Repeat this parameter multiple times to limit data to several filters at once.

Sample request

require 'faraday'
require 'faraday_middleware'
require 'json'
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


public_key = "<your-public-key>"
private_key = "<your-private-key>"

date = Time.now.httpdate

connection = Faraday.new(url: 'https://analytics.luigisbox.com') do |conn|
  conn.use FaradayMiddleware::Gzip
end

response = connection.get("/filters") do |req|
  req.headers['Content-Type'] = "application/json; charset=utf-8"
  req.headers['Date'] = date
  req.headers['Authorization'] = "faraday #{public_key}:#{digest(private_key, "GET", "/filters", date)}"
end

if response.success?
  puts JSON.pretty_generate(JSON.parse(response.body))
else
  puts "Error, HTTP status #{response.status}"
  puts response.body
end

#!/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
}


public_key="<your-public-key>"
private_key="<your-private-key>"

date=$(env LC_ALL=en_US date -u '+%a, %d %b %Y %H:%M:%S GMT')
signature=$(digest "$private_key" "GET" "/filters" "$date")

curl -i -XGET --compressed\
  -H "Date: $date" \
  -H "Content-Type: application/json; charset=utf-8" \
  -H "Authorization: curl $public_key:$signature" \
  "https://analytics.luigisbox.com/filters"

<?php

// Using Guzzle (http://guzzle.readthedocs.io/en/latest/overview.html#installation)
require 'GuzzleHttp/autoload.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');

$public_key = "<your-public-key>";
$private_key = "<your-private-key>";

$signature = digest($private_key, 'GET', '/filters', $date);

$client = new GuzzleHttp\Client();
$res = $client->request('GET', "https://analytics.luigisbox.com/filters", [
  'headers' => [
    'Accept-Encoding' => 'gzip, deflate',
    'Content-Type' => 'application/json; charset=utf-8',
    'Date' => $date,
    'Authorization' => "guzzle {$public_key}:{$signature}",
  ],
]);

echo $res->getStatusCode();
echo $res->getBody();

// This configuration and code work with the Postman tool
// https://www.getpostman.com/
//
// Start by creating the required HTTP headers in the "Headers" tab
//  - Accept-Encoding: gzip, deflate
//  - 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 requestPath = '/filters'
var timestamp = new Date().toUTCString();
var signature = ['GET', "application/json; charset=utf-8", timestamp, requestPath].join("\n");

var encryptedSignature = CryptoJS.HmacSHA256(signature, privateKey).toString(CryptoJS.enc.Base64);

postman.setGlobalVariable("authorization", "ApiAuth " + publicKey + ":" + encryptedSignature);
postman.setGlobalVariable("date", timestamp);

// This endpoint requires no body

The above command returns JSON structured like this.

[
  {
    "name": "Categories",
    "users_count": 11793,
    "conversion_rate": 2.74,
    "values": [
      {
        "name": "Laptop",
        "users_count": 644,
        "conversion_rate": 8.58,
        "links": [
          {
            "rel": "self",
            "href": "https://analytics.luigisbox.com/filters?pair[]=Categories:Laptop"
          }
        ]
      },
      {
        "name": "Desktop",
        "users_count": 1595,
        "conversion_rate": 5.45,
        "links": [
          {
            "rel": "self",
            "href": "https://analytics.luigisbox.com/filters?pair[]=Categories:Desktop"
          }
        ]
      }
    ]
  },
  {
    "name": "In Stock",
    "users_count": 11793,
    "conversion_rate": 2.74,
    "values": [
      {
        "name": "Yes",
        "users_count": 1192,
        "conversion_rate": 4.41,
        "links": [
          {
            "rel": "self",
            "href": "https://analytics.luigisbox.com/filters?pair[]=In+Stock:Yes"
          }
        ]
      }
    ]
  }
]

You can simply follow the self href to get details on a specific filter pair.

Frequent queries

GET https://analytics.luigisbox.com/frequent_queries

The frequent queries endpoint gives you a list of your top queries, as we tracked them in Luigi's Box. All queries are lowercased and any non-ASCII characters are converted their ASCII approximation.

This endpoint requires HMAC authentication. Refer to the Authentication section for details.
This endpoint supports segmentation and date filtering. Refer to the Analytics APIs section for details.

Request Headers

Consider sending request header of Accept-Encoding as well with values for supported encoding methods of your HTTP client, e.g. gzip or br, gzip, deflate for multiple supported methods. Encodings make the response from Frequent queries endpoint considerably smaller and thus faster to transfer.

Sample request

require 'faraday'
require 'faraday_middleware'
require 'json'
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


public_key = "<your-public-key>"
private_key = "<your-private-key>"

date = Time.now.httpdate

connection = Faraday.new(url: 'https://analytics.luigisbox.com') do |conn|
  conn.use FaradayMiddleware::Gzip
end

response = connection.get("/frequent_queries") do |req|
  req.headers['Content-Type'] = "application/json; charset=utf-8"
  req.headers['Date'] = date
  req.headers['Authorization'] = "faraday #{public_key}:#{digest(private_key, "GET", "/frequent_queries", date)}"
end

if response.success?
  puts JSON.pretty_generate(JSON.parse(response.body))
else
  puts "Error, HTTP status #{response.status}"
  puts response.body
end

#!/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
}


public_key="<your-public-key>"
private_key="<your-private-key>"

date=$(env LC_ALL=en_US date -u '+%a, %d %b %Y %H:%M:%S GMT')
signature=$(digest "$private_key" "GET" "/frequent_queries" "$date")

curl -i -XGET --compressed\
  -H "Date: $date" \
  -H "Content-Type: application/json; charset=utf-8" \
  -H "Authorization: curl $public_key:$signature" \
  "https://analytics.luigisbox.com/frequent_queries"

<?php

// Using Guzzle (http://guzzle.readthedocs.io/en/latest/overview.html#installation)
require 'GuzzleHttp/autoload.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');

$public_key = "<your-public-key>";
$private_key = "<your-private-key>";

$signature = digest($private_key, 'GET', '/frequent_queries', $date);

$client = new GuzzleHttp\Client();
$res = $client->request('GET', "https://analytics.luigisbox.com/frequent_queries", [
  'headers' => [
    'Accept-Encoding' => 'gzip, deflate',
    'Content-Type' => 'application/json; charset=utf-8',
    'Date' => $date,
    'Authorization' => "guzzle {$public_key}:{$signature}",
  ],
]);

echo $res->getStatusCode();
echo $res->getBody();

// This configuration and code work with the Postman tool
// https://www.getpostman.com/
//
// Start by creating the required HTTP headers in the "Headers" tab
//  - Accept-Encoding: gzip, deflate
//  - 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 requestPath = '/frequent_queries'
var timestamp = new Date().toUTCString();
var signature = ['GET', "application/json; charset=utf-8", timestamp, requestPath].join("\n");

var encryptedSignature = CryptoJS.HmacSHA256(signature, privateKey).toString(CryptoJS.enc.Base64);

postman.setGlobalVariable("authorization", "ApiAuth " + publicKey + ":" + encryptedSignature);
postman.setGlobalVariable("date", timestamp);

// This endpoint requires no body

The above command returns JSON structured like this.

[
  {
    "query": "query x",
    "searches_count": 5917,
    "links": [
      {
        "rel": "self",
        "href": "https://analytics.luigisbox.com/query_detail?q=x"
      }
    ]
  },
  {
    "query": "query y",
    "searches_count": 1475,
    "links": [
      {
        "rel": "self",
        "href": "https://analytics.luigisbox.com/query_detail?q=y"
      }
    ]
  },
  {
    "query": "query z",
    "searches_count": 1127,
    "links": [
      {
        "rel": "self",
        "href": "https://analytics.luigisbox.com/query_detail?q=z"
      }
    ]
  }
]

You can simply follow the self href to get details on a specific query.

No results queries

GET https://analytics.luigisbox.com/no_results_queries

The no results queries endpoint gives you a list of queries for which we tracked a "no-results" response in last 30 days. All queries are lowercased and any non-ASCII characters are converted their ASCII approximation.

This endpoint requires HMAC authentication. Refer to the Authentication section for details.
This endpoint supports segmentation and date filtering. Refer to the Analytics APIs section for details.

Request Headers

Consider sending request header of Accept-Encoding as well with values for supported encoding methods of your HTTP client, e.g. gzip or br, gzip, deflate for multiple supported methods. Encodings make the response from No results queries endpoint considerably smaller and thus faster to transfer.

Sample request

require 'faraday'
require 'faraday_middleware'
require 'json'
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


public_key = "<your-public-key>"
private_key = "<your-private-key>"

date = Time.now.httpdate

connection = Faraday.new(url: 'https://analytics.luigisbox.com') do |conn|
  conn.use FaradayMiddleware::Gzip
end

response = connection.get("/no_results_queries") do |req|
  req.headers['Content-Type'] = "application/json; charset=utf-8"
  req.headers['Date'] = date
  req.headers['Authorization'] = "faraday #{public_key}:#{digest(private_key, "GET", "/no_results_queries", date)}"
end

if response.success?
  puts JSON.pretty_generate(JSON.parse(response.body))
else
  puts "Error, HTTP status #{response.status}"
  puts response.body
end

#!/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
}


public_key="<your-public-key>"
private_key="<your-private-key>"

date=$(env LC_ALL=en_US date -u '+%a, %d %b %Y %H:%M:%S GMT')
signature=$(digest "$private_key" "GET" "/no_results_queries" "$date")

curl -i -XGET --compressed\
  -H "Date: $date" \
  -H "Content-Type: application/json; charset=utf-8" \
  -H "Authorization: curl $public_key:$signature" \
  "https://analytics.luigisbox.com/no_results_queries"

<?php

// Using Guzzle (http://guzzle.readthedocs.io/en/latest/overview.html#installation)
require 'GuzzleHttp/autoload.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');

$public_key = "<your-public-key>";
$private_key = "<your-private-key>";

$signature = digest($private_key, 'GET', '/no_results_queries', $date);

$client = new GuzzleHttp\Client();
$res = $client->request('GET', "https://analytics.luigisbox.com/no_results_queries", [
  'headers' => [
    'Accept-Encoding' => 'gzip, deflate',
    'Content-Type' => 'application/json; charset=utf-8',
    'Date' => $date,
    'Authorization' => "guzzle {$public_key}:{$signature}",
  ],
]);

echo $res->getStatusCode();
echo $res->getBody();

// This configuration and code work with the Postman tool
// https://www.getpostman.com/
//
// Start by creating the required HTTP headers in the "Headers" tab
//  - Accept-Encoding: gzip, deflate
//  - 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 requestPath = '/no_results_queries'
var timestamp = new Date().toUTCString();
var signature = ['GET', "application/json; charset=utf-8", timestamp, requestPath].join("\n");

var encryptedSignature = CryptoJS.HmacSHA256(signature, privateKey).toString(CryptoJS.enc.Base64);

postman.setGlobalVariable("authorization", "ApiAuth " + publicKey + ":" + encryptedSignature);
postman.setGlobalVariable("date", timestamp);

// This endpoint requires no body

The above command returns JSON structured like this.

[
  {
    "query": "query x",
    "searches_count": 5917,
    "links": [
      {
       "rel": "self",
       "href": "https://app.luigisbox.com/sites/23-7723/queries?in=Search+Results&q=query+x&show=noresults"
      }
    ]
  },
  {
    "query": "query y",
    "searches_count": 1475,
    "links": [
      {
        "rel": "self",
        "href": "https://app.luigisbox.com/sites/23-7723/queries?in=Search+Results&q=query+y&show=noresults"
      }
    ]
  },
  {
    "query": "query z",
    "searches_count": 1127,
    "links": [
      {
        "rel": "self",
        "href": "https://app.luigisbox.com/sites/23-7723/queries?in=Search+Results&q=query+z&show=noresults"
      }
    ]
  }
]

The self href leads to query detail page in Luigi's Box app.

Query Detail

GET https://analytics.luigisbox.com/query_detail?q=term

The query detail endpoint gives you top hits (in terms of CTR and conversions) of the chosen query.

This endpoint requires HMAC authentication. Refer to the Authentication section for details.
This endpoint supports segmentation and date filtering. Refer to the Analytics APIs section for details.

Request Headers

Consider sending request header of Accept-Encoding as well with values for supported encoding methods of your HTTP client, e.g. gzip or br, gzip, deflate for multiple supported methods. Encodings make the response from Query Detail endpoint considerably smaller and thus faster to transfer.

Sample request

require 'faraday'
require 'faraday_middleware'
require 'json'
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


public_key = "<your-public-key>"
private_key = "<your-private-key>"

date = Time.now.httpdate

connection = Faraday.new(url: 'https://analytics.luigisbox.com') do |conn|
  conn.use FaradayMiddleware::Gzip
end

response = connection.get("/query_detail?q=term") do |req|
  req.headers['Content-Type'] = "application/json; charset=utf-8"
  req.headers['Date'] = date
  req.headers['Authorization'] = "faraday #{public_key}:#{digest(private_key, "GET", "/query_detail", date)}"
end

if response.success?
  puts JSON.pretty_generate(JSON.parse(response.body))
else
  puts "Error, HTTP status #{response.status}"
  puts response.body
end

#!/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
}


public_key="<your-public-key>"
private_key="<your-private-key>"

date=$(env LC_ALL=en_US date -u '+%a, %d %b %Y %H:%M:%S GMT')
signature=$(digest "$private_key" "GET" "/query_detail" "$date")

curl -i -XGET --compressed\
  -H "Date: $date" \
  -H "Content-Type: application/json; charset=utf-8" \
  -H "Authorization: curl $public_key:$signature" \
  "https://analytics.luigisbox.com/query_detail?q=term"

<?php

// Using Guzzle (http://guzzle.readthedocs.io/en/latest/overview.html#installation)
require 'GuzzleHttp/autoload.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');

$public_key = "<your-public-key>";
$private_key = "<your-private-key>";

$signature = digest($private_key, 'GET', '/query_detail', $date);

$client = new GuzzleHttp\Client();
$res = $client->request('GET', "https://analytics.luigisbox.com/query_detail?q=term", [
  'headers' => [
    'Accept-Encoding' => 'gzip, deflate',
    'Content-Type' => 'application/json; charset=utf-8',
    'Date' => $date,
    'Authorization' => "guzzle {$public_key}:{$signature}",
  ],
]);

echo $res->getStatusCode();
echo $res->getBody();

// This configuration and code work with the Postman tool
// https://www.getpostman.com/
//
// Start by creating the required HTTP headers in the "Headers" tab
//  - Accept-Encoding: gzip, deflate
//  - 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 requestPath = '/query_detail'
var timestamp = new Date().toUTCString();
var signature = ['GET', "application/json; charset=utf-8", timestamp, requestPath].join("\n");

var encryptedSignature = CryptoJS.HmacSHA256(signature, privateKey).toString(CryptoJS.enc.Base64);

postman.setGlobalVariable("authorization", "ApiAuth " + publicKey + ":" + encryptedSignature);
postman.setGlobalVariable("date", timestamp);

// This endpoint requires no body

The above command returns JSON structured like this.

{
  "with_clicks": [
    {
      "title": "Product X",
      "url": "www.e-shop.com/products/123",
      "clicks": 465
    },
    {
      "title": "Product Y",
      "url": "www.e-shop.com/products/456",
      "clicks": 324
    }
  ],
  "with_conversions": [
    {
      "title": "Product X",
      "url": "www.e-shop.com/products/123",
      "conversions": 29
    },
    {
      "title": "Product Z",
      "url": "www.e-shop.com/products/789",
      "conversions": 16
    }
  ]
}

Query Enhancement

GET https://live.luigisbox.com/rephrase?q=term

The query enhancement endpoint gives you a rephrased query for any given input query in order to avoid no or low converting search results.

This endpoint requires HMAC authentication. Refer to the Authentication section for details.

Request Headers

Consider sending request header of Accept-Encoding as well with values for supported encoding methods of your HTTP client, e.g. gzip or br, gzip, deflate for multiple supported methods. Encodings make the response from Query Enhancement endpoint considerably smaller and thus faster to transfer.

Sample request

require 'faraday'
require 'faraday_middleware'
require 'json'
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


public_key = "<your-public-key>"
private_key = "<your-private-key>"

date = Time.now.httpdate

connection = Faraday.new(url: 'https://analytics.luigisbox.com') do |conn|
  conn.use FaradayMiddleware::Gzip
end

response = connection.get("/rephrase") do |req|
  req.headers['Content-Type'] = "application/json; charset=utf-8"
  req.headers['Date'] = date
  req.headers['Authorization'] = "faraday #{public_key}:#{digest(private_key, "GET", "/rephrase", date)}"
end

if response.success?
  puts JSON.pretty_generate(JSON.parse(response.body))
else
  puts "Error, HTTP status #{response.status}"
  puts response.body
end

#!/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
}


public_key="<your-public-key>"
private_key="<your-private-key>"

date=$(env LC_ALL=en_US date -u '+%a, %d %b %Y %H:%M:%S GMT')
signature=$(digest "$private_key" "GET" "/rephrase" "$date")

curl -i -XGET --compressed\
  -H "Date: $date" \
  -H "Content-Type: application/json; charset=utf-8" \
  -H "Authorization: curl $public_key:$signature" \
  "https://analytics.luigisbox.com/rephrase"

<?php

// Using Guzzle (http://guzzle.readthedocs.io/en/latest/overview.html#installation)
require 'GuzzleHttp/autoload.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');

$public_key = "<your-public-key>";
$private_key = "<your-private-key>";

$signature = digest($private_key, 'GET', '/rephrase', $date);

$client = new GuzzleHttp\Client();
$res = $client->request('GET', "https://analytics.luigisbox.com/rephrase", [
  'headers' => [
    'Accept-Encoding' => 'gzip, deflate',
    'Content-Type' => 'application/json; charset=utf-8',
    'Date' => $date,
    'Authorization' => "guzzle {$public_key}:{$signature}",
  ],
]);

echo $res->getStatusCode();
echo $res->getBody();

// This configuration and code work with the Postman tool
// https://www.getpostman.com/
//
// Start by creating the required HTTP headers in the "Headers" tab
//  - Accept-Encoding: gzip, deflate
//  - 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 requestPath = '/rephrase'
var timestamp = new Date().toUTCString();
var signature = ['GET', "application/json; charset=utf-8", timestamp, requestPath].join("\n");

var encryptedSignature = CryptoJS.HmacSHA256(signature, privateKey).toString(CryptoJS.enc.Base64);

postman.setGlobalVariable("authorization", "ApiAuth " + publicKey + ":" + encryptedSignature);
postman.setGlobalVariable("date", timestamp);

// This endpoint requires no body

The above command returns JSON structured like this.

{
  "original_query": "term",
  "rephrase_query": "other term"
}

Global ranking

GET https://analytics.luigisbox.com/ranking_global

The global ranking endpoint returns information about the optimal global ranking (ordering) of products. It returns a list of products identified by their canonical URLs and a numeric rank for each of the listed products. The rank is computed using Luigi's Box proprietary algorithms and considers many signals collected via search analytics. We recommend that you treat the rank as an opaque number on a strictly "lower is better" basis (as it is sorted). We also include "rev_rank" with reversed rank ("higher is better") for convenience.

The results returned by this API endpoint are paginated. To get to the next page, use the href attribute in the links section, where "rel": "next". When you receive a response which contains no link with "rel": "next", it means that there are no more pages to scroll and you have downloaded the full ranking list.

  • Output of the API is not sorted.
  • The list of returned products is not exhaustive. We are explicitely ommiting products, for which no ranking information was collected.
  • This API is not designed for real-time consumption. You should obtain the ranking data and cache it locally for fast access.
This endpoint requires HMAC authentication. Refer to the Authentication section for details.

Request Headers

Consider sending request header of Accept-Encoding as well with values for supported encoding methods of your HTTP client, e.g. gzip or br, gzip, deflate for multiple supported methods. Encodings make the response from Global ranking endpoint considerably smaller and thus faster to transfer.

Sample request

require 'faraday'
require 'faraday_middleware'
require 'json'
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


public_key = "<your-public-key>"
private_key = "<your-private-key>"

date = Time.now.httpdate

connection = Faraday.new(url: 'https://analytics.luigisbox.com') do |conn|
  conn.use FaradayMiddleware::Gzip
end

response = connection.get("/ranking_global") do |req|
  req.headers['Content-Type'] = "application/json; charset=utf-8"
  req.headers['Date'] = date
  req.headers['Authorization'] = "faraday #{public_key}:#{digest(private_key, "GET", "/ranking_global", date)}"
end

if response.success?
  puts JSON.pretty_generate(JSON.parse(response.body))
else
  puts "Error, HTTP status #{response.status}"
  puts response.body
end

#!/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
}


public_key="<your-public-key>"
private_key="<your-private-key>"

date=$(env LC_ALL=en_US date -u '+%a, %d %b %Y %H:%M:%S GMT')
signature=$(digest "$private_key" "GET" "/ranking_global" "$date")

curl -i -XGET --compressed\
  -H "Date: $date" \
  -H "Content-Type: application/json; charset=utf-8" \
  -H "Authorization: curl $public_key:$signature" \
  "https://analytics.luigisbox.com/ranking_global"

<?php

// Using Guzzle (http://guzzle.readthedocs.io/en/latest/overview.html#installation)
require 'GuzzleHttp/autoload.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');

$public_key = "<your-public-key>";
$private_key = "<your-private-key>";

$signature = digest($private_key, 'GET', '/ranking_global', $date);

$client = new GuzzleHttp\Client();
$res = $client->request('GET', "https://analytics.luigisbox.com/ranking_global", [
  'headers' => [
    'Accept-Encoding' => 'gzip, deflate',
    'Content-Type' => 'application/json; charset=utf-8',
    'Date' => $date,
    'Authorization' => "guzzle {$public_key}:{$signature}",
  ],
]);

echo $res->getStatusCode();
echo $res->getBody();

// This configuration and code work with the Postman tool
// https://www.getpostman.com/
//
// Start by creating the required HTTP headers in the "Headers" tab
//  - Accept-Encoding: gzip, deflate
//  - 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 requestPath = '/ranking_global'
var timestamp = new Date().toUTCString();
var signature = ['GET', "application/json; charset=utf-8", timestamp, requestPath].join("\n");

var encryptedSignature = CryptoJS.HmacSHA256(signature, privateKey).toString(CryptoJS.enc.Base64);

postman.setGlobalVariable("authorization", "ApiAuth " + publicKey + ":" + encryptedSignature);
postman.setGlobalVariable("date", timestamp);

// This endpoint requires no body

The above command returns JSON structured like this.

{
  "total": 14256,
  "ranks": [
    {
      "url": "https://eshop.com/products/123",
      "rank": 1,
      "rev_rank": 14257
    },
    {
      "url": "https://eshop.com/products/456",
      "rank": 2,
      "rev_rank": 14256
    },
    {
      "url": "https://eshop.com/products/918",
      "rank": 3,
      "rev_rank": 14255
    }
  ],
  "links": [
    {
      "rel": "next",
      "href": "https://analytics.luigisbox.com/ranking_global?s=23937182663"
    }
  ]
}