> ## Documentation Index
> Fetch the complete documentation index at: https://docs.amber.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Export your product catalog

> Read every Parent Product, Product Option, SKU, and option price for an ERP, PIM, or warehouse

Build a catalog export with stable IDs that you can join in your own system.
**Parent Products** are styles or product families. **Product Options** belong
to a Parent Product. **SKUs** identify individual catalog variations and can
reference a Product Option. These are three different resources.

Before you start, obtain an [API key](/authentication), set `AMBER_API_KEY` and
`AMBER_BRAND`, and save the shared [Node.js client](/pagination#reusable-nodejs-client)
as `amber-client.mjs`. The script below requires Node.js 22 or later.

<Steps>
  <Step title="Inspect one page">
    ```bash theme={null}
    curl --fail-with-body \
      -H "Authorization: Bearer $AMBER_API_KEY" \
      "https://app.amber.ai/api/public/v1/brands/$AMBER_BRAND/products?limit=25"
    ```

    The response's `items` contains Parent Product summaries, including `id`,
    `code`, and `name`. Continue with `nextCursor` until it is `null`.
    `/products` supports `limit` and `cursor`; it has no product-code search or
    `updatedSince` filter. Use the returned IDs in later requests.
  </Step>

  <Step title="Export the collections once per brand">
    Save this as `export-catalog.mjs`. Each output line is a JSON object with
    `resource` and `record`, so different resource shapes remain distinguishable.

    ```javascript export-catalog.mjs theme={null}
    import { once } from "node:events";
    import { apiUrl, records } from "./amber-client.mjs";

    const resources = [
      "products", "product-options", "skus", "product-option-prices",
      "product-option-values", "options", "option-values",
      "sku-dimension-values", "dimensions", "dimension-values",
    ];

    for (const resource of resources) {
      for await (const record of records(apiUrl(resource))) {
        const line = JSON.stringify({ resource, record }) + "\n";
        if (!process.stdout.write(line)) await once(process.stdout, "drain");
      }
    }
    ```

    ```bash theme={null}
    node export-catalog.mjs > catalog.ndjson.tmp && mv catalog.ndjson.tmp catalog.ndjson
    ```

    The final file is replaced only when the script succeeds. Requests run
    sequentially and honor `429` responses through the shared client. An empty
    collection contributes no lines. A successful run can produce an empty file.
  </Step>

  <Step title="Join records using IDs">
    | Record | Join |
    | - | - |
    | Product Option | `productId` to Parent Product `id` |
    | SKU | `productId` to Parent Product `id`; nullable `productOptionId` to Product Option `id` |
    | Option price | `productOptionId` to Product Option `id`; retain `kind`, `currency`, and decimal-string `amount` |
    | Assigned option value | `productOptionId` to Product Option `id`; `optionValueId` to `/option-values` `id` |
    | Assigned SKU dimension | `skuId` to SKU `id`; `dimensionValueId` to `/dimension-values` `id` |

    `/options` defines attributes; it does not return Product Options.
    Option-value definitions carry `optionId`, and dimension-value definitions
    carry `dimensionId`. Keep these library IDs to resolve labels.

    Assignment records may have no `id`. Key option prices by
    `(productOptionId, kind, currency)`, option-value assignments by
    `(productOptionId, optionValueId)`, and SKU dimensions by
    `(skuId, dimensionValueId)`. Include brand and resource in your local keys.
  </Step>
</Steps>

## Narrow the export to one Parent Product

Use a real Parent Product ID from step 1:

```bash theme={null}
curl --fail-with-body --get \
  -H "Authorization: Bearer $AMBER_API_KEY" \
  --data-urlencode "productId=$PRODUCT_ID" \
  --data-urlencode "limit=100" \
  "https://app.amber.ai/api/public/v1/brands/$AMBER_BRAND/product-options"
```

The same `productId` filter works on `/skus` and `/product-option-prices`.
To retrieve one Product Option's SKUs, use `/skus?productOptionId={id}`.
Every filtered collection still needs pagination.

## Add specifications or run on a schedule

The export contains the listed collections, not every field in a Parent Product
detail. Use [Read product specifications](/tutorials/product-specifications)
for custom fields, BOMs, versioned specifications, and measurement tables.
For recurring jobs, follow [the reconciliation guidance](/pagination#plan-a-recurring-export).
Read-only access does not provide an import or update operation.
