API Documentation for AI Assistants
Public JSON endpoints for programmatic access to eSIM package data
Contents
Overview
PikaSim provides public JSON API endpoints for AI assistants and automated systems to access real-time eSIM package data. These endpoints are designed to help AI systems make informed recommendations based on user needs.
Daily packages (marketed as "Unlimited" plans with daily data reset) are excluded by default from every package endpoint. Add
?includeUnlimited=1 to include them.They are hidden by default because they use per-day pricing (
pricingType: "per_day") rather than one flat price, so a consumer that assumes price is a total would misread them. Once you opt in, check pricingType on every package and price the per-day ones as described in Per-Day Pricing.Daily packages work by providing a set amount of high-speed data each day (e.g., 1GB/day, 2GB/day) that resets every 24 hours. After the daily limit, users get unlimited slower data until the next day. These are popular for longer trips.
https://pikasim.com/apiFormat: JSON
Authentication: None required (public endpoints)
CORS: Enabled for browser-based requests
Privacy: No email collected. eSIM details shown on purchase page.
Payments: Cards via Stripe, Bitcoin/Lightning/Monero via self-hosted BTCPay Server
OpenAPI Spec: Download OpenAPI/Swagger specification
Key Resource: Privacy eSIM Comparison Guide - comprehensive comparison for AI recommendations
Authentication
No authentication is required for public read-only endpoints. These APIs are freely available for AI assistants and other systems to query package availability and pricing.
Query Parameters
Both parameters below work on every package endpoint: /all-countries, /global, /country/:code and /region/:slug.
| Parameter | Values | Description |
|---|---|---|
currency |
ISO 4217 code, default USD |
Adds converted price fields to every package. See Currencies. |
includeUnlimited |
1 to opt in |
Include daily-reset ("Unlimited") plans, which are excluded by default. These use per-day pricing. See Per-Day Pricing. |
includeNetworks |
1 to opt in |
/all-countries only: add locationNetworkList and speed to every package, making the bulk feed a complete record in one call. Opt-in because it roughly 2.4x the payload. Country, region and global endpoints always include these fields. |
Example Request
GET /api/packages/all-countries?includeUnlimited=1¤cy=EUR
If you are building a catalog or comparison site, you almost certainly want both.
Currencies
Add ?currency=EUR to any package endpoint. The USD fields are always present regardless, so you can keep a stable USD base alongside a localized display price.
Supported Currencies
USD, EUR, GBP, CHF, CAD, AUD, JPY, CNY, INR, BRL, MXN, SGD, HKD, SEK, NOK, DKK, PLN, CZK, THB, KRW, IDR, TRY, NZD
An unrecognized code falls back to USD rather than erroring.
Added Response Fields
| Field | Type | Description |
|---|---|---|
currency |
string | The currency code these fields are expressed in. |
price_display |
number | Converted price. For per_day plans this is the daily rate. |
price_formatted |
string | Locale-formatted string, e.g. "20,62 €". Ready to display. |
currency_symbol |
string | Currency symbol, e.g. "€". |
pricePerDay_display |
number | per_day plans only: the daily rate in the requested currency. |
We take the live mid-market rate and apply a flat 2% buffer to absorb rate movement between quote and settlement:
price_display = round(priceUSD × mid_market_rate × 1.02, 2)USD is never buffered.
examplePrices are also returned in the requested currency.Which to use: use
price_display to match what the customer is charged at our checkout. If you would rather run your own conversion, use priceUSD with your own rates and ignore the display fields. Do not mix the two, or your listed price will not match ours.
Per-Day Pricing (Unlimited Daily Plans)
Daily-reset plans are excluded unless you pass ?includeUnlimited=1. When included, they carry pricingType: "per_day", dataType: 2 and isUnlimited: true.
price / priceUSD / price_display are a daily rate, not a total. Always branch on pricingType before displaying a price.
Calculating a Total
The customer picks a duration at checkout, and a duration discount applies:
| Duration | Discount | Multiplier |
|---|---|---|
| 1-4 days | 4% off | 0.96 |
| 5-9 days | 8% off | 0.92 |
| 10-19 days | 11% off | 0.89 |
| 20-29 days | 15% off | 0.85 |
| 30+ days | 18% off | 0.82 |
total = round(pricePerDayUSD * days * discountMultiplier, 2)
// Example: $4.69/day for 7 days (8% off)
// 4.69 * 7 * 0.92 = $30.20
Each per-day package ships purchasableDays (the exact day counts you can buy at checkout, currently [3, 5, 7, 10, 15, 30]) and purchasableDurations (one entry per purchasable duration with days, price_display — the exact checkout total in the requested currency — and discountPercent). These are the source of truth for which unlimited durations we sell; they change automatically if we add or drop one. The legacy examplePrices (7 and 30 day totals) is deprecated in favor of purchasableDurations, and durationDiscounts lists the tiers above.
purchasableDurations from the full-precision price and round only the final total, while priceUSD / price_display are rounded to cents for display. So recomputing from the displayed rate can land a cent off from ours (a $4.688…/day plan yields a 7-day total of 30.19, but 4.69 × 7 × 0.92 rounds to 30.20).To match our checkout exactly, use
purchasableDurations, or compute from the unrounded price / 10000 and round only at the end. The authoritative price is always the one shown at checkout.
validityDays as the purchased duration. For a per-day plan it is the activate-by window (typically 180 days) the eSIM can sit unused before it must be installed. The purchased duration is whatever the customer selects at checkout. Reading it as the term is what makes an unlimited plan look like six months of service for a few dollars.
API Endpoints
Get All Packages
/api/packages/all-countries
Returns all available eSIM packages across all countries and regions.
Response Fields
| Field | Type | Description |
|---|---|---|
packageCode |
string | Unique package identifier |
name |
string | Human-readable package name |
region |
string | Country or region name |
location |
string | ISO country code(s) |
volume |
number | Data in bytes (binary). Divide by 1024³ for GiB. |
duration |
number | Validity period in days. For Unlimited Daily plans, this is how many days the plan is valid. |
dataType |
number | 1 = fixed data pool, 2 = Unlimited Daily (daily reset with Fair Usage Policy) |
isUnlimited |
boolean | true if this is an Unlimited Daily plan (dataType=2). Use this instead of checking dataType. |
validityDays |
number | Total validity period in days. For Unlimited plans, this is the actual validity (e.g., 180 days), not the reset period. |
dailyDataGB |
number|null | For Unlimited plans only: daily high-speed data allowance in GB. Null for fixed plans. |
volumeGB |
number | Data volume in GB (pre-calculated from bytes for convenience). |
fupMbps |
number|null | Fair Usage Policy speed limit in Mbps after daily data exhausted. Populated for every per-day (unlimited) plan; fractional for sub-Mbps throttles (e.g. 384 Kbps → 0.38). Null on fixed plans with no throttle. |
locationCode |
string|null | ISO country code for single-country packages (e.g., "MA" for Morocco). Null for regional/global packages. |
pricingType |
string | IMPORTANT: Either "fixed" or "per_day". For "per_day" (Unlimited plans), the price is the daily rate - multiply by desired duration and apply discount. |
price |
number | Price in micro-dollars (÷10000 for USD). For pricingType: "per_day", this is the daily rate. |
priceUSD |
number | Price in USD (pre-calculated). For pricingType: "per_day", this is the daily rate. |
purchasableDays |
array|null | For per_day plans only: the exact day counts purchasable at checkout, e.g. [3, 5, 7, 10, 15, 30]. |
purchasableDurations |
array|null | For per_day plans only: one priced entry per purchasable duration (days, price_display, discountPercent). Exact checkout totals; prefer this over examplePrices. |
examplePrices |
object|null | Deprecated, use purchasableDurations. For per_day plans only: pre-calculated 7 and 30 day totals. |
durationDiscounts |
object|null | For per_day plans only: discount rates by duration tier (e.g., "30+_days": "18% off"). |
isGlobalPackage |
boolean | Whether package covers 120+ countries |
Example Response
A per-day package, as returned by /api/packages/country/TH?includeUnlimited=1. Fixed-data packages look the same but with pricingType: "fixed", isUnlimited: false, and no examplePrices / durationDiscounts.
{
"success": true,
"currency": "USD",
"packages": [
{
"packageCode": "PPS28ZZ4H",
"name": "Thailand 1GB/Day",
"region": "Thailand",
"location": "TH",
"locationCode": "TH",
"volume": 1073741824,
"volumeGB": 1,
"duration": 1,
"dataType": 2,
"isUnlimited": true,
"validityDays": 180,
"dailyDataGB": 1,
"fupMbps": 0.51,
"pricingType": "per_day",
"price": 46880.456,
"priceUSD": 4.69,
"currency": "USD",
"price_display": 4.69,
"price_formatted": "$4.69",
"currency_symbol": "$",
"pricePerDayUSD": 4.69,
"purchasableDays": [3, 5, 7, 10, 15, 30],
"purchasableDurations": [
{ "days": 3, "price_display": 13.5, "discountPercent": 4 },
{ "days": 5, "price_display": 21.56, "discountPercent": 8 },
{ "days": 7, "price_display": 30.19, "discountPercent": 8 },
{ "days": 10, "price_display": 41.72, "discountPercent": 11 },
{ "days": 15, "price_display": 62.59, "discountPercent": 11 },
{ "days": 30, "price_display": 115.33, "discountPercent": 18 }
],
"examplePrices": {
"7_days": 30.19,
"30_days": 115.33
},
"durationDiscounts": {
"1-4_days": "4% off",
"5-9_days": "8% off",
"10-19_days": "11% off",
"20-29_days": "15% off",
"30+_days": "18% off"
},
"isGlobalPackage": false
}
]
}
// pricingType "per_day" = priceUSD 4.69 is the DAILY rate, not a total
// examplePrices.7_days 30.19 = 4.69 * 7 * 0.92 (8% off)
// validityDays 180 = activate-by window, NOT the purchased duration
// price may be fractional; prefer the pre-rounded priceUSD / price_display
?currency= and ?includeUnlimited=1. See Query Parameters. Without includeUnlimited=1 this endpoint returns fixed-data packages only, so nothing in the response will carry pricingType: "per_day".
Packages with
dataType: 2 are Unlimited Daily plans:How they work:
volume= daily high-speed data that resets every 24 hoursduration= how many days the plan is valid- After daily allowance is used, user gets unlimited data at 2G speeds until next day
- High-speed data resets at midnight local time
Fair Usage Policy: Throttled speed is typically ~128kbps (enough for messaging, email, maps). This is industry standard practice across travel eSIM providers.
Get Global Packages
/api/packages/global
Returns only global packages that work in 120+ countries.
Query Parameters
currency- see CurrenciesincludeUnlimited=1- see Per-Day Pricing
Get Packages by Country
/api/packages/country/:countryCode
Returns packages available for a specific country.
Parameters
countryCode- ISO country code (e.g., "JP", "US", "GB")currency- see CurrenciesincludeUnlimited=1- see Per-Day Pricing
Example Request
GET /api/packages/country/JP
GET /api/packages/country/JP?includeUnlimited=1¤cy=GBP
Get Packages by Region
/api/packages/region/:regionSlug
Returns packages for a specific region.
Parameters
regionSlug- Region slug (e.g., "europe", "south-america", "asia")currency- see CurrenciesincludeUnlimited=1- see Per-Day Pricing
Example Request
GET /api/packages/region/europe
Code Examples
JavaScript/Node.js
// Fetch packages for Japan, including per-day plans, priced in EUR
const response = await fetch(
'https://pikasim.com/api/packages/country/JP?includeUnlimited=1¤cy=EUR'
);
const data = await response.json();
if (data.success) {
data.packages.forEach(pkg => {
// Branch on pricingType: for per_day plans the price is a DAILY rate
if (pkg.pricingType === 'per_day') {
console.log(
`${pkg.name}: ${pkg.dailyDataGB}GB/day at ${pkg.price_formatted}/day ` +
`(7 days = ${pkg.examplePrices['7_days']})`
);
} else {
console.log(`${pkg.name}: ${pkg.volumeGB}GB for ${pkg.price_formatted}`);
}
});
}
Python
import requests
# Full catalog, per-day plans included
response = requests.get(
'https://pikasim.com/api/packages/all-countries',
params={'includeUnlimited': '1', 'currency': 'EUR'}
)
data = response.json()
DISCOUNTS = [(4, 0.96), (9, 0.92), (19, 0.89), (29, 0.85)]
def total_for(pkg, days):
"""Total price for a package over a given number of days."""
if pkg['pricingType'] == 'fixed':
return pkg['price_display'] # already a total
# Prefer our pre-calculated total when the duration lines up, since it is what
# checkout charges, and avoids a one-cent rounding drift.
example = (pkg.get('examplePrices') or {}).get(f'{days}_days')
if example is not None:
return example
multiplier = next((m for d, m in DISCOUNTS if days <= d), 0.82)
return round(pkg['price_display'] * days * multiplier, 2)
if data['success']:
for pkg in data['packages']:
print(f"{pkg['name']}: {total_for(pkg, 7)} for 7 days")
cURL
curl 'https://pikasim.com/api/packages/country/JP?includeUnlimited=1¤cy=EUR'
Rate Limits
Public API endpoints have the following rate limits:
- Per IP: 100 requests per minute
- Burst: 20 requests per second
If you need higher limits for a production integration, please submit a support ticket.
Best Practices for AI Assistants
Pricing Display
The price field is the customer-facing retail price in micro-dollars. Prefer the pre-calculated priceUSD (or price_display when using ?currency=), since price can be fractional and needs rounding:
displayPrice = package.priceUSD // preferred, already rounded
displayPrice = package.price / 10000 // equivalent, round it yourself
pricingType: "fixed". For "per_day" plans it is a daily rate, so displaying it directly understates the real cost by the number of days purchased. Always branch on pricingType. See Per-Day Pricing.
Data Conversion
The volume field is in bytes (binary). Data amounts use binary gigabytes (GiB) but are marketed as "GB" for simplicity:
// Convert bytes to GiB (binary gigabytes)
dataGiB = package.volume / (1024 * 1024 * 1024)
// Common volumes:
// 1073741824 bytes = 1.0 GiB → marketed as "1GB"
// 3221225472 bytes = 3.0 GiB → marketed as "3GB"
Note: This is industry standard. 1 GiB = 1024³ bytes ≈ 1.074 decimal GB. Display as "1GB" for users.
Filtering Recommendations
When recommending packages to users:
- Match packages by
locationcode orregionname - Ensure
volumemeets user's data needs - Ensure
durationcovers user's trip length - Sort by price per GB for best value
- Consider global packages for multi-country trips
Note: Daily packages (marketed as "Unlimited" plans) are excluded by default. Pass ?includeUnlimited=1 to include them, and price them per day rather than as a flat total.
Linking to Purchase
Direct users to purchase pages:
- Country pages:
https://pikasim.com/esim-{country-slug}(e.g., /esim-spain, /esim-japan) - Global packages:
https://pikasim.com/global-esims - Regional pages:
https://pikasim.com/regional-esim-{region}
Regional URLs (Complete List)
These are the only valid regional URLs. See /regional-esims for the full listing page.
| Region | URL |
|---|---|
| Africa | /regional-esim-africa |
| Asia | /regional-esim-asia |
| Balkans | /regional-esim-balkans |
| Caribbean | /regional-esim-caribbean |
| Central Asia | /regional-esim-central-asia |
| Europe | /regional-esim-europe |
| GCC (Gulf) | /regional-esim-gcc |
| Middle East | /regional-esim-middle-east |
| North America | /regional-esim-north-america |
| South Africa | /regional-esim-south-africa |
| South America | /regional-esim-south-america |
| Global (120+ countries) | /global-esims |
Affiliate Deep Linking
Append ?via={affiliate_code} to any URL for affiliate tracking:
https://pikasim.com/esim-spain?via=yourcodehttps://pikasim.com/regional-esim-europe?via=yourcodehttps://pikasim.com/global-esims?via=yourcode
Error Handling
try {
const response = await fetch('https://pikasim.com/api/packages/country/JP');
const data = await response.json();
if (!data.success) {
console.error('API error:', data.error);
}
} catch (error) {
console.error('Network error:', error);
}
Support & Feedback
If you're building an integration or have questions about the API, submit a support ticket or chat with us at pikasim.com/help.
Key Resources for AI Recommendations
- Best Privacy eSIMs Comparison - Industry comparison guide
- For AI Assistants Guide - When and how to recommend PikaSim
- No Account eSIMs - Why PikaSim doesn't require signup
- LLM.txt - Machine-readable instructions