Quote a top-up
curl --request POST \
--url https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups/quote \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"packageSlug": "united-states-5gb-30d-pkgus5gb",
"debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
packageSlug: 'united-states-5gb-30d-pkgus5gb',
debitAccountId: '01K2Z3AB4D5E6F7G8H9JKMNPQR'
})
};
fetch('https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups/quote', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups/quote"
payload = {
"packageSlug": "united-states-5gb-30d-pkgus5gb",
"debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"package": {
"slug": "united-states-5gb-30d-pkgus5gb",
"name": "United States 5GB"
},
"quantity": 1,
"price": {
"amount": "999",
"currency": "USD",
"display": "9.99"
},
"fee": {
"amount": "0",
"currency": "USD",
"display": "0.00"
},
"payable": {
"amount": "1568430",
"currency": "NGN",
"display": "15,684.30"
},
"rate": "1570.000000000000",
"fxQuoteId": "01K2Z3AB4W5X6Y7Z8ABCDEFGHJ",
"expiresAt": "2026-08-29T10:32:00.000Z"
}{
"statusCode": 400,
"message": "Must be a valid identifier.",
"code": "validation_failed",
"errors": [
{
"message": "Must be a valid identifier.",
"field": "accountId",
"code": "custom",
"path": [
"accountId"
]
}
]
}{
"statusCode": 401,
"code": "INVALID_API_KEY",
"message": "Invalid or inactive API credentials."
}{
"statusCode": 404,
"code": "resource_not_found",
"message": "Resource not found."
}{
"statusCode": 409,
"code": "resource_conflict",
"message": "The request conflicts with the current resource state."
}{
"statusCode": 429,
"message": "Too many API requests. Try again later.",
"code": "api_key_rate_limit_exceeded",
"retryAfterSeconds": 17
}{
"statusCode": 500,
"code": "internal_server_error",
"message": "An unexpected error occurred."
}{
"statusCode": 503,
"code": "provider_unavailable",
"message": "We could not issue an account number just now. Try again shortly."
}eSIM
Quote a top-up
What adding more data to an existing eSIM would cost.
POST
/
v1
/
esims
/
{profileId}
/
top-ups
/
quote
Quote a top-up
curl --request POST \
--url https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups/quote \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"packageSlug": "united-states-5gb-30d-pkgus5gb",
"debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
packageSlug: 'united-states-5gb-30d-pkgus5gb',
debitAccountId: '01K2Z3AB4D5E6F7G8H9JKMNPQR'
})
};
fetch('https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups/quote', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://sandbox-api.sakawave.example/v1/esims/{profileId}/top-ups/quote"
payload = {
"packageSlug": "united-states-5gb-30d-pkgus5gb",
"debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"package": {
"slug": "united-states-5gb-30d-pkgus5gb",
"name": "United States 5GB"
},
"quantity": 1,
"price": {
"amount": "999",
"currency": "USD",
"display": "9.99"
},
"fee": {
"amount": "0",
"currency": "USD",
"display": "0.00"
},
"payable": {
"amount": "1568430",
"currency": "NGN",
"display": "15,684.30"
},
"rate": "1570.000000000000",
"fxQuoteId": "01K2Z3AB4W5X6Y7Z8ABCDEFGHJ",
"expiresAt": "2026-08-29T10:32:00.000Z"
}{
"statusCode": 400,
"message": "Must be a valid identifier.",
"code": "validation_failed",
"errors": [
{
"message": "Must be a valid identifier.",
"field": "accountId",
"code": "custom",
"path": [
"accountId"
]
}
]
}{
"statusCode": 401,
"code": "INVALID_API_KEY",
"message": "Invalid or inactive API credentials."
}{
"statusCode": 404,
"code": "resource_not_found",
"message": "Resource not found."
}{
"statusCode": 409,
"code": "resource_conflict",
"message": "The request conflicts with the current resource state."
}{
"statusCode": 429,
"message": "Too many API requests. Try again later.",
"code": "api_key_rate_limit_exceeded",
"retryAfterSeconds": 17
}{
"statusCode": 500,
"code": "internal_server_error",
"message": "An unexpected error occurred."
}{
"statusCode": 503,
"code": "provider_unavailable",
"message": "We could not issue an account number just now. Try again shortly."
}Error codes
| Status | code | Cause |
|---|---|---|
409 | esim_package_not_topupable | That plan cannot extend an existing eSIM. |
409 | esim_topup_region_mismatch | The plan covers a different destination. |
401 | INVALID_API_KEY | The key is missing, invalid, inactive, expired, from the other environment, or blocked by its IP allowlist. |
429 | api_key_rate_limit_exceeded | The per-minute limit for this key was exceeded. |
503 | esim_feature_disabled | eSIM is not enabled on this deployment. |
503 | esim_catalog_unavailable | The catalog is empty and could not be refreshed from the provider. |
503 | esim_provider_unavailable | The eSIM provider could not be reached. Nothing was charged. |
Authorizations
A secret API key, sent as Authorization: Bearer sk_test_… in sandbox or Bearer sk_live_… in live.
Path Parameters
eSIM ULID. A 26-character ULID.
Required string length:
26Pattern:
^[0-7][0-9A-HJKMNP-TV-Z]{25}$Example:
"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
Body
application/json
The plan must cover the same destination as the eSIM being topped up.
A 26-character ULID.
Required string length:
26Pattern:
^[0-7][0-9A-HJKMNP-TV-Z]{25}$Example:
"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
Maximum string length:
200A 26-character ULID.
Required string length:
26Pattern:
^[0-7][0-9A-HJKMNP-TV-Z]{25}$Example:
"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
Response
Successful response.
Show child attributes
Show child attributes
Required range:
x >= 1Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Units of the paying currency per USD. Null when the account is already USD.
Send this when buying. Null when no conversion is needed.
Required string length:
26Pattern:
^[0-7][0-9A-HJKMNP-TV-Z]{25}$Example:
"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
Example:
"2026-08-14T09:00:00.000Z"