eSIM Data Store API
Purchase eSIM plans, manage eSIMs, monitor usage, and control your wallet balance.
Table of Contents
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.
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:
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"
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"}'
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"
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"
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"}'
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
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:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Plan identifier |
name | string | Display name of the plan |
description | string | null | Optional description |
price | string (decimal) | Price in USD, e.g. "9.99" |
duration | number | Validity duration |
durationUnit | string | Unit of duration, e.g. "DAY" |
dataAmount | number | Included data amount |
dataAmountUnit | string | Unit of data, e.g. "GB" |
coverage | string | Coverage region or country |
provisioningFee | string (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
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
| Field | Type | Required | Description |
|---|---|---|---|
planId | string (UUID) | Yes | ID of the plan to purchase |
Headers
| Header | Required | Description |
|---|---|---|
Idempotency-Key | Recommended | UUID to prevent duplicate purchases. Same key within 24 hours returns the cached response. |
Response 201
Returns an OrderResponse object:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Order identifier |
planId | string (UUID) | Purchased plan ID |
iccid | string | null | ICCID of the provisioned eSIM (null while pending) |
operationType | string | "NEW" or "TOPUP" |
status | string | One of: PENDING, COMPLETED, FAILED, REFUND_PENDING, REFUNDED, REFUND_FAILED, CONFIRM_PENDING, CONFIRM_FAILED |
subscriptionStatus | string | null | One of: PENDING, ACTIVE, EXPIRED, TERMINATED, or null |
planPrice | string (decimal) | Plan price before provisioning fee |
provisioningFee | string (decimal) | Provisioning fee included in salePrice ("0.00" for top-ups) |
salePrice | string (decimal) | Total price charged for this order (planPrice + provisioningFee) |
refundRequestedAt | string (ISO 8601) | null | Set when the customer accepted a refund (order entered REFUND_PENDING). |
refundedAt | string (ISO 8601) | null | Set when the wallet credit + status flip committed (status is REFUNDED). |
refundedAmount | string (decimal) | null | Equal to planPrice (= salePrice − provisioningFee, 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. |
createdAt | string (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"
}
Retrieve details for a specific order.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (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"
List your orders with cursor-based pagination.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Number of results to return (max 100) |
cursor | string (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
List all eSIMs on your account.
Response 200
Returns an array of EsimListItem objects:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | eSIM identifier |
iccid | string | ICCID of the eSIM |
status | string | One of: PROVISIONED, ACTIVE, EXPIRED, BLOCKED |
createdAt | string (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 eSIM details with live status from the eSIM provider.
Sandbox: Sandbox eSIMs always show ACTIVE status.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | eSIM identifier |
Response 200
Returns an EsimResponse with an additional liveStatus object:
| Field | Type | Description |
|---|---|---|
liveStatus.iccid | string | ICCID confirmed by provider |
liveStatus.esimQr | string | QR code data |
liveStatus.status | string | Current 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 data usage, activation time, and expiry for an eSIM.
Sandbox: Sandbox returns simulated usage data.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | eSIM identifier |
Response 200
| Field | Type | Description |
|---|---|---|
iccid | string | ICCID of the eSIM |
usedAmount | number | null | Data consumed (null if not yet activated) |
totalAmount | number | Total data allowance |
amountUnit | string | Unit of data, e.g. "MB" |
status | string | Current status of the eSIM subscription |
activationTime | string (ISO 8601) | When the eSIM was activated |
expiry | string (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"
}
List available top-up plans for a specific eSIM.
Sandbox: Sandbox returns all plans with customer pricing (no upstream filter).
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | eSIM identifier |
Response 200
| Field | Type | Description |
|---|---|---|
planId | string (UUID) | Top-up plan identifier |
name | string | Display name of the top-up plan |
duration | number | Validity duration |
durationUnit | string | Unit of duration |
amount | number | Data amount included |
amountUnit | string | Unit of data |
coverage | string[] | List of covered regions/countries |
price | string (decimal) | Price in USD |
provisioningFee | string (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"
}
]
Top up an existing eSIM with additional data.
Sandbox: Sandbox top-ups simulate upstream without real provisioning.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | eSIM identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
planId | string (UUID) | Yes | ID of the top-up plan (from available top-ups) |
Headers
| Header | Required | Description |
|---|---|---|
Idempotency-Key | Recommended | UUID 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 your current prepaid wallet balance.
Response 200
| Field | Type | Description |
|---|---|---|
balance | string (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
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
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Order identifier |
Response 202
| Field | Type | Description |
|---|---|---|
orderId | string (UUID) | The order being refunded |
status | string | REFUND_PENDING when the inline upstream call did not complete in time; REFUNDED when wallet credit + status flip committed |
refundRequestedAt | string (ISO 8601) | Always present — when the refund request was accepted |
refundedAt | string (ISO 8601) | null | Set only when status === REFUNDED |
refundedAmount | string (decimal) | null | Equal to the order's planPrice (= salePrice − provisioningFee, 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
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>— requiredIdempotency-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 bymethod + 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.
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>— requiredIdempotency-Key: <uuid>— strongly recommended (same semantics asforce-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.
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
| Parameter | Type | Description |
|---|---|---|
month | string (YYYY-MM) | Required. Reporting month, UTC. |
sections | string | Comma-separated subset of revenue, profitability, customers. Defaults to all three. |
format | string | json (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.
| Field | Type | Description |
|---|---|---|
month | string | Echoes the month query parameter |
generatedAt | string (ISO 8601) | Timestamp the report was rendered |
revenue.grossRevenue | string (decimal) | Sum of salePrice across orders in the month |
revenue.totalUpstreamCost | string (decimal) | Gross upstream cost (no refund netting) |
revenue.grossMargin | string (decimal) | grossRevenue − totalUpstreamCost |
revenue.marginPercent | string (decimal) | Gross margin as a percentage |
revenue.refundedAmount | string (decimal) | Sum of customer-side refund credits (= planPrice) |
revenue.refundCount | number | Refunded-order count |
revenue.netRevenue | string (decimal) | grossRevenue − refundedAmount |
revenue.upstreamRefundedAmount | string (decimal) | Sum of orders.upstream_refunded_amount — upstream cost reclaimed via refunds |
revenue.netUpstreamCost | string (decimal) | totalUpstreamCost − upstreamRefundedAmount |
revenue.netMargin | string (decimal) | netRevenue − netUpstreamCost — true after-refund margin |
revenue.orderCounts | { new, topup } | Per-operation-type order count |
revenue.daily[] | array | Per-day breakdown. Same three net fields are present on every day. Bucketed by order created_at date, not refund commit date. |
profitability.plans[] | array | Per-plan profitability. Carries orderCount, refundCount, grossRevenue, refundedAmount, netRevenue, grossMargin, unitMargin, plus the three FIN-32 net fields. |
customers[] | array | Per-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"
}
]
}
}
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
- If a request with the same
Idempotency-Keyis received within 24 hours, the API returns the original cached response without creating a duplicate order. - If a duplicate request arrives while the original is still being processed, the API returns
409 Conflict. - Each key should be a UUID. Reusing keys across different request bodies may produce unexpected results.
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
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.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Results per page (max 100) |
cursor | string (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
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
| Code | Meaning | Description |
|---|---|---|
400 | Bad Request | Invalid path parameter format (e.g. non-UUID ID) |
401 | Unauthorized | Missing or invalid API key |
404 | Not Found | Resource does not exist or belongs to another account |
409 | Conflict | Duplicate idempotency key with an in-flight request |
422 | Unprocessable Entity | Validation error (request body failed validation) |
429 | Too Many Requests | Rate limit exceeded |
502 | Bad Gateway | eSIM provider error — retry recommended |
503 | Service Unavailable | eSIM provider temporarily unavailable — retry after delay |
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.
| Category | Endpoints | Limit |
|---|---|---|
| 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 |
Retry-After header for the recommended wait time.