# Availability

Check stock and purchasability of up to 50 SKUs in one request.

Section: Commerce · REST API
Canonical URL: https://docs.omniretailconnect.nl/commerce/api/availability

## Check availability

`GET /api/v1/availability` — Needs commerce.read. Equivalent MCP tool: check_sku_availability.

A bulk stock check for 1 to 50 SKUs.

*Query parameters of GET /availability*

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `skus` | string or array | Required | 1 to 50 SKUs, each at most 64 characters. Either one comma-separated value (`skus=A,B`) or a repeated array parameter (`skus[]=A&skus[]=B`). |

**Check two SKUs · curl**

```bash
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/availability?skus=HAM-500,HAM-700" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"
```

**Check two SKUs · JavaScript**

```javascript
// Server-side code only: never put the token in browser code.
// Top-level await needs an ES module (a .mjs file or "type": "module").
const token = process.env.COMMERCE_API_TOKEN; // ocm_<token>

const response = await fetch('https://<your-shop-domain>/api/v1/availability?skus=HAM-500,HAM-700', {
  headers: { Authorization: `Bearer ${token}` },
});

console.log(response.status, await response.json());
```

**Check two SKUs · PHP**

```php
<?php

$token = getenv('COMMERCE_API_TOKEN'); // ocm_<token>

$curl = curl_init('https://<your-shop-domain>/api/v1/availability?skus=HAM-500,HAM-700');
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],
]);

$body = json_decode(curl_exec($curl), true);

echo curl_getinfo($curl, CURLINFO_RESPONSE_CODE), PHP_EOL;
print_r($body);
```

**Check two SKUs · Python**

```python
import json
import os
import urllib.error
import urllib.request

token = os.environ["COMMERCE_API_TOKEN"]  # ocm_<token>

request = urllib.request.Request(
    "https://<your-shop-domain>/api/v1/availability?skus=HAM-500,HAM-700",
    headers={"Authorization": f"Bearer {token}"},
)

try:
    with urllib.request.urlopen(request) as response:
        print(response.status, json.load(response))
except urllib.error.HTTPError as error:
    # Errors are JSON too: {"error": {"code": ..., "message": ...}}
    print(error.code, json.load(error))
```

The array form, with curl:

**Array form**

```bash
curl -sS -G "https://<your-shop-domain>/api/v1/availability" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN" \
  --data-urlencode "skus[]=HAM-500" \
  --data-urlencode "skus[]=HAM-700"
```

## Response

A list of one entry per distinct SKU, in input order. There is no `meta`. Example values are illustrative. Names and prices are not included.

**Example response · 200**

```json
{
    "data": [
        {
            "sku": "HAM-500",
            "found": true,
            "quantity": 14,
            "purchasable": true,
            "low_stock": false,
            "expected_restock_at": null,
            "source": "local",
            "status": "ok"
        },
        {
            "sku": "HAM-700",
            "found": true,
            "quantity": 3,
            "purchasable": true,
            "low_stock": true,
            "expected_restock_at": null,
            "source": "local",
            "status": "ok"
        },
        {
            "sku": "NOPE-1",
            "found": false,
            "quantity": null,
            "purchasable": false,
            "low_stock": false,
            "expected_restock_at": null,
            "source": "local",
            "status": "ok"
        }
    ]
}
```

*Fields of each entry*

| Field | Type | Description |
| --- | --- | --- |
| `sku` | string | The SKU this entry is about. |
| `found` | boolean | `false` for an unknown SKU. This is not an error. |
| `quantity` | integer\|null | `null` when stock is not tracked, or when the status is `unavailable`. |
| `purchasable` | boolean | Whether the variant can be bought. `false` when the status is `unavailable`. |
| `low_stock` | boolean | `true` when the quantity is at or below the low-stock threshold set for the variant. |
| `expected_restock_at` | string\|null | Expected restock time (ISO 8601), when known. |
| `source` | string | `local` or `external`. |
| `status` | string | `ok`, or `unavailable` when the inventory source could not be reached for that variant. |

- In the comma form, entries are trimmed and blank entries are dropped. In the array form, blank entries are dropped and a comma is part of the SKU.
- Exact duplicates are answered once. Whether `a` matches `A` depends on the database collation, so send SKUs in their stored spelling.
- An unknown SKU is `found: false` with status `ok`.
- Status `unavailable` does not mean the SKU is out of stock: the inventory source could not be reached.

## Errors

*Errors of GET /availability*

| Status | error.code | When |
| --- | --- | --- |
| `401` | `unauthenticated` | Missing, malformed, unknown, revoked or expired token, or a token without the `api` channel. |
| `414` | `uri_too_long` | The URL is longer than 4096 bytes by default. 50 SKUs of 64 characters need about 3200. |
| `422` | `invalid_argument` | `skus` missing, empty or all blank; more than 50 SKUs; an entry longer than 64 characters; an unknown parameter. |
| `429` | `too_many_requests` | Rate limit reached. Wait the number of seconds in `Retry-After`. |

> **Warning — Long URLs:** Fifty long SKUs make a long URL: 50 SKUs of 64 characters need about 3200 characters in the comma form and about 3.5 to 3.9 thousand in the array form, both under the default URL cap of 4096 bytes. Longer URLs are refused with `414`. Split large lists into several requests. See [request limits](https://docs.omniretailconnect.nl/commerce/getting-started/limits.md#request-size).
