Catalogue
The catalogue is the list of packages you can sell. A package is one plan: a data allowance for a country or a region, valid for a number of days, at a price for your account.
The catalogue and its prices are the same with a test key and a live key. How the catalogue and prices work explains what is listed and how price is set.
| Method | Path | What it does |
|---|---|---|
GET |
/v1/countries |
List the countries and regions you can sell. |
GET |
/v1/packages |
List packages. |
GET |
/v1/packages/{id} |
Get one package. |
List countries and regions
Section titled “List countries and regions”GET /v1/countries
Returns every country and region that has at least one package. Countries come first, then regions. Use it to build your destination picker. This list is not paginated.
There are no parameters.
curl "$ESIMIFY_API_URL/v1/countries" \ -H "Authorization: Bearer $ESIMIFY_API_KEY"const res = await fetch(`${process.env.ESIMIFY_API_URL}/v1/countries`, { headers: { Authorization: `Bearer ${process.env.ESIMIFY_API_KEY}` },});const body = await res.json();<?php$ch = curl_init(getenv('ESIMIFY_API_URL') . '/v1/countries');curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('ESIMIFY_API_KEY'), ],]);$body = json_decode(curl_exec($ch), true);$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);import osimport requests
res = requests.get( os.environ["ESIMIFY_API_URL"] + "/v1/countries", headers={"Authorization": "Bearer " + os.environ["ESIMIFY_API_KEY"]}, timeout=30,)body = res.json()Response
Section titled “Response”200 OK
{ "data": [ { "code": "TH", "name": "Thailand", "type": "country", "packages_count": 14 }, { "code": "AE", "name": "United Arab Emirates", "type": "country", "packages_count": 11 }, { "code": "region-412", "name": "Asia", "type": "region", "packages_count": 6 } ]}| Field | Type | Description |
|---|---|---|
code |
string | The code to filter packages by. For a country it is the ISO 3166-1 alpha-2 code. For a region it is region- and a number, and stays the same if the region is renamed. |
name |
string | The display name. |
type |
string | country or region. |
packages_count |
integer | How many packages you can sell for it. For a country this includes regional and global packages that cover it, so it equals the number of packages GET /v1/packages?country= returns. |
Errors
Section titled “Errors”This endpoint returns only the common errors.
List packages
Section titled “List packages”GET /v1/packages
Returns packages, one page at a time. Filter by country or region to get the plans for one destination.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
country |
query | string | No | Two-letter country code, such as TH. Upper or lower case. Returns the packages that work in that country, regional and global ones included. An unknown code returns an empty list. |
region |
query | string | No | A region code from GET /v1/countries, such as region-412. The older name-based codes, such as asia, are still accepted. |
unlimited |
query | boolean | No | true returns unlimited-data packages only. false returns metered packages only. |
q |
query | string | No | Search in the package and destination name, ignoring case. Up to 100 characters. |
limit |
query | integer | No | 1 to 100. Default 25. |
cursor |
query | string | No | The next_cursor of the previous page. |
curl "$ESIMIFY_API_URL/v1/packages?country=TH&limit=2" \ -H "Authorization: Bearer $ESIMIFY_API_KEY"const res = await fetch(`${process.env.ESIMIFY_API_URL}/v1/packages?country=TH&limit=2`, { headers: { Authorization: `Bearer ${process.env.ESIMIFY_API_KEY}` },});const body = await res.json();<?php$ch = curl_init(getenv('ESIMIFY_API_URL') . '/v1/packages?country=TH&limit=2');curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('ESIMIFY_API_KEY'), ],]);$body = json_decode(curl_exec($ch), true);$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);import osimport requests
res = requests.get( os.environ["ESIMIFY_API_URL"] + "/v1/packages", headers={"Authorization": "Bearer " + os.environ["ESIMIFY_API_KEY"]}, params={"country": "TH", "limit": "2"}, timeout=30,)body = res.json()Response
Section titled “Response”200 OK with a list of Package objects.
{ "data": [ { "id": "pkg_10482", "name": "Thailand 1 GB 7 days", "type": "country", "country": "TH", "countries": [ "TH" ], "data_mb": 1024, "unlimited": false, "validity_days": 7, "price": { "amount": "249.00", "currency": "INR" }, "retail_price": { "amount": "349.00", "currency": "INR" }, "topup": true }, { "id": "pkg_10497", "name": "Thailand Unlimited 10 days", "type": "country", "country": "TH", "countries": [ "TH" ], "data_mb": null, "unlimited": true, "validity_days": 10, "price": { "amount": "1149.00", "currency": "INR" }, "retail_price": { "amount": "1499.00", "currency": "INR" }, "topup": true } ], "has_more": true, "next_cursor": "MTA0OTc"}price is what the package costs you. retail_price is the price eSIMify sells the same package for. What you charge your customer is your decision.
Packages are returned in a stable order, by id. country and region together return the packages that match both. A regional package has country null: read countries.
Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
400 |
invalid_request |
A query parameter is not valid: a country that is not two letters, an unlimited that is not true or false, a q longer than 100 characters, a limit outside 1 to 100, or a cursor that did not come from this list. param names it. |
Any endpoint can also return the common errors.
Get a package
Section titled “Get a package”GET /v1/packages/{id}
Returns one package. Use it to check a price just before you place an order. A 200 here means the package can be ordered now.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string | Yes | The package id, such as pkg_10482. |
curl "$ESIMIFY_API_URL/v1/packages/pkg_10482" \ -H "Authorization: Bearer $ESIMIFY_API_KEY"const res = await fetch(`${process.env.ESIMIFY_API_URL}/v1/packages/pkg_10482`, { headers: { Authorization: `Bearer ${process.env.ESIMIFY_API_KEY}` },});const pkg = await res.json();<?php$ch = curl_init(getenv('ESIMIFY_API_URL') . '/v1/packages/pkg_10482');curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('ESIMIFY_API_KEY'), ],]);$pkg = json_decode(curl_exec($ch), true);$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);import osimport requests
res = requests.get( os.environ["ESIMIFY_API_URL"] + "/v1/packages/pkg_10482", headers={"Authorization": "Bearer " + os.environ["ESIMIFY_API_KEY"]}, timeout=30,)pkg = res.json()Response
Section titled “Response”200 OK with a Package.
{ "id": "pkg_10482", "name": "Thailand 1 GB 7 days", "type": "country", "country": "TH", "countries": [ "TH" ], "data_mb": 1024, "unlimited": false, "validity_days": 7, "price": { "amount": "249.00", "currency": "INR" }, "retail_price": { "amount": "349.00", "currency": "INR" }, "topup": true}Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
404 |
package_not_found |
No package you can sell has this id. It may have been retired, or the id is not exact: pkg_010482 is not pkg_10482. |
Any endpoint can also return the common errors.
How the catalogue and prices work
Section titled “How the catalogue and prices work”Prices
Section titled “Prices”priceis your price: the eSIMify retail price (retail_price) less your partner discount. Your discount comes from your partner tier, or from a rate agreed for your account. The Business portal shows it.priceis never aboveretail_price, and never below what the package costs eSIMify. For a few packages that means your discount is smaller than usual.- Prices can change. A change to the catalogue or to your discount shows within a minute. An order is charged at the price at the moment you place it, so compare the order’s
totalwith what you expected. - Amounts have two decimal places and are in INR.
One package for each plan
Section titled “One package for each plan”Several mobile network providers can offer the same plan. The list shows one package for each destination, data size and validity:
- The package shown is the one eSIMify sells in its own store, then the one with the lowest price for you.
- Sizes within about 20% of a standard size count as the same plan, and so do validities in the same band. The 1 GB slot can be filled by a package of 1200 MB, and the 7-day slot by one of 8 days. Read
data_mbandvalidity_daysfrom the package, not from its name. - A plan with a daily allowance, such as “2 GB/Day”, is its own package. Its
data_mbis the amount for one day. - The
idbehind a slot can change when a better offer appears or a package is retired. Two partners on different discounts can see different ids for the same slot. - An
idyou stored earlier can still be fetched and ordered while that package is on sale. Once it is retired,GET /v1/packages/{id}andPOST /v1/ordersanswer404 package_not_found. Then pick the package the list shows now.
Refresh your copy of the catalogue at least once a day.
What is never listed
Section titled “What is never listed”A package is left out of the list, answers 404 by id and cannot be ordered when:
- it has been retired, or its mobile network provider is switched off
- it is not sold in INR
- it requires identity verification (KYC) of the traveller before it can be activated, such as eSIMs for use in India: this API has no KYC step
- it is outside the set of packages agreed for your account, or it is unlimited and your account does not sell unlimited packages
- its data size or the countries it covers cannot be stated reliably
- its price for you would round to less than 0.01

