Get an API key
Open an account, then create a key under Dashboard, API keys. Live keys (wmmn_live_sk_) send real traffic and charge your balance. Test keys (wmmn_test_sk_) run the same request through routing and pricing without delivering it, and draw on test credit, which comes with an invite code.
price-number works without a key (30 requests per minute). resolve runs as your account, so it needs a key with routes:read and also sees private routes you have bought.
Every sample reads the key from the environment. Keep it out of source control.
export PACKETEXCHANGE_API_KEY=wmmn_live_sk_...Fund the balance
PacketExchange is prepaid. Top up from $5 by card or crypto, or from $100 by wire, under Dashboard, Billing. An agent can top itself up with x402. There is no contract and no monthly fee.
Nothing to fund. Pricing and previews are free on every account.
CostFree. Nothing is sent or dialled.
Make the request
Price a number for voice (per minute) or sms (per message), then resolve it with a strategy: cheapest, best_quality or balanced (the default when you send without a routeId).
# 1. Every route that serves the number, priced for it, cheapest first. No key needed.
curl -s "https://packetexchange.io/api/v1/routes/price-number?number=%2B447700900123&type=voice" |
jq '.data.routes[:5][] | {rate, destination, billingIncrement, cliType, expectedAsr}'
# 2. The route Smart Routing would pick for your account, with a strategy. Free, nothing is dialled.
curl -s "https://packetexchange.io/api/v1/routes/resolve?to=%2B447700900123&type=voice&strategy=cheapest" \
-H "Authorization: Bearer $PACKETEXCHANGE_API_KEY" | jq .data.selectedHandle the response
price-number lists up to 100 routes, cheapest first; total counts all of them. resolve returns the selected route and up to four ranked alternatives.
{
"success": true,
"data": {
"number": "447700900123",
"type": "voice",
"unit": "min",
"total": <integer>,
"routes": [
{
"id": "<route id>",
"destination": "<e.g. United Kingdom-Mobile>",
"matchedPrefix": "<digits>",
"rate": "<USD, 6 decimals>",
"billingIncrement": "<e.g. 1/1, or null>",
"pricedBy": "deck",
"cliType": "<caller ID type>",
"expectedAsr": "<seller-stated %, or null>",
"exchangeScore": <1-100, or null until it has traffic>
}
],
"notice": null
}
}{
"success": true,
"data": {
"strategy": "cheapest",
"selected": {
"id": "<route id>",
"destinationName": "<listing name, e.g. Niger Mobile · Ref 7K2Q>",
"price": "<USD, 6 decimals>",
"cliType": "<caller ID type>",
"matchedPrefix": "<digits>"
},
"alternatives": [],
"count": <integer>
}
}Values in angle brackets are placeholders for your own ids, times and amounts.
- rate
- What this route charges for this number, per minute or per message, before the platform fee.
- measured
- ASR, ACD, NER and median PDD measured on real buyer calls on this listing; null until it has carried at least 50 calls from at least two buyers.
- expectedAsr, expectedAcd
- Stated by the seller when they listed the route.
- exchangeScore
- A 1-100 score from the tier and the measured figures when the listing has recent ones, else the stated figures; null ("New") until the route has carried traffic.
- notice
- "sanctioned" when the destination is embargoed and no route can serve it.
When something goes wrong
- This is the public list price: negotiated prices and private grants are not applied to price-number.
- Seller identities are never included in either response.
- Pass the chosen route's id as routeId on a send, or pass a strategy and let Smart Routing choose at send time.
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input",
"details": [
{ "path": "to", "message": "<why the field was refused>" }
]
}
}