Get All Packages
Retrieve available eSIM packages with your pricing.
Endpoint
GET /api/v1/business/esims/packages
AI prompt — Get All Packages
Paste into ChatGPT, Claude, Cursor, Copilot or any coding agent to generate this part of your integration. Built-in recommendation: Sync the catalogue into your own database every 6–12 h (limit=100, ~70 paced requests) and serve your storefront from it — never proxy this endpoint.
Show prompt text (70 lines)
# eSIMfly Business API — Get All Packages (GET /esims/packages) — prompt for AI coding assistants
TASK: build a CATALOGUE SYNC that copies the eSIMfly package list into our own database on a
schedule, and serve our storefront from that database. Do NOT call this endpoint per customer
request, per search, or per page view. Do NOT proxy it.
WHY: the endpoint assembles and prices the entire multi-provider catalogue (7,000+ packages) on
every call and paginates in memory. A local copy is faster for customers, keeps us far inside the
rate limits, and is much cheaper for eSIMfly. Prices only change occasionally, and the order
endpoint always charges the current price anyway (you only send packageCode).
COMMON RULES (apply to every eSIMfly request)
- Base URL: https://esimfly.net/api/v1/business
- Headers on every call: RT-AccessCode (esf_...), RT-RequestID (fresh UUID v4 per request; reuse -> 400 DUPLICATE_REQUEST),
RT-Timestamp (ms since epoch; >5 min old -> 401 INVALID_TIMESTAMP),
RT-Signature = UPPERCASE hex HMAC-SHA256(secretKey, timestamp + requestId + accessCode + rawBody).
rawBody = "" for GET; for POST/PUT sign the exact body string you send, with Content-Type: application/json.
- Keep access code + secret key server-side in env vars. Never ship them to a browser or mobile app.
- Responses: { success: true, ... } or { success: false, error|message, code }. Branch on `success` and `code`.
- Rate limits are per API key, shown in the business dashboard (typically 100/minute, 1,000/hour, 10,000/day) -> RATE_LIMIT_EXCEEDED. Pace bulk work at <= 1 req/s.
- Read `currency` from responses (USD | IQD | EUR for enterprise). Never hard-code it. IQD amounts are integers.
- Package codes are opaque strings: store and send back verbatim, never parse or prefix them.
- Node.js/TypeScript: use the official SDK instead of raw HTTP — `npm install @esimfly/sdk`
(https://github.com/eSimfly-Official/esimfly-sdk-nodejs); it implements these rules. Other languages: implement the contract below.
- Full multi-endpoint prompt: https://docs.esimfly.net/llm/esimfly-api-full-prompt.txt
ENDPOINT
GET https://esimfly.net/api/v1/business/esims/packages?page=1&limit=100
Query params: page (default 1), limit (default 50, max 100),
type = local | regional | global (optional; enterprise self-service accounts get "enterprise"),
search = country/region name (optional; ad-hoc lookups only — NOT for the sync).
RESPONSE 200
{ "success": true, "data": { "packages": [Package, ...],
"pagination": { "page": 1, "limit": 100, "total": 7123, "total_pages": 72 } } }
Package = {
package_code: string (OPAQUE — "PHAJHEAYP", "1654977", "ent_1234567", "merhaba-7days-1gb"),
name, region, type: "local"|"regional"|"global"|"enterprise",
data_amount_gb: number, validity_days: int, cost: number (YOUR buy price), currency: "USD"|"IQD"|"EUR",
features: { voice_minutes, sms_count, is_rechargeable }, is_unlimited, has_voice, has_sms,
countries?: ["AT","BE",...] // ISO alpha-2, eSIMfly packages — full coverage list for regional/global
locationNetworkList: [{ locationName, locationLogo, operatorList: [{ operatorName, networkType }] }],
networks?: [{ network_id, network_name, network_type, country_code, country_name, country_iso2, continent, mcc_code, mnc_code }],
// O2 / Vodafone / Bouygues extras (optional): phone_number, network_carrier, has_5g, has_hotspot, can_extend,
// is_recurring, activation_policy: "immediate"|"first_use", data_breakdown, voice_details
}
Notes: only packages >= 1 GB are returned; sorted local -> regional -> global; duplicate unlimited
plans are collapsed to the cheapest per duration.
SYNC ALGORITHM (implement exactly)
1. Job runs every 6–12 hours (cron) and on demand from an admin "Sync now" button. Use a lock so
two syncs never overlap.
2. run_started_at = now(). page = 1. Loop:
GET /esims/packages?page={page}&limit=100 (fresh RT-RequestID each time)
upsert every package by package_code, set last_seen_at = run_started_at, is_active = true
sleep 1000 ms; page++ ; stop when page > data.pagination.total_pages
~72 requests for ~7,000 packages. On RATE_LIMIT_EXCEEDED wait 60 s and retry the same page (max 3).
3. If EVERY page succeeded: UPDATE packages SET is_active = false WHERE last_seen_at < run_started_at.
If any page failed: skip this step, keep the previous catalogue live, alert an admin.
4. NEVER DELETE package rows — orders, eSIMs and invoices reference package_code.
5. Store JSON columns for features, countries, locationNetworkList, networks and provider extras.
Add our own columns: sell_price (cost + margin, recomputed after each sync), is_active,
last_seen_at, synced_at, and a search index on name/region/countries.
6. Storefront reads (listing, search, country filter, price) hit OUR database only.
7. Keep `cost` as the authoritative buy price; margin lives in our DB. When you show a price,
use sell_price; when you order, send only package_code (the API charges current cost).
DELIVERABLE: migration for the packages table, the paced sync job (with lock + abort-safe
deactivation), an admin trigger, and repository functions the storefront uses instead of the API.
Authentication
This endpoint requires HMAC authentication. See Authentication for details.
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
| RT-AccessCode | String | Yes | Your API access code |
| RT-RequestID | String | Yes | Unique request ID (UUID v4) |
| RT-Timestamp | String | Yes | Request timestamp in milliseconds |
| RT-Signature | String | Yes | HMAC-SHA256 signature |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| search | String | - | Search packages by country name or destination |
| type | String | - | Filter by package type: "local", "regional", or "global". Enterprise self-service accounts return "enterprise" instead — see Enterprise accounts |
| page | Integer | 1 | Page number for pagination |
| limit | Integer | 50 | Number of results per page (max 100) |
Response
Success Response (200 OK)
USD User Example:
{
"success": true,
"data": {
"packages": [
{
"package_code": "PHAJHEAYP",
"name": "United States 1GB 7Days",
"region": "United States",
"type": "local",
"data_amount_gb": 1,
"validity_days": 7,
"cost": 1.44,
"currency": "USD",
"features": {
"voice_minutes": 0,
"sms_count": 0,
"is_rechargeable": true
},
"is_unlimited": false,
"has_voice": false,
"has_sms": false,
"countries": ["US"],
"locationNetworkList": [
{
"locationName": "United States",
"locationLogo": "/images/flags/us.png",
"operatorList": [
{
"operatorName": "Verizon",
"networkType": "5G"
},
{
"operatorName": "T-Mobile",
"networkType": "5G"
}
]
}
],
"networks": [
{
"network_id": 310,
"network_name": "Verizon",
"network_type": "5G",
"country_code": "us",
"country_name": "United States",
"country_iso2": "us",
"continent": "North America",
"mcc_code": "311",
"mnc_code": "480"
},
{
"network_id": 311,
"network_name": "T-Mobile",
"network_type": "5G",
"country_code": "us",
"country_name": "United States",
"country_iso2": "us",
"continent": "North America",
"mcc_code": "310",
"mnc_code": "260"
}
]
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 245,
"total_pages": 5
}
}
}
IQD User Example:
{
"success": true,
"data": {
"packages": [
{
"package_code": "PHAJHEAYP",
"name": "United States 1GB 7Days",
"region": "United States",
"type": "local",
"data_amount_gb": 1,
"validity_days": 7,
"cost": 1901,
"currency": "IQD",
"features": {
"voice_minutes": 0,
"sms_count": 0,
"is_rechargeable": true
},
"is_unlimited": false,
"has_voice": false,
"has_sms": false,
"locationNetworkList": [
{
"locationName": "United States",
"locationLogo": "/images/flags/us.png",
"operatorList": [
{
"operatorName": "Verizon",
"networkType": "5G"
}
]
}
]
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 245,
"total_pages": 5
}
}
}
Package Fields
| Field | Type | Description |
|---|---|---|
| package_code | String | Unique package identifier. Treat it as an opaque string — formats vary by provider (for example "PHAJHEAYP", "1654977", or "ent_1234567" for an enterprise account's own package). Pass it back exactly as received |
| name | String | Package display name |
| region | String | Region or country name |
| type | String | Package type: "local", "regional", "global", or "enterprise" for an enterprise self-service account's own packages |
| data_amount_gb | Number | Data allowance in GB |
| validity_days | Integer | Validity period in days |
| cost | Number | Your cost price in user's preferred currency |
| currency | String | Currency code based on the account: USD or IQD, or EUR for an enterprise account |
| features | Object | Package features object |
| features.voice_minutes | Integer | Voice minutes included (0 if none) |
| features.sms_count | Integer | SMS messages included (0 if none) |
| features.is_rechargeable | Boolean | Whether package can be recharged |
| is_unlimited | Boolean | Unlimited data flag |
| has_voice | Boolean | Has voice minutes included |
| has_sms | Boolean | Has SMS included |
| countries | Array | ISO country codes covered by the package, e.g. ["AT","BE","DE"] (eSIMfly packages). Useful for regional/global packages to see exactly which countries are included |
| locationNetworkList | Array | Detailed network coverage by location |
| networks | Array | Raw per-network coverage rows with MCC/MNC and continent detail (eSIMfly packages). See Networks Structure |
Additional Fields (O2, Vodafone, Bouygues Telecom Packages)
These fields are included for packages from O2, Vodafone, and Bouygues Telecom carriers:
| Field | Type | Description |
|---|---|---|
| phone_number | Object/null | Included phone number (country, prefix) |
| network_carrier | String/null | Carrier name (e.g., "O2", "Vodafone") |
| has_5g | Boolean | 5G support |
| has_hotspot | Boolean | Hotspot/tethering support |
| can_extend | Boolean | Can be extended after expiry |
| is_recurring | Boolean | Supports auto-renewal subscription |
| activation_policy | String | "immediate" or "first_use" |
| data_breakdown | Object/null | UK vs roaming data split (uk_data_gb, roaming_data_gb) |
| voice_details | Object/null | Call and text details (see below) |
Voice Details Structure
| Field | Type | Description |
|---|---|---|
| uk_calls_unlimited | Boolean | Unlimited UK calls |
| uk_texts_unlimited | Boolean | Unlimited UK texts |
| eu_minutes | Number/"unlimited"/null | EU call minutes included |
| eu_texts | Number/"unlimited"/null | EU texts included |
| international_minutes | Number/"unlimited"/null | International call minutes |
| international_texts | Number/"unlimited"/null | International texts |
| sms_inbound_only | Boolean | SMS receive only (no outbound) |
LocationNetworkList Structure
| Field | Type | Description |
|---|---|---|
| locationName | String | Country or location name |
| locationLogo | String | Flag image URL |
| operatorList | Array | List of network operators |
| operatorList[].operatorName | String | Network operator name |
| operatorList[].networkType | String | Network technology (e.g., "4G/5G") |
Networks Structure
The networks array provides the raw, per-network coverage detail for eSIMfly packages. Each entry is one operator in one country, including MCC/MNC codes — useful for low-level network matching or building your own coverage views.
| Field | Type | Description |
|---|---|---|
| network_id | Integer/null | Internal network identifier |
| network_name | String/null | Operator name (e.g., "MCI Iran") |
| network_type | String/null | Network technology (e.g., "4G", "5G") |
| country_code | String/null | Lowercase country code (e.g., "ir") |
| country_name | String/null | Country name (e.g., "Iran") |
| country_iso2 | String/null | ISO 3166-1 alpha-2 code (e.g., "ir") |
| continent | String/null | Continent name (e.g., "Asia") |
| mcc_code | String/null | Mobile Country Code (e.g., "432") |
| mnc_code | String/null | Mobile Network Code (e.g., "11") |
countries and networks are populated for eSIMfly packages. For regional/global packages, countries lists every covered country and networks enumerates each operator per country. Other providers expose coverage via locationNetworkList.
Examples
Search for United States Packages
const queryParams = new URLSearchParams({
search: 'United States',
limit: 20
});
const response = await fetch(
`https://esimfly.net/api/v1/business/esims/packages?${queryParams}`,
{
headers: generateHMACHeaders() // Your HMAC function
}
);
Search for Regional Packages
const queryParams = new URLSearchParams({
search: 'Europe',
limit: 50
});
const response = await fetch(
`https://esimfly.net/api/v1/business/esims/packages?${queryParams}`,
{
headers: generateHMACHeaders()
}
);
Filter by Package Type
Get all local packages:
const queryParams = new URLSearchParams({
type: 'local',
limit: 50
});
const response = await fetch(
`https://esimfly.net/api/v1/business/esims/packages?${queryParams}`,
{
headers: generateHMACHeaders()
}
);
Get all regional packages:
const queryParams = new URLSearchParams({
type: 'regional',
limit: 50
});
const response = await fetch(
`https://esimfly.net/api/v1/business/esims/packages?${queryParams}`,
{
headers: generateHMACHeaders()
}
);
Get all global packages:
const queryParams = new URLSearchParams({
type: 'global',
limit: 50
});
const response = await fetch(
`https://esimfly.net/api/v1/business/esims/packages?${queryParams}`,
{
headers: generateHMACHeaders()
}
);
Combine Search and Type Filter
Search for United States packages with local type only:
const queryParams = new URLSearchParams({
search: 'United States',
type: 'local',
limit: 20
});
const response = await fetch(
`https://esimfly.net/api/v1/business/esims/packages?${queryParams}`,
{
headers: generateHMACHeaders()
}
);
Pricing
The cost field shows your cost price. Apply your own profit margin when displaying prices to your customers.
Package Types
Local Packages (type: "local")
- Coverage for a single country
- Most cost-effective for single-destination travel
- Example: "United States 1GB - 7 Days"
Regional Packages (type: "regional")
- Coverage for multiple countries in a region
- Ideal for multi-country travel
- Example: "Europe 5GB - 30 Days" (covers 40+ countries)
Global Packages (type: "global")
- Worldwide coverage
- Premium pricing for maximum flexibility
- Example: "Discover Global 10GB - 30 Days" (covers 170+ countries)
Filtering Best Practices
- For destination search: Use
searchparameter with country name - For package type filtering: Use
typeparameter with values "local", "regional", or "global" (enterprise self-service packages return "enterprise") - Combine filters: You can use both
searchandtypetogether for precise results - Pagination: Use
pageandlimitfor large result sets (default 50, max 100) - Sync, don't proxy: copy the catalogue into your own database on a schedule (every 6–12 hours,
limit=100, paced at 1 request/second) and serve your storefront from it. The endpoint assembles and prices the full 7,000+ package catalogue on every call, so per-request use is slow for you and expensive for everyone. See the AI prompt above or the AI / LLM Integration guide
Error Responses
400 Bad Request
Invalid request ID format:
{
"success": false,
"error": "Invalid or missing RT-RequestID header. Must be a valid UUID v4.",
"code": "INVALID_REQUEST_ID"
}
Duplicate request ID:
{
"success": false,
"error": "Request ID has already been used",
"code": "DUPLICATE_REQUEST"
}
401 Unauthorized
Missing authentication:
{
"success": false,
"error": "Authentication required",
"message": "Please provide either Bearer token or complete HMAC signature authentication"
}
Invalid API key:
{
"success": false,
"error": "Invalid API key",
"code": "INVALID_API_KEY"
}
Invalid HMAC signature:
{
"success": false,
"error": "Invalid HMAC signature",
"code": "INVALID_SIGNATURE"
}
Incomplete HMAC authentication:
{
"success": false,
"error": "HMAC signature authentication required",
"message": "Missing required headers: RT-Signature, RT-Timestamp, and RT-RequestID are mandatory when using RT-AccessCode",
"code": "HMAC_REQUIRED"
}
403 Forbidden
Not a business account:
{
"success": false,
"error": "Invalid user or not a business account",
"code": "INVALID_USER"
}
500 Internal Server Error
{
"success": false,
"error": "Failed to fetch packages"
}
Notes
- Multi-Currency Pricing: Prices are shown in your account currency (
USD,IQD, orEURfor enterprise accounts) - Currency Conversion: IQD prices are converted using real-time exchange rates
- Currency Preference: Set your preferred currency in the business dashboard settings
- Packages are sorted: local plans first (sorted by GB, then unlimited by days), followed by regional, then global
- Only packages with 1GB or more data are returned (small packages under 1GB are filtered out)
- Duplicate unlimited plans are filtered, showing only the cheapest option per duration
- The
costfield shows your cost price (your profit margin is not exposed) - Package availability may change based on provider stock
- Use the
typeparameter to filter packages by coverage type (local/regional/global) - Combine
searchandtypeparameters for more precise filtering - Use pagination for large result sets (7000+ packages available)
- Sync the catalogue into your own database (every 6–12 hours) instead of calling this endpoint per customer request; never delete synced rows, mark them inactive
- Use
locationNetworkListfor detailed network coverage information by location - For eSIMfly packages, use
countriesfor the full list of covered ISO country codes andnetworksfor raw per-operator detail (MCC/MNC). Both are especially useful for regional and global packages