Developer platform
The wholesale market, one API away.
SMS, voice, verification, phone numbers and AI voice agents on one key, over the routes carriers list on the marketplace. Pay the carrier's rate plus a fee capped at $0.001, prepaid from $5, with no contract.
- One key for SMS, voice, numbers and AI voice
- Carrier rate plus 2%, capped at $0.001
- Prepaid from $5, no contract
Live, as this page was served
One request, priced before you send
price-number is public and needs no key. Ask it for any number and every live route that serves it comes back, cheapest first, with its billing increment and caller ID type.
$ curl "https://packetexchange.io/api/v1/routes/price-number?number=447911123456&type=sms"200 OKlive response
{
"number": "447911123456",
"unit": "msg",
"total": 3,
"routes": [
{
"rate": "0.020250",
"matchedPrefix": "44",
"billingIncrement": "1/1",
"cliType": "full_cli"
},
…
]
}Start here
Your first request in four steps
From an empty terminal to a message on a wholesale route. The account is free to open; you pay only for what you send.
Open an account
An email and a password, or continue with Google. No contract, no monthly fee and no sales call.
Add credit from $5
Prepaid by card or crypto from $5, or by wire from $100. Each request draws the carrier’s rate plus a fee capped at $0.001.
Create an API key
Scope it to what it needs, such as sms:send, and keep it in the PACKETEXCHANGE_API_KEY environment variable. Test keys run a request through routing and pricing without sending it, on test credit that comes with an invitation.
Make the request
Run the sample. The response states what the message cost, and the dashboard logs every request by key.
curl https://packetexchange.io/api/v1/comms/sms \
-H "Authorization: Bearer $PACKETEXCHANGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "+447700900123", "from": "Acme",
"message": "Your order 1042 has shipped."}'200 OKresponse
{
"success": true,
"data": {
"messageId": "…",
"status": "accepted",
"segments": 1,
"cost": "…"
}
}PACKETEXCHANGE_API_KEY.PHP, Go, Java, C# and Ruby - Try itAPI referenceEvery endpoint, with a console that sends the request with your key.
- ImportPostman and InsomniaThe whole API as a collection, with live and test environments.
- GenerateOpenAPI documentBuild a typed client in any language, or load it into your tools.
- WatchStatusLive checks of the API, messaging and calling, every minute.
- FollowChangelogEvery API change, dated, with the endpoints it touches.
- AskSupportA global team across time zones, reached from your dashboard.
What you can build
Everything your app says to a phone
Pick a product to see the call you would make, what comes back and what it costs. Prices are read from the live marketplace.
Verify API · SMS, voice and call-in
Verify a phone number
Send a one-time code by text, or by a call that reads it aloud in English, Spanish, French, German, Portuguese or Hindi. We generate the code, keep only a hash of it and tell you if the user typed it right. Or skip the code with call-in: the user calls your own phone number from the DID Store, with no SMS to deliver.
RequestPOST/api/v1/verify/start{ "to": "+447700900123", "channel": "voice", "language": "en", "brand": "Acme" }Response200 OK{ "verificationId": "…", "channel": "voice", "status": "pending", "sendStatus": "initiated", "expiresAt": "…" }Then POST /verify/check with the code: approved, denied, expired or max_attempts.
- What it costs
- $0.0203/msgUK mobile SMS, cheapest live route$0.0191/minUK mobile call, cheapest live routeCodes: no per-verification fee and checks are free. You pay for the text or the call that carries the code, plus the platform fee. Call-in: your phone number's monthly price plus $0.01 per approved verification, nothing if it expires.
- Key scope
- verify:write
- Also in this job
- POST/verify/checkGET/verify/{id}POST/verify/call-in/numbersPOST/comms/voice-otp
Also on the API
- Number lookupLine type, network and cheapest price. Free.
- WebhooksSigned events for sends, calls and inbound texts, with resend.
- Do-not-call listNumbers on your list are suppressed from calls and SMS.
- x402 top-upsAn agent funds its own balance in USDC on Base.
- Switch APIRun a hosted Class-4 switch from code.
Verify API · SMS, voice and call-in
Verify a phone number
Send a one-time code by text, or by a call that reads it aloud in English, Spanish, French, German, Portuguese or Hindi. We generate the code, keep only a hash of it and tell you if the user typed it right. Or skip the code with call-in: the user calls your own phone number from the DID Store, with no SMS to deliver.
{
"to": "+447700900123",
"channel": "voice",
"language": "en",
"brand": "Acme"
}{
"verificationId": "…",
"channel": "voice",
"status": "pending",
"sendStatus": "initiated",
"expiresAt": "…"
}Then POST /verify/check with the code: approved, denied, expired or max_attempts.
- What it costs
- $0.0203/msgUK mobile SMS, cheapest live route$0.0191/minUK mobile call, cheapest live routeCodes: no per-verification fee and checks are free. You pay for the text or the call that carries the code, plus the platform fee. Call-in: your phone number's monthly price plus $0.01 per approved verification, nothing if it expires.
- Key scope
- verify:write
- Also in this job
- POST/verify/checkGET/verify/{id}POST/verify/call-in/numbersPOST/comms/voice-otp
Quickstarts
Pick a job, pick a language
Eleven guides from key to working request, each with complete code in cURL, Node, Python, PHP, Go, Java, C# and Ruby, the response it returns and what it costs.
- Send an SMSMessaging
- Verify a phone number by SMSVerification
- Verify a phone number by voice callVerification
- Verify a phone number by call-inVerification
- Make a phone callVoice
- Build an AI voice agentVoice
- Buy a phone numberNumbers and routing
- Price a number (least-cost routing)Numbers and routing
- Test a caller ID on a routeNumbers and routing
- Check routes before you buyNumbers and routing
- Receive webhooks and verify signaturesWebhooks and agents
- Connect an AI agent over MCPWebhooks and agents
Why developers choose it
The tools you expect, at the price carriers pay
A typical messaging platform resells the wholesale market with its own margin on top. Here you are on the market itself, with a developer toolkit built around it.

Wholesale rates, one capped fee
You pay the rate the carrier listed plus 2%, never more than $0.001 per message or call. No retail markup.
Every route and price, side by side
The marketplace lists each route with its price, caller ID type and the quality its seller states.
One key, one balance
SMS, voice, verification, numbers and AI voice on one prepaid balance, with scopes on every key.
Test keys
Run the full request through routing and pricing without delivering anything or touching your live balance.
Signed webhooks you can replay
Timestamped HMAC signatures, a delivery log, and resend for anything your endpoint missed.
Typed SDKs and a full reference
Node.js and Python clients, and an OpenAPI reference generated from the API itself.
Usage per key
Requests, error rate and latency for each key, so you can see which service is calling.
x402 for agents
An agent tops up its own balance with one signed USDC payment on Base, no card on file.
$0.001the most the platform fee can be, per message or call
The carrier's rate, plus 2% capped at $0.001. Prepaid from $5.
No monthly fee and no per-verification fee on codes. Card, crypto or wire; agents can top up with x402.
API changelog
What changed in the API
- Added
Call-in verification in the Verify API
POST /verify/start takes a new channel, call_in: instead of sending a code, it returns callNumber and callNumberDisplay, one of your own phone numbers, which your user calls from the phone being verified. Buy the number in the DID Store, in the country of the people you verify, and switch it on with POST /verify/call-in/numbers or in the control panel; while it is on, calls to it are used for verification only and do not ring. GET /verify/call-in/numbers lists your numbers and which are on, and DELETE /verify/call-in/numbers/{phoneNumberId} switches one off. The call is matched by caller ID and ended without being answered, so it is free for the caller, and the verification is approved as soon as it arrives. It costs $0.01 per approved verification from your balance, on top of the number's monthly price; an expired one is free. Each call-in number verifies phone numbers in its own country; starting one for a country where you have no call-in number is refused with a 400 that says how to add one, or to use sms or voice. expirySeconds defaults to 300 for call_in. Every start now accepts an optional reference (your own id, echoed back), and starting again with the same reference or number while one is pending returns the pending verification. GET /verify lists your verifications, filterable by reference, status and channel. On test keys, POST /verify/{id}/simulate-call approves a pending call_in verification without a call. Two new webhook events: verification.approved (for every channel, when a code is approved or a call-in call arrives) and verification.expired (call_in only). POST /verify/check on a call_in verification is a 400, since there is no code. Existing sms and voice integrations are unchanged.
POST/verify/startGET/verify/{id}GET/verifyGET/verify/call-in/numbersPOST/verify/call-in/numbersDELETE/verify/call-in/numbers/{phoneNumberId}GET/verify/call-in/coveragePOST/verify/{id}/simulate-call
- Added
Send a test SMS over an SMS route before you buy
POST /routes/check/sms sends one short fixed message ("PacketExchange test message: this route delivered to your phone. Ref K7QD") over exactly the SMS route you name to your own mobile, which the route must cover, and returns a checkId; GET /routes/check/{id} reports accepted (the route took it, no receipt yet), delivered (a carrier receipt confirmed it) or refused (with errorCode, and the carrier's own stat and err values when a receipt said it failed). It is charged at the route's price for one message plus the normal platform fee, from your test credit first; a refused message is not charged and one whose receipt says it failed is refunded. The text and sender are fixed. Limits: 3 test messages to one number an hour, 5 different numbers and 20 test messages per account a day. Test keys return a simulated result and send nothing. Tests appear in GET /routes/checks with mode sms and never count in SMS volume, revenue or quality figures. Route check results gain the accepted, delivered and refused values and the checkType sms.
POST/routes/check/smsGET/routes/check/{id}GET/routes/checks
- Added
Automatic failover on every Smart Routing call and SIP trunk
A call placed without a routeId that a route refuses before it is answered (SIP 403, 480, 500, 502, 503, 504 or no response) is now tried straight away on your next route: your routing order for that number, else Smart Routing's next choice, up to three routes. Only the route that connects is billed; a wrong number (404, 484), a busy or declined call and no answer are never retried, and a Voice API call that has started ringing is never moved to another route. The response, the call.completed webhook and GET /comms/calls/{id} name the route that carried the call (routeId) and list the attempts when there was more than one. Calls over your SIP trunk fail over across your purchased routes for the number in the same order when a route answers with a server error or congestion (5xx), and GET /purchases/failover shows your first choice and backups per destination.
POST/comms/callsGET/comms/calls/{id}GET/purchases/failover
- Added
Measured route quality on every listing
Marketplace listings now carry a measured object: ASR, ACD, NER (the share of calls the network delivered to the phone: answered, busy, unanswered or declined) and median PDD, measured on real buyer calls over the last 7 days, else 30 days, else the most recent period that had enough. It is null until a listing has carried at least 50 calls from at least two buyers; route checks, test calls, the seller's own traffic and calls refused before dialling are not counted, and a call that failed over counts once on each route it tried. Rows also carry routeChecks (the last 7 days of route checks, or null). The seller-stated expectedAsr, expectedAcd and expectedPdd are unchanged. New sorts: measured_asr_desc, measured_asr_asc, measured_acd_desc, measured_acd_asc, ner_desc, ner_asc and quality_desc. The Exchange Score uses measured figures when a listing has them for the last 7 or 30 days. Signed-out callers get the call count as a band and no buyer count.
GET/routesGET/routes/{id}GET/routes/price-number
Send your first message today
Open an account, add credit from $5 and create a key. Wholesale rates from the first request, with no contract and no monthly fee.
