eSIMs API overview
Browse the package catalog, place orders, top up existing eSIMs and track usage across 190+ countries.
Sell travel data eSIMs across 190+ countries from a single API surface. FX-Port sources packages from its eSIM supplier network and prices them for your agency in your own currency: you never call a supplier directly. A package is a data plan for one destination (or one region); an order creates one eSIM from it; a topup adds a package to an eSIM that already exists.
For a summary of what the API covers, who it is for and how to get access, see the eSIM API for travel companies.
How an integration works
Read the catalog
GET /returns the packages for a destination with prices in your currency. Keep theesims/ packages?country=france package_idof the package the traveler chooses.Create the order
POST /with thatesims/ orders package_idand the traveler's email and phone creates one eSIM. Keepbooking_idand the ICCID (data.iccid) from the response.Give the traveler the eSIM
Show
sim.qrcode_urlor theqrcodetext, or let the traveler open thesharing.link. Setup instructions returns the same data again later, with installation steps.Follow the usage
GET /returns the data left and the expiry.esims/ usage/ {iccid} Top up when the traveler runs out
GET /lists what can be added;esims/ topup-packages/ {iccid} POST /adds it.esims/ topups
eSIM concepts
Read these before integrating.
Endpoints
| Method | Path | Permission | Rate limit group | Description |
|---|---|---|---|---|
GET | / | Read | Default | List the package catalog, filtered by country and type |
GET | / | Read | Default | List devices known to support eSIM |
POST | / | Read+Write | Booking | Create a new eSIM order |
GET | / | Read | Default | List the packages that can top up one eSIM |
POST | / | Read+Write | Booking | Top up an existing eSIM |
GET | / | Read | Default | Usage of one eSIM |
GET | / | Read | Default | Usage of several eSIMs |
GET | / | Read | Default | List the agency's eSIM orders (topups are not listed here) |
GET | / | Read | Default | Topups of one eSIM |
GET | / | Read | Default | Check that a booking is yours |
GET | / | Read | Default | Setup data (QR code, APN, steps) of an order |
GET | / | Read | Default | Installation instructions for an ICCID |
Only order creation and topup creation change anything: on live they debit your agency balance and provision the eSIM. Every other endpoint only reads. The full parameters, schemas and examples of every endpoint are in the API reference.
Rate limits
Every API key may send 1,200 requests per minute to the booking group (creating an order and creating a
topup) and 6,000 requests per minute to the default group (every other eSIM endpoint: the catalog, devices,
topup packages, usage, bookings, instructions). The counters are per key and per
minute. A request over the limit is answered 429 with the code rate_limit_exceeded: see
Rate limits and fair use. Too many orders or topups of one key or agency running at the same time
can be answered 429 concurrency_limit_exceeded: retry after the seconds in Retry-After. Separately, the supplier limits how often usage can be
read: see eSIM usage.
Sandbox behaviour
A sandbox key (fxp_test_...) works against the supplier's test system, so every endpoint above can
be tried end to end:
| Behaviour | Detail |
|---|---|
| An order creates a test eSIM | The supplier's sandbox returns a real test eSIM with a dummy activation code (LPA:1$lpa.airalo.com$DUMMY-...). It cannot be installed on a phone. |
| Nothing is debited | Orders and topups carry balanceDebited, transactionCreated and payoutCreated (twins: balance_debited, transaction_created, payout_created), all false. They are also on the error bodies of both endpoints. |
| Topup balance | A topup answers balance previous: 0.0 and current: 0.0: the sandbox has no balance. |
| Usage | A new eSIM shows its full allowance and ACTIVE; usage does not change after a topup. |
| Bookings | Sandbox orders and topups appear only to sandbox keys: orders in bookings, topups in the topup history, and both in the account history and the booking detail. |
| Live-only checks | The balance check (402 insufficient_balance), the 60-second duplicate check and the balance lock (503 balance_lock_timeout) only run on live. |
Traveler contact details
email and phone are required on order and topup creation —
not optional. What happens to them:
email: on an order, FX-Port asks the supplier to email the QR code and a sharing link to this address as soon as the eSIM is created, so the traveler can install it without you relaying the QR code. On a topup the email is kept on the booking only; it is not sent to the supplier.phone,first_name,last_name: kept on the booking and returned by the booking endpoints. They are not sent to the supplier.
Only pass the traveler's own contact details — never billing, pricing, or account information.
first_name and last_name are optional and are not restricted to ASCII: accents and
non-Latin scripts are accepted as they are (only the length is checked).
Conventions of the eSIM endpoints
- Bodies have
success. Every answer carriessuccess(a call that fails also carries the usual error body, see below), except the answer of verify. - Creations answer
200, not201, and have noLocationheader. - JSON in, JSON out. Send
Content-Type: application/on orders and topups (the media type is read in any case). A form-encoded body is also read, but a JSON body without the JSON content type is read as an empty form and refused withjson missing_field. - Wrapped payloads. Topup packages, usage and instructions wrap their payload one level deeper (
data.data); each entry of bulk usage does it again (data.data.data). The reference shows each shape. - Units and times. Data is in megabytes. Times that come from the supplier (
created_atof an order or eSIM,expired_atof usage) areYYYY-MM-DD HH:MM:SSin UTC without a zone; times stored by FX-Port (bookings) are ISO 8601 with a zone. - No trailing slash.
/is redirected with an empty body: call the path without the slash.api/ v1/ esims/ packages/ - Amounts are in the currency of your agency (
currency), never in the supplier's. - Spellings that are read. An ICCID may be written in groups of spaces or hyphens (
8944 4654 0000 0123 456);localetakes any spelling of a locale (zh-Hans,pt_BR,fr_FR.UTF-8, anAccept-Languagevalue) andcountrya country name (United States); an emptypage,limit,forceorall(?page=) means not sent. A value that is really wrong keeps its stable code (invalid_iccid,invalid_locale,invalid_country...). - Names. Several answers keep a legacy camelCase or short key next to its snake_case twin (
codeandorder_id,idandpackage_id,balanceDebitedandbalance_debited,pagination.pagesandtotal_pages...). Both carry the same value; use the snake_case one in new code.?naming=snakereturns every key in snake_case. See Conventions and naming.
Error responses
Every error has a stable code in errors[0].code: branch on it, not on the message. See
Errors for the shape and every shared code.
| HTTP | Typical code | Cause |
|---|---|---|
400 | missing_field, invalid_package_id, invalid_iccid, invalid_email, invalid_phone, invalid_type, invalid_json | A required field is missing or has the wrong format |
401 | authentication_required, invalid_api_key | Missing or invalid API key |
402 | insufficient_balance | Agency balance lower than the amount to debit (live) |
403 | read_write_permission_required, agency_not_active | The key has read permission only, or the agency is not active |
404 | not_found, iccid_not_found, booking_not_found | The package, the eSIM or the booking does not exist for your agency |
409 | duplicate_request | The same order or topup is already being processed or was just confirmed |
422 | validation_error | A query parameter has the wrong type or range (for example limit=500) |
429 | rate_limit_exceeded, concurrency_limit_exceeded | Too many requests per minute, or too many orders or topups of your key or agency being processed at the same time. Retry after the seconds in Retry-After |
500 | internal_error, booking_record_failed, balance_debit_failed, supplier_response_invalid | An error on our side; the last three come after the supplier created the eSIM |
503 | service_unavailable, balance_lock_timeout, duplicate_check_unavailable, service_busy | The supplier connection, a lock or the service is temporarily unavailable; retry in a few seconds |
When the supplier refuses an order or a topup, the response is a supplier_error with the supplier's own
HTTP status (for example 422 or 429), nothing is debited, and a failed booking is saved for
support. Usage, topup packages, topups and instructions answer 404 iccid_not_found for an ICCID that is not
an eSIM of your agency; setup instructions answer 404 booking_not_found for a booking that is not yours.
Loading answers. Right after a restart the catalog and the device list may not be loaded yet:
GET / and GET / then answer 202 with
"success": false and an empty data, and the device list can answer 503 while its fetcher starts.
This is not an error of your request: wait a few seconds and send it again.
Questions?
Email the FX-Port developer team at [email protected] for integration help, feature requests, or to report an API error.