eSIM Data Store API

Purchase eSIM plans, manage eSIMs, monitor usage, and control your wallet balance.

Base URL: https://api.esimdatastore.com/api

1. Introduction

Welcome to the eSIM Data Store API. This RESTful JSON API lets you browse eSIM plans, purchase eSIMs, monitor data usage, top up active eSIMs, and manage your prepaid wallet balance. All requests and responses use JSON and require authentication via API key.

2. Authentication

Authenticate every request by including your API key in the X-API-Key header. Each key is scoped to a single customer account — all resources you create and query are automatically isolated to your account.

Header Format

X-API-Key: your-api-key-here

API keys are provided during onboarding. If you need a key or need to rotate an existing one, contact support.

Important: Requests without a valid API key receive a 401 Unauthorized response. Keep your key secret and never expose it in client-side code.

3. Quick Start Guide

Follow these steps to make your first eSIM purchase and monitor it:

1

Browse Plans

Fetch the list of available eSIM plans with pricing and coverage details.

curl -X GET https://api.esimdatastore.com/api/plans \
  -H "X-API-Key: your-api-key"
2

Purchase a Plan

Buy an eSIM by submitting an order with the desired planId. Include an Idempotency-Key header to prevent double charges.

curl -X POST https://api.esimdatastore.com/api/orders \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{"planId": "plan-uuid-here"}'
3

Get Your eSIM

List your eSIMs, then fetch a specific eSIM to get the esimQr value for device installation.

curl -X GET https://api.esimdatastore.com/api/esims \
  -H "X-API-Key: your-api-key"
4

Monitor Usage

Check data consumption, activation time, and expiry for any eSIM.

curl -X GET https://api.esimdatastore.com/api/esims/{esim-id}/usage \
  -H "X-API-Key: your-api-key"
5

Top Up

When data is running low, check available top-up plans and add more data.

# Check available top-ups
curl -X GET https://api.esimdatastore.com/api/esims/{esim-id}/topups/available \
  -H "X-API-Key: your-api-key"

# Apply a top-up
curl -X POST https://api.esimdatastore.com/api/esims/{esim-id}/topup \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 660e8400-e29b-41d4-a716-446655440001" \
  -d '{"planId": "topup-plan-uuid"}'
6

Check Balance

View your prepaid wallet balance. Purchases are deducted automatically.

curl -X GET https://api.esimdatastore.com/api/wallets/balance \
  -H "X-API-Key: your-api-key"

4. Endpoints Reference

Plans

GET /plans

List all available eSIM plans with pricing and coverage details. Only returns plans that are not disabled, not removed from upstream, and priced for your account.

Response 200

Returns an array of PlanResponse objects:

FieldTypeDescription
idstring (UUID)Plan identifier
namestringDisplay name of the plan
descriptionstring | nullOptional description
pricestring (decimal)Price in USD, e.g. "9.99"
durationnumberValidity duration
durationUnitstringUnit of duration, e.g. "DAY"
dataAmountnumberIncluded data amount
dataAmountUnitstringUnit of data, e.g. "GB"
coveragestringCoverage region or country
provisioningFeestring (decimal)Fixed eSIM provisioning fee for NEW orders, e.g. "0.50". Top-ups are always "0.00".
Example Request
curl -X GET https://api.esimdatastore.com/api/plans \
  -H "X-API-Key: your-api-key"
Example Response
[
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Europe 5GB / 30 Days",
    "description": "High-speed data across 35 European countries",
    "price": "12.50",
    "duration": 30,
    "durationUnit": "DAY",
    "dataAmount": 5,
    "dataAmountUnit": "GB",
    "coverage": "Europe",
    "provisioningFee": "0.50"
  }
]

Orders

POST /orders Idempotent

Purchase an eSIM plan. The cost is deducted from your wallet balance.

Sandbox: Sandbox customers receive dummy eSIMs (ICCID prefix 89990) with no real provisioning.

Request Body

FieldTypeRequiredDescription
planIdstring (UUID)YesID of the plan to purchase

Headers

HeaderRequiredDescription
Idempotency-KeyRecommendedUUID to prevent duplicate purchases. Same key within 24 hours returns the cached response.

Response 201

Returns an OrderResponse object:

FieldTypeDescription
idstring (UUID)Order identifier
planIdstring (UUID)Purchased plan ID
iccidstring | nullICCID of the provisioned eSIM (null while pending)
operationTypestring"NEW" or "TOPUP"
statusstringOne of: PENDING, COMPLETED, FAILED, REFUND_PENDING, REFUNDED, REFUND_FAILED, CONFIRM_PENDING, CONFIRM_FAILED
subscriptionStatusstring | nullOne of: PENDING, ACTIVE, EXPIRED, TERMINATED, or null
planPricestring (decimal)Plan price before provisioning fee
provisioningFeestring (decimal)Provisioning fee included in salePrice ("0.00" for top-ups)
salePricestring (decimal)Total price charged for this order (planPrice + provisioningFee)
refundRequestedAtstring (ISO 8601) | nullSet when the customer accepted a refund (order entered REFUND_PENDING).
refundedAtstring (ISO 8601) | nullSet when the wallet credit + status flip committed (status is REFUNDED).
refundedAmountstring (decimal) | nullEqual to planPrice (= salePriceprovisioningFee, per FIN-32 refund policy) when status is REFUNDED; null otherwise. TOPUP refunds credit the full salePrice since provisioningFee is "0.00". Use this to display the credited amount without a separate fetch.
createdAtstring (ISO 8601)Timestamp of order creation
Example Request
curl -X POST https://api.esimdatastore.com/api/orders \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{"planId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}'
Example Response
{
  "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "planId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "iccid": null,
  "operationType": "NEW",
  "status": "PENDING",
  "subscriptionStatus": "PENDING",
  "planPrice": "12.00",
  "provisioningFee": "0.50",
  "salePrice": "12.50",
  "createdAt": "2026-04-06T14:30:00.000Z"
}
GET /orders/:id

Retrieve details for a specific order.

Path Parameters

ParameterTypeDescription
idstring (UUID)Order identifier

Response 200

Returns an OrderResponse object (same schema as POST /orders).

Example Request
curl -X GET https://api.esimdatastore.com/api/orders/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
  -H "X-API-Key: your-api-key"
GET /orders Paginated

List your orders with cursor-based pagination.

Query Parameters

ParameterTypeDefaultDescription
limitnumber50Number of results to return (max 100)
cursorstring (UUID)ID of the last order from the previous page

Response 200

Returns an array of OrderResponse objects.

Example Request
curl -X GET "https://api.esimdatastore.com/api/orders?limit=10" \
  -H "X-API-Key: your-api-key"

eSIMs

GET /esims

List all eSIMs on your account.

Response 200

Returns an array of EsimListItem objects:

FieldTypeDescription
idstring (UUID)eSIM identifier
iccidstringICCID of the eSIM
statusstringOne of: PROVISIONED, ACTIVE, EXPIRED, BLOCKED
createdAtstring (ISO 8601)Timestamp of creation

Note: The install QR (esimQr) is only available on the detail endpoint GET /esims/:id.

Example Request
curl -X GET https://api.esimdatastore.com/api/esims \
  -H "X-API-Key: your-api-key"
Example Response
[
  {
    "id": "c3d4e5f6-7890-abcd-ef12-345678901234",
    "iccid": "8901234567890123456",
    "status": "ACTIVE",
    "createdAt": "2026-04-06T14:31:00.000Z"
  }
]
GET /esims/:id

Get eSIM details with live status from the eSIM provider.

Sandbox: Sandbox eSIMs always show ACTIVE status.

Path Parameters

ParameterTypeDescription
idstring (UUID)eSIM identifier

Response 200

Returns an EsimResponse with an additional liveStatus object:

FieldTypeDescription
liveStatus.iccidstringICCID confirmed by provider
liveStatus.esimQrstringQR code data
liveStatus.statusstringCurrent provider status
Example Request
curl -X GET https://api.esimdatastore.com/api/esims/c3d4e5f6-7890-abcd-ef12-345678901234 \
  -H "X-API-Key: your-api-key"
Example Response
{
  "id": "c3d4e5f6-7890-abcd-ef12-345678901234",
  "iccid": "8901234567890123456",
  "esimQr": "LPA:1$provider.example.com$ACTIVATION_CODE",
  "status": "ACTIVE",
  "createdAt": "2026-04-06T14:31:00.000Z",
  "liveStatus": {
    "iccid": "8901234567890123456",
    "esimQr": "LPA:1$provider.example.com$ACTIVATION_CODE",
    "status": "ACTIVE"
  }
}
GET /esims/:id/usage

Get data usage, activation time, and expiry for an eSIM.

Sandbox: Sandbox returns simulated usage data.

Path Parameters

ParameterTypeDescription
idstring (UUID)eSIM identifier

Response 200

FieldTypeDescription
iccidstringICCID of the eSIM
usedAmountnumber | nullData consumed (null if not yet activated)
totalAmountnumberTotal data allowance
amountUnitstringUnit of data, e.g. "MB"
statusstringCurrent status of the eSIM subscription
activationTimestring (ISO 8601)When the eSIM was activated
expirystring (ISO 8601)When the data plan expires
Example Request
curl -X GET https://api.esimdatastore.com/api/esims/c3d4e5f6-7890-abcd-ef12-345678901234/usage \
  -H "X-API-Key: your-api-key"
Example Response
{
  "iccid": "8901234567890123456",
  "usedAmount": 1250,
  "totalAmount": 5120,
  "amountUnit": "MB",
  "status": "ACTIVE",
  "activationTime": "2026-04-06T15:00:00.000Z",
  "expiry": "2026-05-06T15:00:00.000Z"
}
GET /esims/:id/topups/available

List available top-up plans for a specific eSIM.

Sandbox: Sandbox returns all plans with customer pricing (no upstream filter).

Path Parameters

ParameterTypeDescription
idstring (UUID)eSIM identifier

Response 200

FieldTypeDescription
planIdstring (UUID)Top-up plan identifier
namestringDisplay name of the top-up plan
durationnumberValidity duration
durationUnitstringUnit of duration
amountnumberData amount included
amountUnitstringUnit of data
coveragestring[]List of covered regions/countries
pricestring (decimal)Price in USD
provisioningFeestring (decimal)Provisioning fee (always "0.00" for top-ups)
Example Request
curl -X GET https://api.esimdatastore.com/api/esims/c3d4e5f6-7890-abcd-ef12-345678901234/topups/available \
  -H "X-API-Key: your-api-key"
Example Response
[
  {
    "planId": "b2c3d4e5-f678-90ab-cdef-123456789012",
    "name": "Europe 3GB Top-Up",
    "duration": 30,
    "durationUnit": "DAY",
    "amount": 3,
    "amountUnit": "GB",
    "coverage": ["Europe"],
    "price": "8.00",
    "provisioningFee": "0.00"
  }
]
POST /esims/:id/topup Idempotent

Top up an existing eSIM with additional data.

Sandbox: Sandbox top-ups simulate upstream without real provisioning.

Path Parameters

ParameterTypeDescription
idstring (UUID)eSIM identifier

Request Body

FieldTypeRequiredDescription
planIdstring (UUID)YesID of the top-up plan (from available top-ups)

Headers

HeaderRequiredDescription
Idempotency-KeyRecommendedUUID to prevent duplicate top-ups

Response 201

Returns an OrderResponse object (same schema as POST /orders, with operationType: "TOPUP").

Example Request
curl -X POST https://api.esimdatastore.com/api/esims/c3d4e5f6-7890-abcd-ef12-345678901234/topup \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 770e8400-e29b-41d4-a716-446655440002" \
  -d '{"planId": "b2c3d4e5-f678-90ab-cdef-123456789012"}'
Example Response
{
  "id": "d5e6f7a8-b901-2345-cdef-678901234567",
  "planId": "b2c3d4e5-f678-90ab-cdef-123456789012",
  "iccid": "8901234567890123456",
  "operationType": "TOPUP",
  "status": "PENDING",
  "subscriptionStatus": "ACTIVE",
  "planPrice": "8.00",
  "provisioningFee": "0.00",
  "salePrice": "8.00",
  "createdAt": "2026-04-06T16:00:00.000Z"
}

Wallet

GET /wallets/balance

Get your current prepaid wallet balance.

Response 200

FieldTypeDescription
balancestring (decimal)Current balance in USD, e.g. "150.00"
Example Request
curl -X GET https://api.esimdatastore.com/api/wallets/balance \
  -H "X-API-Key: your-api-key"
Example Response
{
  "balance": "150.00"
}

Refunds

POST /orders/:id/refund

Accepts a refund request (async). Returns 202 Accepted. The wallet credit and the REFUNDED status flip commit atomically only after upstream termination succeeds. Poll GET /orders/:id until status is REFUNDED (success) or REFUND_FAILED (60-day window missed or admin-forced).

Sandbox: Sandbox refunds skip real upstream termination and commit inline.

Path Parameters

ParameterTypeDescription
idstring (UUID)Order identifier

Response 202

FieldTypeDescription
orderIdstring (UUID)The order being refunded
statusstringREFUND_PENDING when the inline upstream call did not complete in time; REFUNDED when wallet credit + status flip committed
refundRequestedAtstring (ISO 8601)Always present — when the refund request was accepted
refundedAtstring (ISO 8601) | nullSet only when status === REFUNDED
refundedAmountstring (decimal) | nullEqual to the order's planPrice (= salePriceprovisioningFee, per FIN-32 refund policy) when status === REFUNDED; null while pending or failed. TOPUP refunds credit the full salePrice since provisioningFee is "0.00". Same value as OrderResponse.refundedAmount on subsequent GET /orders/:id calls.
Example Request
curl -X POST https://api.esimdatastore.com/api/orders/f47ac10b-58cc-4372-a567-0e02b2c3d479/refund \
  -H "X-API-Key: your-api-key"
Example Response — inline commit

NEW order with salePrice $15.00 and provisioningFee $0.50 — refund credit equals planPrice $14.50.

{
  "orderId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "status": "REFUNDED",
  "refundRequestedAt": "2026-05-13T12:00:00.000Z",
  "refundedAt": "2026-05-13T12:00:01.234Z",
  "refundedAmount": "14.50"
}
Example Response — async (worker will commit)
{
  "orderId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "status": "REFUND_PENDING",
  "refundRequestedAt": "2026-05-13T12:00:00.000Z",
  "refundedAt": null,
  "refundedAmount": null
}
Polling pattern
# Poll GET /orders/:id every ~2s until terminal state.
for i in {1..30}; do
  STATUS=$(curl -s "$API/orders/$ORDER_ID" \
    -H "X-API-Key: $API_KEY" | jq -r .status)
  echo "poll #$i: $STATUS"
  [[ "$STATUS" == "REFUNDED" || "$STATUS" == "REFUND_FAILED" ]] && break
  sleep 2
done
POST /admin/orders/:id/refund/force-complete

Admin-only. Bypasses the upstream-termination wait and commits the wallet credit + flips the order to REFUNDED. Use this when an out-of-band signal (eSIMfx support ticket, manual termination) confirms the upstream subscription is terminated but the worker hasn't yet caught up. Requires an admin bearer token; an Idempotency-Key header is strongly recommended (see Headers below). Emits REFUND_FORCED_BY_ADMIN with the supplied reason.

Headers

  • Authorization: Bearer <ADMIN_TOKEN> — required
  • Idempotency-Key: <uuid> — strongly recommended. The interceptor treats a missing key as a normal one-shot call (no replay protection); when present, the admin idempotency scope is keyed by method + url + key, so retries with the same key replay the cached result and using the same key against the other admin endpoint or a different order is collision-free.

Request Body

{ "reason": "eSIMfx ticket #12345 — termination confirmed" }

reason is required, 1–500 chars. The string lands in REFUND_FORCED_BY_ADMIN as the operator's audit record.

Response 200

Same shape as POST /orders/:id/refund's response ({ orderId, status, refundRequestedAt, refundedAt, refundedAmount }) — status is REFUNDED and refundedAmount is populated.

POST /admin/orders/:id/refund/force-fail

Admin-only. Flips the order to REFUND_FAILED without crediting the wallet. Use this for confirmed duplicates, fraud, or refund requests that should never have been accepted. The customer is not credited — handle remediation out of band. Same auth + idempotency semantics as force-complete (admin bearer required; Idempotency-Key recommended but not enforced). Emits REFUND_FORCED_BY_ADMIN.

Headers

  • Authorization: Bearer <ADMIN_TOKEN> — required
  • Idempotency-Key: <uuid> — strongly recommended (same semantics as force-complete: missing key is a one-shot call; present key replays the cached result on retry).

Request Body

{ "reason": "confirmed duplicate refund — support ticket #4321" }

Response 200

Same shape as POST /orders/:id/refund's response — status is REFUND_FAILED and refundedAmount is null.

Reports

Admin / Report token only. JSON / CSV / PDF financial export, scoped by month. Bearer-token auth on either ADMIN_TOKEN or the read-only ADMIN_REPORT_TOKEN.

GET /reports/financial

Generate a financial report for a given month, optionally filtered to a subset of sections, in JSON / CSV / PDF.

Headers

  • Authorization: Bearer <ADMIN_TOKEN | ADMIN_REPORT_TOKEN> — required.

Query Parameters

ParameterTypeDescription
monthstring (YYYY-MM)Required. Reporting month, UTC.
sectionsstringComma-separated subset of revenue, profitability, customers. Defaults to all three.
formatstringjson (default), csv, or pdf.

Response 200

JSON response shape (CSV / PDF formats serialize the same fields). The revenue, revenue.daily[], and profitability.plans[] objects each carry three FIN-32 net-of-upstream-refund fields: upstreamRefundedAmount, netUpstreamCost, netMargin.

FieldTypeDescription
monthstringEchoes the month query parameter
generatedAtstring (ISO 8601)Timestamp the report was rendered
revenue.grossRevenuestring (decimal)Sum of salePrice across orders in the month
revenue.totalUpstreamCoststring (decimal)Gross upstream cost (no refund netting)
revenue.grossMarginstring (decimal)grossRevenue − totalUpstreamCost
revenue.marginPercentstring (decimal)Gross margin as a percentage
revenue.refundedAmountstring (decimal)Sum of customer-side refund credits (= planPrice)
revenue.refundCountnumberRefunded-order count
revenue.netRevenuestring (decimal)grossRevenue − refundedAmount
revenue.upstreamRefundedAmountstring (decimal)Sum of orders.upstream_refunded_amount — upstream cost reclaimed via refunds
revenue.netUpstreamCoststring (decimal)totalUpstreamCost − upstreamRefundedAmount
revenue.netMarginstring (decimal)netRevenue − netUpstreamCost — true after-refund margin
revenue.orderCounts{ new, topup }Per-operation-type order count
revenue.daily[]arrayPer-day breakdown. Same three net fields are present on every day. Bucketed by order created_at date, not refund commit date.
profitability.plans[]arrayPer-plan profitability. Carries orderCount, refundCount, grossRevenue, refundedAmount, netRevenue, grossMargin, unitMargin, plus the three FIN-32 net fields.
customers[]arrayPer-customer spend rollup (revenue side only — no upstream/cost fields)
Example Request
curl -H "Authorization: Bearer $ADMIN_REPORT_TOKEN" \
  "https://api.esimdatastore.com/api/reports/financial?month=2026-03§ions=revenue,profitability"
Example Response — abbreviated revenue + profitability
{
  "month": "2026-03",
  "generatedAt": "2026-04-03T12:00:00.000Z",
  "revenue": {
    "grossRevenue": "40.00",
    "totalUpstreamCost": "24.00",
    "grossMargin": "16.00",
    "marginPercent": "40.00",
    "refundedAmount": "5.00",
    "refundCount": 1,
    "netRevenue": "35.00",
    "upstreamRefundedAmount": "3.00",
    "netUpstreamCost": "21.00",
    "netMargin": "14.00",
    "orderCounts": { "new": 2, "topup": 1 },
    "daily": [
      {
        "date": "2026-03-01",
        "grossRevenue": "20.00",
        "upstreamCost": "12.00",
        "grossMargin": "8.00",
        "refundedAmount": "0.00",
        "refundCount": 0,
        "orders": 1,
        "upstreamRefundedAmount": "0.00",
        "netUpstreamCost": "12.00",
        "netMargin": "8.00"
      }
    ]
  },
  "profitability": {
    "plans": [
      {
        "planId": "019db811-6b8f-70a7-96a1-f30b7bf45523",
        "planName": "EU 5GB",
        "orderCount": 3,
        "refundCount": 1,
        "grossRevenue": "40.00",
        "refundedAmount": "5.00",
        "netRevenue": "35.00",
        "grossMargin": "16.00",
        "unitMargin": "5.33",
        "upstreamRefundedAmount": "3.00",
        "netUpstreamCost": "21.00",
        "netMargin": "14.00"
      }
    ]
  }
}
CSV / PDF: The CSV and PDF exporters serialize the same fields. CSV adds Upstream Refunded Amount, Net Upstream Cost, and Net Margin columns alongside the existing rollup columns; PDF mirrors them inline in the daily breakdown and per-plan tables.

5. Idempotency

To prevent duplicate charges, include an Idempotency-Key header with a unique value (UUID recommended) on supported endpoints.

Supported Endpoints

  • POST /orders
  • POST /esims/:id/topup

How It Works

Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Tip: Always use idempotency keys for purchase and top-up operations. Network retries without an idempotency key can result in double charges.

6. Pagination

The GET /orders endpoint supports cursor-based pagination. Pass the id of the last order from the previous page as the cursor query parameter.

ParameterTypeDefaultDescription
limitnumber50Results per page (max 100)
cursorstring (UUID)Last order ID from previous page

Example: Paginating Through Results

# First page
curl -X GET "https://api.esimdatastore.com/api/orders?limit=10" \
  -H "X-API-Key: your-api-key"

# Response includes 10 orders, last one has id "abc123..."

# Next page — pass last order's id as cursor
curl -X GET "https://api.esimdatastore.com/api/orders?limit=10&cursor=abc123-def456-..." \
  -H "X-API-Key: your-api-key"

# Continue until an empty array is returned
Note: When the response returns an empty array, you have reached the end of the results.

7. Error Handling

All errors return a consistent JSON structure with a status code, message, and optional field-level errors.

Error Format

All error responses include statusCode, message, and an error code.

{
  "statusCode": 409,
  "message": "Duplicate request in progress",
  "error": "CONFLICT"
}

Validation errors (422) include per-field details in the message array:

{
  "statusCode": 422,
  "message": [
    { "property": "planId", "constraints": { "isUuid": "planId must be a UUID" } }
  ],
  "error": "Unprocessable Entity"
}

Status Codes

CodeMeaningDescription
400Bad RequestInvalid path parameter format (e.g. non-UUID ID)
401UnauthorizedMissing or invalid API key
404Not FoundResource does not exist or belongs to another account
409ConflictDuplicate idempotency key with an in-flight request
422Unprocessable EntityValidation error (request body failed validation)
429Too Many RequestsRate limit exceeded
502Bad GatewayeSIM provider error — retry recommended
503Service UnavailableeSIM provider temporarily unavailable — retry after delay
Note: Validation errors return 422 Unprocessable Entity, not 400 Bad Request. Only malformed path parameters (e.g. a non-UUID string where a UUID is expected) return 400.

Example Error Responses

401 Unauthorized
{
  "statusCode": 401,
  "message": "Unauthorized"
}
409 Conflict (Duplicate Idempotency Key)
{
  "statusCode": 409,
  "message": "Duplicate request in progress",
  "error": "CONFLICT"
}
422 Unprocessable Entity (Validation Error)
{
  "statusCode": 422,
  "message": [
    {
      "property": "planId",
      "constraints": { "isUuid": "planId must be a UUID" }
    }
  ],
  "error": "Unprocessable Entity"
}

8. Rate Limits

Requests are rate-limited per API key. When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait.

CategoryEndpointsLimit
Standard GET /plans, GET /orders, GET /esims, GET /wallets/balance 30 requests / minute
Sensitive GET /esims/:id, GET /esims/:id/usage, GET /esims/:id/topups/available, POST /orders, POST /esims/:id/topup 10 requests / minute
Refunds POST /orders/:id/refund 5 requests / minute
Best practice: Implement exponential backoff when you receive a 429 response. Check the Retry-After header for the recommended wait time.