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

# List products

> Every product you can order, at your own wholesale price

The full catalogue: every `option_id` you can send to
[Create order](https://docs.esimstory.com/endpoints/create-order.md), with what it covers and what you are
invoiced for it. It is the same data as the catalogue CSV, kept current.

Prices move when we reprice. A decrease applies straight away; an increase is
announced in writing 7 days before it applies, as your agreement sets out. So
**fetch this once a day and cache it**, rather than per order or on every page
view.

## Query parameters

All optional. Without them you get the whole catalogue.

**country** `string`

An ISO 3166-1 alpha-2 code, such as `JP`. Returns every plan that works there:
the country's own plans and every regional plan that covers it.

**location\_type** `string`

`country` or `region`.

**location** `string`

A country or region name exactly as the response spells it, such as `Japan`
or `Europe (42 Countries)`. Case does not matter. An unknown name returns an
empty list, not the whole catalogue.

## Signing a GET

Same five lines as any other request, with the body hash taken over the empty
string — see [Authentication](https://docs.esimstory.com/authentication/overview.md). The path you sign is
the path alone, **without** the query string:

```
/api/v1/api_partner/products
```

## Compression

Send `Accept-Encoding: gzip` and the whole catalogue arrives as about 130 KB
rather than about 4 MB. Most HTTP clients send the header and decompress for
you.

## Response

`200` with a `products` array, one object per orderable product, sorted by
country or region, then data allowance, then duration.

**option\_id** `string`

**The only value that goes in an order**, as `option_id`.

**location\_type** `string`

`country` or `region`.

**location** `string`

The plan's name for where it works, such as `Japan` or
`Europe (42 Countries)`.

**countries** `object[]`

Every country the plan covers: one entry on a country plan, each listed
country on a regional one. Match against your own data by `iso_code`.

**properties**

**name** `string`

Country name, such as `Japan`.

**iso\_code** `string`

ISO 3166-1 alpha-2 code, such as `JP`.

**duration\_days** `integer`

How long the plan runs once activated.

**data** `string`

Daily allowance as a label: `500MB`, `1GB`, `2GB`, `3GB` or `Unlimited`.
`Unlimited` plans run at full speed for the first 5GB each day, then at
`1Mbps` — advertise them with that condition.

**data\_mb** `integer`

The same, in megabytes. On `Unlimited` plans this is `5120`, the full-speed
part of the day — not a cap. Show `data` to travellers, not this.

**carrier** `string`

Networks the profile roams on.

**qos** `string`

Speed once the day's allowance is used up.

**network** `string`

Supported generations, such as `4G(LTE) / 5G`.

**activation\_window\_days** `integer`

Days from purchase within which the eSIM must be activated.

**apn** `string`

Access point name the device uses.

**wholesale\_price\_usd** `string`

What you are invoiced per unit, in USD, two decimals. Specific to your
account. Orders already placed keep the price they were placed at.

Prices are strings so they never arrive as `4.5` or with a floating-point tail.

```json Response theme={null}
{
  "products": [
    {
      "option_id": "686ffd73-61af-ee11-be9e-002248f7dbdd",
      "location_type": "country",
      "location": "Japan",
      "countries": [{ "name": "Japan", "iso_code": "JP" }],
      "duration_days": 7,
      "data": "1GB",
      "data_mb": 1024,
      "carrier": "SoftBank, KDDI",
      "qos": "384kbps",
      "network": "4G(LTE) / 5G",
      "activation_window_days": 180,
      "apn": "cmhk",
      "wholesale_price_usd": "3.70"
    }
  ]
}
```

## The catalogue CSV

We may also send you the same catalogue as two CSV files — one for
single-country plans, one for regional plans — for example with your price
list. Use them to review prices or seed a database; the API stays the current
source. Each column maps to a field above:

| CSV column | Field |
| - | - |
| `Country` / `Region` | `location` |
| `Duration (Days)` | `duration_days` |
| `Data` | `data` |
| `Carrier` | `carrier` |
| `QOS` | `qos` |
| `Network` | `network` |
| `Activation Window (Days)` | `activation_window_days` |
| `APN` | `apn` |
| `Option ID` | `option_id` |
| `Wholesale Price (USD)` | `wholesale_price_usd` |

The header names are the ones shown above, spaces and parentheses included.
Locate columns by header rather than by position. A CSV is a snapshot: when it
and the API disagree, the API is right.

## In sandbox

Sandbox returns its own catalogue at your sandbox price. Option IDs are the same
as in production, so an integration built against sandbox works unchanged.

## Errors

### 401 Unauthorized

Missing, expired or invalid signature headers. See
[Authentication](https://docs.esimstory.com/authentication/overview.md).

### 429 Too Many Requests

**10 requests per minute**, counted against your access key. The response is
the whole catalogue — about 11,000 products — so cache it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.