Skip to content
FX-Port/Docs
GuidesAPI referenceChangelog
Get API keys
  • Introduction
  • Quickstart
  • Security
  • Authentication
  • Environments and Postman
  • Sandbox
  • Conventions and naming
  • Legacy names and twins
  • Errors
  • Rate limits and fair use
  • Streaming search results

Concepts

  • Overview
  • Pricing
  • Supplier continuity

Catalog and orders

  • Package catalog
  • Create an order
  • Top up

After the order

  • Usage
  • Bookings
  • Setup instructions
  • Use these docs with AI

eSIMs · After the order

eSIM usage

Retrieve the remaining data, voice and SMS balance of one eSIM or of up to 50 eSIMs.

Usage is cached and is not real time. FX-Port keeps the last usage of an eSIM for up to 15 minutes to protect the supplier's rate limits. Pass force=true to ask the supplier again when you need the freshest number (for example right after a topup), but do not do it on every request: the supplier limits how often usage can be read and rejects clients that poll too often.

Single eSIM usage

GET /api/v1/esims/usage/{iccid}

Permission: Read

The ICCID is 18 to 22 digits (it may be written in groups of spaces or hyphens, 8944 4654 0000 0123 456: the digits are read) and must belong to an eSIM your agency ordered in this environment. An ICCID that is unknown, or that belongs to another agency, is answered 404 iccid_not_found.

curl --request GET \
  --url 'https://api.fx-port.com/api/v1/esims/usage/8900000540463077957' \
  --header 'Authorization: Bearer fxp_test_YOUR_KEY'
const iccid = "8900000540463077957";
const response = await fetch(`https://api.fx-port.com/api/v1/esims/usage/${iccid}`, {
  headers: { Authorization: "Bearer fxp_test_YOUR_KEY" },
});
const body = await response.json();
const { remaining, total, expired_at, status } = body.data.data; // one level deeper than most answers
import requests

iccid = "8900000540463077957"
response = requests.get(
    f"https://api.fx-port.com/api/v1/esims/usage/{iccid}",
    headers={"Authorization": "Bearer fxp_test_YOUR_KEY"},
    timeout=60,
)
usage = response.json()["data"]["data"]
Query parameterDescription
forcetrue skips the 15-minute cache and asks the supplier again. An empty value means not sent

Response example

{
  "environment": "sandbox",
  "success": true,
  "data": {
    "data": {
      "remaining": 1024,
      "total": 1024,
      "expired_at": "2026-11-08 15:23:29",
      "is_unlimited": false,
      "status": "ACTIVE",
      "remaining_voice": 0,
      "remaining_text": 0,
      "total_voice": 0,
      "total_text": 0
    },
    "meta": { "message": "success" }
  }
}

An answer that comes from the cache adds two keys at the top level: "cached": true and "cache_age_seconds": 412 (the age of the answer in seconds). A fresh answer has neither.

Fields of data.data

FieldMeaning
remaining, totalData left and data allowance, in megabytes
expired_atWhen the eSIM expires: YYYY-MM-DD HH:MM:SS in UTC, without a zone
is_unlimitedtrue for an unlimited eSIM
statusThe state, as the supplier reports it: ACTIVE (in use), NOT_ACTIVE (not installed or not used yet), FINISHED (the allowance is used up) or EXPIRED (the validity ended)
remaining_voice, remaining_text, total_voice, total_textVoice minutes and SMS left and included. 0 for data-only packages

When the supplier cannot answer

If the supplier is rate limiting or unreachable and FX-Port has a previous answer for this eSIM, you get that answer, marked as stale, with status 200:

{
  "success": true,
  "data": { "data": { "remaining": 1024, "total": 1024, "...": "..." }, "meta": { "message": "success" } },
  "cached": true,
  "stale": true,
  "stale_reason": "Rate limited",
  "retry_after": 60
}

stale_reason says why in a few words and retry_after (seconds, present when the supplier was rate limiting) says when to ask again. Without a previous answer the call fails instead: 429 rate_limit_exceeded with retry_after, 503 service_unavailable when the supplier connection is down, or the supplier's own status with the message "Failed to fetch usage".

In the sandbox, a new eSIM shows its full allowance and the status ACTIVE, and the usage does not change after a topup.

Bulk usage

GET /api/v1/esims/usage/bulk

Permission: Read

Returns the usage of several eSIMs in one call, for a list or a dashboard. Always send iccids with the ICCIDs you show: the supplier is asked one eSIM after another, so a long list that is not cached takes seconds.

curl --request GET \
  --url 'https://api.fx-port.com/api/v1/esims/usage/bulk?iccids=8900000540463077957,8900000540463077965' \
  --header 'Authorization: Bearer fxp_test_YOUR_KEY'
const iccids = ["8900000540463077957", "8900000540463077965"];
const response = await fetch(`https://api.fx-port.com/api/v1/esims/usage/bulk?iccids=${iccids.join(",")}`, {
  headers: { Authorization: "Bearer fxp_test_YOUR_KEY" },
});
const { data: entries } = await response.json();
for (const entry of entries) console.log(entry.iccid, entry.status, entry.data?.data.remaining);
Query parameterDescription
iccidsICCIDs separated by commas (semicolons and line breaks work too): at most 50 for an API key (400 too_many_iccids above that), each 18 to 22 digits (400 invalid_iccid otherwise). The parameter may also be repeated (iccids=a&iccids=b) or written iccids[]=a&iccids[]=b, and an ICCID may be written in groups of spaces or hyphens. Without it, the 50 newest eSIM orders of your agency in this environment are read
forcetrue skips the cache for every eSIM. An empty value means not sent
  • One entry per ICCID, in the order you asked. An eSIM that was topped up still appears once.
  • An ICCID that is not an eSIM of your agency is left out of the answer, without an error.
  • The same 15-minute cache and stale answers as the single call apply per eSIM.

Response example

{
  "environment": "sandbox",
  "success": true,
  "data": [
    {
      "iccid": "8900000540463077957",
      "data": {
        "data": {
          "remaining": 1024,
          "total": 1024,
          "expired_at": "2026-11-08 15:23:29",
          "is_unlimited": false,
          "status": "ACTIVE",
          "remaining_voice": 0,
          "remaining_text": 0,
          "total_voice": 0,
          "total_text": 0
        },
        "meta": { "message": "success" }
      },
      "status": "success"
    }
  ]
}

Each entry has its own status. Read it per entry: the call answers 200 even when some entries failed.

Entry statusMeaningdata
successFresh or cached usageThe usage, one level deeper than the single call: data.data
staleThe supplier could not answer: the last known usageThe last known usage
rate_limitedThe supplier is rate limiting and there is no previous answer. retry_after says when to ask againnull
errorThe usage could not be read. error is usage_unavailable after an unexpected failurenull
PENDINGThe order has no eSIM yet: its ICCID starts with PENDING-null

Retry rate_limited entries later instead of calling bulk usage again at once.

Errors

HTTPcodeCause
400invalid_iccidAn ICCID is not 18 to 22 digits
400too_many_iccidsMore than 50 ICCIDs in iccids (bulk)
401authentication_required, invalid_api_keyMissing or invalid API key
404iccid_not_foundThe ICCID is not an eSIM of your agency in this environment (single usage)
429rate_limit_exceededOver 6,000 requests per minute for this key, or the supplier is rate limiting and there is no previous answer (retry_after)
503service_unavailable, service_busyThe supplier connection or the service is busy and there is no previous answer. Retry in a few seconds
PreviousTop up an eSIMNext eSIM bookings

On this page

  • Single eSIM usage
  • Response example
  • Fields of data.data
  • When the supplier cannot answer
  • Bulk usage
  • Response example
  • Errors

© 2026 FX-Port

TermsPrivacy[email protected]