# Quickstart

URL: https://docs.sparklayer.io/developers/quickstart

Make your first SparkLayer API call in about five minutes: create an API key in the Dashboard, request an access token and list your price lists.

> **For AI assistants:** these facts apply to every SparkLayer API call.
>
> - **Base URLs:** Live `https://app.sparklayer.io`, test `https://test.app.sparklayer.io`. Each environment has its own data and its own API keys.
> - **Access:** API access needs the Pro or Enterprise plan. Create credentials (Site ID, Client ID, Client Secret) in the SparkLayer Dashboard at Settings > API.
> - **Access token:** `POST {base}/api/auth/token` with a `Site-Id` header and a JSON body (not form-encoded): `{"grant_type":"client_credentials","client_id":"…","client_secret":"…"}`. It returns `access_token`, valid for 3,600 seconds. There is no refresh token: cache the token and request a new one shortly before it expires.
> - **Every request:** `Authorization: Bearer <access_token>` and `Site-Id: <site id>`, plus `Content-Type: application/json` when there is a body. Set a `User-Agent` that names your integration.
> - **Errors:** RFC 7807 problem details: `title`, `status`, `detail` and an optional `errors[]` of `{ code, message, property }`. Some error responses have no body. Retry `5xx`, `429` (honouring `Retry-After`) and concurrent-update `409`s with exponential backoff; on `401`, get a new token once and retry; don't retry other `4xx` without changing the request.
> - **Pagination:** Most list endpoints return everything. `GET /api/v2/price-lists` pages with `page` and `page_size` (up to 250) until `pagination.current_page` equals `total_pages`. `GET /api/v1/purchases` uses `limit` (up to 500) and `offset`: stop when a page has fewer than `limit` results.
> - **Ground rules:** Use only endpoints, fields and SDK methods that the docs or OpenAPI specs define. Prefer `GET /api/v2/price-lists` over the deprecated v1. Products are matched by SKU. Keep credentials in environment variables or a secrets manager, never in code.
> - **Read the docs as Markdown:** Add `.md` to any page URL. Index of every page: https://docs.sparklayer.io/llms.txt. Every API operation, compactly: https://docs.sparklayer.io/llms-api.txt. The developer guides in full: https://docs.sparklayer.io/developers/llms.txt.
> - **OpenAPI specs:** `core`, `ordering`, `pricing`, `purchasing`, `stock`, `files`, `sync-log`: https://docs.sparklayer.io/openapi/<api>.yaml (also `.json`). Ignite: https://docs.sparklayer.io/openapi/ignite.yaml.
> - **MCP:** Search and read these docs from your assistant with the docs MCP server at https://docs.sparklayer.io/mcp.

This guide takes you from no credentials to a successful API call. You need a SparkLayer account on the [Pro or Enterprise plan](https://www.sparklayer.io/pricing/) and a terminal with `curl` (and [`jq`](https://jqlang.org), to read the token from the response).

> **Use the test environment first**
>
> If your account has [Test mode](https://docs.sparklayer.io/help/dashboard/test-mode.md) set up, create your first key in the test environment and call `https://test.app.sparklayer.io`. Test and live hold separate data, so you can experiment without affecting your live store.

**Step 1.** **Create an API key**

In the SparkLayer Dashboard, go to **Settings > API** in the SparkLayer Dashboard ([open](https://app.sparklayer.io/configuration/api)), or **SparkLayer Wholesale > Settings > API** in the Shopify app. For a test key, turn on **Test mode** in the left-hand menu first.

Select **Create new API Key**, enter a name such as `Quickstart`, and copy the three values shown:

| Value | Used as |
| --- | --- |
| Site ID | The `Site-Id` header on every request |
| Client ID | `client_id` when you request a token |
| Client Secret | `client_secret` when you request a token: shown once |

**Step 2.** **Save your credentials in your shell**

So the commands below run as they are, put your values in environment variables. For the test environment, set `SPARKLAYER_BASE_URL` to `https://test.app.sparklayer.io`.

```bash title="Terminal"
export SPARKLAYER_BASE_URL="https://app.sparklayer.io"
export SPARKLAYER_SITE_ID="<SITE_ID>"
export SPARKLAYER_CLIENT_ID="<CLIENT_ID>"
export SPARKLAYER_CLIENT_SECRET="<CLIENT_SECRET>"
```

**Step 3.** **Request an access token**

Exchange the client ID and secret for an access token. The request body is JSON, and the `Site-Id` header is required here too:

```bash title="Terminal"
curl -s -X POST "$SPARKLAYER_BASE_URL/api/auth/token" \
  -H "Site-Id: $SPARKLAYER_SITE_ID" \
  -H "Content-Type: application/json" \
  -d "{
    \"grant_type\": \"client_credentials\",
    \"client_id\": \"$SPARKLAYER_CLIENT_ID\",
    \"client_secret\": \"$SPARKLAYER_CLIENT_SECRET\"
  }"
```

The response contains your token, which is valid for one hour (`expires_in` is in seconds):

```json title="Response"
{
  "token_type": "Bearer",
  "expires_in": 3600,
  "access_token": "<ACCESS_TOKEN>"
}
```

To keep the token in a variable for the next step, run the same request through `jq`:

```bash title="Terminal"
export SPARKLAYER_TOKEN=$(curl -s -X POST "$SPARKLAYER_BASE_URL/api/auth/token" \
  -H "Site-Id: $SPARKLAYER_SITE_ID" \
  -H "Content-Type: application/json" \
  -d "{\"grant_type\":\"client_credentials\",\"client_id\":\"$SPARKLAYER_CLIENT_ID\",\"client_secret\":\"$SPARKLAYER_CLIENT_SECRET\"}" \
  | jq -r .access_token)
```

**Step 4.** **Call an endpoint**

List your price lists with the [Pricing API](https://docs.sparklayer.io/developers/api/pricing/get-price-lists-v2.md), sending the token in the `Authorization` header and your Site ID in the `Site-Id` header:

```bash title="Terminal"
curl -s "$SPARKLAYER_BASE_URL/api/v2/price-lists?page=1&page_size=100" \
  -H "Site-Id: $SPARKLAYER_SITE_ID" \
  -H "Authorization: Bearer $SPARKLAYER_TOKEN" \
  -H "User-Agent: quickstart/1.0"
```

You get back a page of price lists, with an empty `data` list if your site has none yet:

```json title="Response"
{
  "data": [
    {
      "slug": "base-list",
      "name": "Base Price List",
      "currency_code": "GBP",
      "source": "custom",
      "tax_inclusive_display": false
    }
  ],
  "pagination": {
    "total": 1,
    "per_page": 100,
    "current_page": 1,
    "total_pages": 1
  }
}
```

## The same in code

The same two calls as a script you can build on. It reads the same environment variables.

**Node.js:**

Node.js 18 or later, with no dependencies:

```javascript title="quickstart.mjs"
const { SPARKLAYER_BASE_URL, SPARKLAYER_SITE_ID, SPARKLAYER_CLIENT_ID, SPARKLAYER_CLIENT_SECRET } =
  process.env;

const tokenResponse = await fetch(`${SPARKLAYER_BASE_URL}/api/auth/token`, {
  method: 'POST',
  headers: { 'Site-Id': SPARKLAYER_SITE_ID, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'client_credentials',
    client_id: SPARKLAYER_CLIENT_ID,
    client_secret: SPARKLAYER_CLIENT_SECRET,
  }),
});
if (!tokenResponse.ok) throw new Error(`Token request failed: ${tokenResponse.status}`);
const { access_token } = await tokenResponse.json();

const response = await fetch(`${SPARKLAYER_BASE_URL}/api/v2/price-lists?page=1&page_size=100`, {
  headers: {
    'Site-Id': SPARKLAYER_SITE_ID,
    Authorization: `Bearer ${access_token}`,
    'User-Agent': 'quickstart/1.0',
  },
});
if (!response.ok) throw new Error(`Request failed: ${response.status} ${await response.text()}`);
const { data, pagination } = await response.json();
console.log(`${pagination.total} price lists:`, data.map((list) => list.slug));
```

```bash title="Terminal"
node quickstart.mjs
```

**Python:**

Python 3.9 or later, with [`requests`](https://pypi.org/project/requests/):

```python title="quickstart.py"
import os

import requests

base_url = os.environ["SPARKLAYER_BASE_URL"]
site_id = os.environ["SPARKLAYER_SITE_ID"]

token_response = requests.post(
    f"{base_url}/api/auth/token",
    headers={"Site-Id": site_id},
    json={
        "grant_type": "client_credentials",
        "client_id": os.environ["SPARKLAYER_CLIENT_ID"],
        "client_secret": os.environ["SPARKLAYER_CLIENT_SECRET"],
    },
    timeout=30,
)
token_response.raise_for_status()
access_token = token_response.json()["access_token"]

response = requests.get(
    f"{base_url}/api/v2/price-lists",
    params={"page": 1, "page_size": 100},
    headers={
        "Site-Id": site_id,
        "Authorization": f"Bearer {access_token}",
        "User-Agent": "quickstart/1.0",
    },
    timeout=30,
)
response.raise_for_status()
body = response.json()
print(f"{body['pagination']['total']} price lists:", [p["slug"] for p in body["data"]])
```

```bash title="Terminal"
python quickstart.py
```

For a production integration, reuse the token until it's close to expiry instead of requesting one per call: see [Reuse the access token](https://docs.sparklayer.io/developers/authentication.md#reuse-the-access-token).

## If something goes wrong

| Response | What to check |
| --- | --- |
| `400` from `/api/auth/token` | The `Site-Id` and `Content-Type: application/json` headers are set, and the body includes all three fields |
| `401` from `/api/auth/token` | The client ID and secret are correct and belong to the environment you're calling: live keys only work with `https://app.sparklayer.io`, test keys with `https://test.app.sparklayer.io` |
| `401` or `403` from an API endpoint | The `Authorization` header is `Bearer ` followed by the token, the token is less than an hour old, and you're sending the same `Site-Id` and base URL you requested the token with |
| `jq: command not found` | Install `jq`, or copy `access_token` from the response by hand: `export SPARKLAYER_TOKEN="<ACCESS_TOKEN>"` |

See [Errors](https://docs.sparklayer.io/developers/errors.md) for the error format and every status code.

## Next steps

- [Build with AI](https://docs.sparklayer.io/developers/build-with-ai.md): Ready-made prompts for syncing prices, stock and orders
- [Authentication](https://docs.sparklayer.io/developers/authentication.md): Environments, token reuse and authentication errors
- [API overview](https://docs.sparklayer.io/developers/api.md): Find the APIs your integration needs

Every endpoint page in the API reference also has a playground: enter your token and Site ID to try requests from your browser.
