---
title: Content Export (GET)
description: Export indexed catalog objects from Luigi's Box for batch synchronization, backup, or downstream processing.
slug: indexing/api/v1/export
docKind: endpoint
hub: indexing
tableOfContents: true
---
import ApiSection from "../../../../../components/ApiSection.astro";
import ApiEndpoint from "../../../../../components/ApiEndpoint.astro";
import ApiCodeTabs from "../../../../../components/ApiCodeTabs.astro";
import { Aside } from "@astrojs/starlight/components";

<Aside type="note">
  This endpoint requires HMAC authentication. Refer to the [Authentication](/platform-foundations/api-principles/#authentication) documentation for details.
</Aside>

<ApiSection>
  <div slot="code">
    <ApiEndpoint method="GET" url="https://live.luigisbox.com/v1/content_export" />
  </div>
## Overview

Use this endpoint to export the current contents of your Luigi's Box catalog for batch processing, backups, migrations, or downstream synchronization.

Each response returns indexed objects together with their record fields, nested objects, and type metadata. Objects of type `query` are not included in the export.

The export is split into pages. Pagination information is returned in the `links` array. When another page is available, one of the objects in `links` has `rel` set to `next`. Use the corresponding `href` value exactly as returned to load the next page.

<Aside type="tip">
  Treat the pagination URL as an opaque value. Reuse it exactly as returned by the API.
</Aside>

<Aside type="danger">
  Export links expire after 10 minutes. If you wait longer before requesting the next page, you must restart the export from the beginning.
</Aside>

<Aside type="note">
  This endpoint is designed for batch export workflows. If you need live querying, filtering, or ranking, use the Search or Autocomplete APIs instead.
</Aside>
</ApiSection>

<ApiSection>
## Query parameters

Use query parameters to control page size, exported fields, object types, and pagination state.

| Parameter | Type | Required | Description |
| :-- | :-- | :-- | :-- |
| `size` | Integer |  | Number of records returned in one response page. Defaults to `300`. Values above `500` are reduced to `500`. |
| `hit_fields` | String |  | Comma-separated list of object fields to include in `attributes`, for example `title,web_url,price`. |
| `requested_types` | String |  | Comma-separated list of object types to export, for example `item,category,brand`. |

## Request headers

<Aside type="note">
  This endpoint supports `Accept-Encoding: gzip` for compressed responses.
</Aside>

  <div slot="code">
    <h4 class="code-section-title">Example: Request URL via cURL</h4>

```shell
host="https://live.luigisbox.com"
endpoint_path="/v1/content_export"
query_string="size=2&requested_types=item,category&hit_fields=title,web_url,price"
request_url="${host}${endpoint_path}?${query_string}"
public_key="your_public_api_key"
private_key="your_private_api_key"
method="GET"
content_type="application/json; charset=utf-8"
date=$(date -u "+%a, %d %b %Y %H:%M:%S GMT")

string_to_sign="$(printf "${method}\n${content_type}\n${date}\n${endpoint_path}")"
signature=$(echo -n "${string_to_sign}" | openssl dgst -sha256 -hmac "${private_key}" -binary | base64)

curl -X ${method} "${request_url}" \
     -H "Content-Type: ${content_type}" \
     -H "Accept-Encoding: gzip, deflate" \
     -H "Date: ${date}" \
     -H "Authorization: ApiAuth ${public_key}:${signature}"
```

  </div>
</ApiSection>

<ApiSection>
## Response structure

The export response is a paginated JSON document.

### Top-level Fields

| Field | Type | Description |
| :-- | :-- | :-- |
| `total` | Integer | Total number of records available in the full export, not just in the current page. |
| `objects` | Array | Records returned in the current response page. |
| `links` | Array | Pagination objects returned by the API. |

### Object fields

Each entry in `objects` contains the following fields:

| Field | Type | Description |
| :-- | :-- | :-- |
| `url` | String | [Object identity](/platform-foundations/identity/) of the exported record. |
| `attributes` | Object | Exported object fields, such as `title`, `web_url`, or `price`. |
| `nested` | Array | Nested objects attached to the main record, such as categories, brands, or variants. |
| `type` | String | Type of the exported record, for example `item`, `category`, or `article`. |
| `exact` | Boolean | Internal export flag returned by Luigi's Box together with the record data. |

  <div slot="code">
    <h4 class="code-section-title">Example: Response Body</h4>

```json
{
  "total": 14256,
  "objects": [
    {
      "url": "/item/1",
      "attributes": {
        "title": "Super product 1",
        "web_url": "/products/super-product-1",
        "price": 49.9
      },
      "nested": [],
      "type": "item",
      "exact": true
    }
  ],
  "links": [
    {
      "rel": "next",
      "href": "https://live.luigisbox.com/v1/content_export?cursor=23937182663"
    }
  ]
}
```

  </div>
</ApiSection>

<ApiSection>
## Code examples

These examples export a small page of `item` and `category` records and request only selected fields so the response stays compact.

Authentication is required for all requests. The examples show how to create the required `Date` and `Authorization` headers for an HMAC-signed request.

<Aside type="danger">
  Your **Private Key** is a secret and should never be exposed in client-side code. It should only be used on a secure server.
</Aside>

<Aside type="tip">
  Your API Keys can be found in the Luigi's Box application under "General settings" > "Integration settings".
</Aside>

  <div slot="code">
    <h4 class="code-section-title">Code Examples</h4>
  <ApiCodeTabs syncKey="indexing-export-request">
    <div slot="ruby">

```ruby
# --- Ruby Example for GET /v1/content_export ---
# Assumes 'faraday' gem is installed (gem install faraday)

require 'faraday'
require 'json'
require 'time'
require 'openssl'
require 'base64'

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"
LUIGISBOX_HOST = 'https://live.luigisbox.com'
ENDPOINT_PATH = '/v1/content_export'
QUERY_STRING = 'size=2&requested_types=item,category&hit_fields=title,web_url,price'
REQUEST_PATH = "#{ENDPOINT_PATH}?#{QUERY_STRING}"

http_method = 'GET'
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.get(REQUEST_PATH) do |req|
  req.headers['Content-Type'] = content_type
  req.headers['Date'] = current_date
  req.headers['Authorization'] = authorization_header
  req.headers['Accept-Encoding'] = 'gzip, deflate'
end

response_body = JSON.parse(response.body)
if response.success? && response_body['objects'].is_a?(Array)
  puts "Export retrieved successfully:"
  puts JSON.pretty_generate(response_body)
else
  puts "Error exporting content: HTTP Status #{response.status}, Response: #{response.body}"
end
```
    </div>
    <div slot="php">

```php
<?php
// PHP Example for GET /v1/content_export
// 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";
$LUIGISBOX_HOST   = 'https://live.luigisbox.com';
$ENDPOINT_PATH    = '/v1/content_export';
$QUERY_STRING     = 'size=2&requested_types=item,category&hit_fields=title,web_url,price';
$REQUEST_URL      = "{$LUIGISBOX_HOST}{$ENDPOINT_PATH}?{$QUERY_STRING}";

$http_method  = 'GET';
$content_type = 'application/json; charset=utf-8';
$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,
    $REQUEST_URL,
    [
        'headers' => [
            'Accept-Encoding' => 'gzip, deflate',
            'Content-Type' => $content_type,
            'Date' => $current_date,
            'Authorization' => $authorization_header,
        ]
    ]
);

$response_body = json_decode($response->getBody(), true);

if ($response->getStatusCode() == 200 && isset($response_body['objects']) && is_array($response_body['objects'])) {
    echo "Export retrieved successfully:" . PHP_EOL;
    echo json_encode($response_body, JSON_PRETTY_PRINT);
} else {
    echo "Error exporting content: HTTP Status " . $response->getStatusCode() . "\nResponse: " . $response->getBody();
}
```
    </div>
    <div slot="javascript">

```javascript
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();
}

const YOUR_PUBLIC_KEY = "your_public_api_key";
const YOUR_PRIVATE_KEY = "your_private_api_key";
const LUIGISBOX_HOST = 'https://live.luigisbox.com';
const ENDPOINT_PATH = '/v1/content_export';
const QUERY_STRING = 'size=2&requested_types=item,category&hit_fields=title,web_url,price';
const REQUEST_URL = `${LUIGISBOX_HOST}${ENDPOINT_PATH}?${QUERY_STRING}`;

const httpMethod = 'GET';
const contentType = 'application/json; charset=utf-8';
const currentDate = new Date().toUTCString();

const signature = generateLuigisBoxDigest(YOUR_PRIVATE_KEY, httpMethod, ENDPOINT_PATH, currentDate, contentType);
const authorizationHeader = `ApiAuth ${YOUR_PUBLIC_KEY}:${signature}`;

axios({
  method: httpMethod,
  url: REQUEST_URL,
  headers: {
    'Content-Type': contentType,
    'Date': currentDate,
    'Authorization': authorizationHeader,
    'Accept-Encoding': 'gzip, deflate'
  }
})
  .then((response) => {
    const responseBody = response.data;
    if (response.status === 200 && Array.isArray(responseBody.objects)) {
      console.log("Export retrieved successfully:", JSON.stringify(responseBody, null, 2));
    } else {
      console.error("Error exporting content: HTTP Status " + response.status + ", Response: " + JSON.stringify(responseBody));
    }
  })
  .catch((error) => {
    console.error("Exception:", error.message);
  });
```
    </div>
  </ApiCodeTabs>

  </div>
</ApiSection>

## Error handling

| HTTP Status | Description |
| :-- | :-- |
| **400 Bad Request** | The request is malformed, contains invalid parameters, or uses an expired or invalid cursor. Check the response body for details. |
| **403 Forbidden** | The request is not allowed for your site in Luigi's Box. |
| **429 Too Many Requests** | The rate limit has been exceeded. Check the `Retry-After` header and see the [Throttling docs](/platform-foundations/api-principles/#throttling). |

## Usage notes

- Use `hit_fields` whenever possible to reduce payload size and speed up export jobs.
- To continue the export, look for the object in `links` where `rel` is `next`, then request its `href` exactly as returned.
- Do not depend on response ordering. If ordering matters in your downstream system, sort the exported data after retrieval.

## Related endpoints

- [Content Update](/indexing/api/v1/content-update/) adds or fully replaces indexed objects.
- [Partial Content Update](/indexing/api/v1/partial-update/) updates specific fields without replacing the full object.
- [Commit Generation](/indexing/api/v1/commit-generation/) removes stale objects after a generation-based sync.
