
Codes by text,or by a call
The Verify API sends a one-time code by SMS or reads it aloud on a call, checks what the user types, and bills only the message or call. No per-verification fee.
In short
The Verify API makes the code, sends it and checks it: two requests, SMS or voice, with only a hash of the code ever stored.
Codes go out on the best-quality route for each destination, premium-rate and pumping ranges are refused, and every number has a resend cooldown and an hourly cap. You pay the route rate for the SMS or call plus a fee capped at $0.001, with no per-verification charge.
What you can do
- 01
Verify API
Start and check a code in two requests. Hashed, single use, five attempts.
- 02
Voice OTP
A call that reads the code aloud, in English, Spanish, French, German, Portuguese or Hindi.
- 03
SMS OTP
Codes by text in seven languages, sent from your brand name.
- 04
Your own codes
Keep code generation in your app and send through the SMS or voice API instead.
How it works
Two requests from sign-up form to verified number
The Verify API makes the code, sends it and checks it. You never store a code, and you pay only for the message or call that carried it.
Take a key
Open a free account and create a key with the Verify permission. A test key runs the whole flow on sandbox credit and hands you the code, so you can finish a check without a phone.
Start a verification
POST /verify/start with the number and a channel: sms, or voice for a call that reads the digits aloud. Codes go out on the best-quality route for the destination unless you name a strategy.
Check what they typed
POST /verify/check with the verification ID and the code. The answer is approved, denied, expired or max_attempts, and a code works once.
Set the guard rails
Fund a prepaid balance, set a daily spend alert and a low-balance alert. Per-number limits and blocked premium-rate ranges are on from the first request.
Verify API
No codes to generate, store or expire
Start a verification and we create a code of 4 to 10 digits, send it in your brand name and keep only a hash of it. Check it with the ID we returned: five attempts, one use, ten minutes by default. The code never appears in a database row, a log line or a live API reply, so there is nothing of value to leak on your side or ours.
- SMS in English, Spanish, French, German, Portuguese, Hindi or Arabic; voice in all but Arabic
- Expiry from 1 to 60 minutes, code length from 4 to 10 digits
- Prefer your own codes? Send them through the SMS API, or speak them with POST /comms/voice-otp
POST /v1/verify/start
{ "to": "+447700900431",
"channel": "voice",
"language": "es",
"brand": "Acme" }{ "verificationId": "5f0c…e21a",
"status": "pending",
"expiresAt": "…T10:10:00Z",
"maxAttempts": 5 }We generate the code and keep only a hash of it. The call reads the digits aloud in Spanish, twice, then hangs up.
POST /v1/verify/check
{ "verificationId": "5f0c…e21a",
"code": "482916" }Voice OTP
A code they can hear, for when a text will not do
Landlines, patchy SMS coverage and users who never see the text: start the same verification with channel voice and the phone rings with the code. The call names your brand, reads the digits one at a time, repeats them, and hangs up. It rides the voice route for the destination and is billed on that route's increment, with no text-to-speech surcharge.
- Six spoken languages: English, Spanish, French, German, Portuguese and Hindi
- The digits are read twice by default, up to three times
- If a carrier refuses the SMS, the reply tells you to retry by voice, and the refused attempt does not count against the number's limit

SMS pumping
An attack meets limits before it meets your balance
SMS pumping turns a sign-up form into someone else's revenue: scripts request codes to numbers that earn a payout per message. The Verify API refuses premium-rate, satellite and known pumping ranges outright, caps how often one number or one number range can be sent a code, and fails closed if those limits cannot be checked. Behind that sits a prepaid balance with a floor at zero, so the worst day has a ceiling you set.
- 5 codes per number per hour, 30 seconds between resends, 20 per number range per hour
- A daily cap of 1,000 verifications per account, raised on request
- Embargoed destinations and numbers on your do-not-contact list are refused before anything is charged
- Daily spend and low-balance alerts, the balance.low webhook, and auto-recharge with its own daily cap
Alert me when today's spend exceeds $40.00.
Alert me when my balance drops below $50.00.
You've spent $41.06 today, above your $40.00 daily cap.
One request
Verify a number in two requests
No onboarding call and no waiting for an account manager. Create an account, take your API key and post to the endpoint below, the same one that carries live traffic. Test keys let you try it without sending a real message or call.
curl https://packetexchange.io/api/v1/verify/start \
-H "Authorization: Bearer $PE_KEY" \
-H "Content-Type: application/json" \
-d '{"to":"+447700900123","channel":"voice"}'
curl https://packetexchange.io/api/v1/verify/check \
-H "Authorization: Bearer $PE_KEY" \
-H "Content-Type: application/json" \
-d '{"verificationId":"<id>","code":"482916"}'Built for verification
What sits between your login and the handset
Quality-first routing
Codes default to the best-quality strategy: the most specific prefix, then the highest stated answer rate, with price only breaking a tie.
SMS or voice, one API
The same start and check requests for both channels. Switch a user to a call without changing how you check the code.
Hashed, single-use codes
Only a keyed hash is stored. Five wrong guesses or the expiry ends the verification, and an approved code cannot be used twice.
Risky ranges refused
Premium-rate, satellite and known pumping ranges are refused before a limit is spent or a cent is charged, and so are embargoed destinations.
Limits per number and range
Resend cooldown, hourly caps per number and per number range, and a daily account cap. Two simultaneous resend taps cannot both slip through.
Keys scoped to verify
Give your auth service a key that can start and check verifications and nothing else. It cannot buy routes, top up or read your account.
Seven languages
Message templates in seven languages and spoken codes in six, with your brand name in the text or read out at the start of the call.
A record of every attempt
Look up any verification for its status, attempts and the cost of its message or call, and find the send itself in your message history and CDRs.
Why PacketExchange
Verification without the verification tax
A one-time code is a short message or a short call. Here it is priced like one.
No per-verification fee
A code costs the route's rate for the SMS or call, plus a platform fee capped at $0.001. Nothing is added per attempt, per successful check or per month.
Built to land, not just to leave
Quality-first routing and stated route tiers put codes on the paths most likely to reach the handset, because a code that never arrives is a sign-up that never finishes.
Exposure capped in advance
Blocked ranges, per-number limits, a prepaid balance with a floor at zero and a daily cap on auto-recharge put a number on your worst day before it happens.
Hosted or yours, same price
Use the Verify API, or keep code generation in your own app and send through the SMS or voice API. Both bill the same way, so there is nothing to migrate away from.
Side by side
A typical verify product, and this one
- What you pay
- A typical verify product: A price per verification, on top of the messages it sends
- On PacketExchangeThe route's rate for the SMS or call, plus a fee capped at $0.001
- Channels
- A typical verify product: Voice sold as a separate add-on
- On PacketExchangeSMS and voice through the same start and check requests
- Route choice
- A typical verify product: Decided inside the provider, out of view
- On PacketExchangeBest quality by default, or cheapest, balanced or a route you bought
- Spend exposure
- A typical verify product: Often postpaid, so a pumping attack surfaces on the invoice
- On PacketExchangeBlocked risky ranges, per-number limits and a prepaid balance with a floor at zero
- Retries
- A typical verify product: A double tap can send two codes
- On PacketExchangeA 30-second resend cooldown per number, claimed atomically
| A typical verify product | On PacketExchange | |
|---|---|---|
| What you pay | A price per verification, on top of the messages it sends | The route's rate for the SMS or call, plus a fee capped at $0.001 |
| Channels | Voice sold as a separate add-on | SMS and voice through the same start and check requests |
| Route choice | Decided inside the provider, out of view | Best quality by default, or cheapest, balanced or a route you bought |
| Spend exposure | Often postpaid, so a pumping attack surfaces on the invoice | Blocked risky ranges, per-number limits and a prepaid balance with a floor at zero |
| Retries | A double tap can send two codes | A 30-second resend cooldown per number, claimed atomically |
Built for
Who runs traffic on it
Login 2FA
A second factor at sign-in and for step-up checks.
Transaction signing
Confirm payments and account changes.
Sign-up checks
Prove the number is real before the account is.
Pricing
A code costs what its message or call costs
- verification fee on top
- $0
- platform fee on the carrier cost
- 2%
- fee cap per code
- $0.001
An SMS code is billed per segment at the route's rate. A voice code is billed at the route's per-minute rate on its billing increment. Either way the platform fee is 2% of that amount, never above $0.001, and there is no verification fee or speech surcharge on top.
No monthly fee and no minimum volume. Fund a prepaid balance from $5 by card or crypto, or from $100 by bank wire.
What you pay for
Route rate
Per SMS segment or per call, as listed on the route for the destination's longest matching prefix.
Platform fee
2% of the carrier cost, capped at $0.001 per code.
Refused sends
An SMS a carrier refuses is reversed in full and does not count against the number's hourly limit.
Rates in the open
Every SMS and voice route shows its rate before you send, on the marketplace and on developer pricing.
Test keys run the whole flow on invite-only sandbox credit, return the code so you can check it, and deliver nothing.
Questions
What security and growth teams ask
The questions buyers ask before they start. Anything not covered here is in the help centre, or ask the team directly.
Do I have to use the Verify API?
No. The Verify API generates, sends and checks the code for you. If you would rather keep codes in your own app, send them through POST /comms/sms in your own wording, or have a call speak them with POST /comms/voice-otp. The price is the same either way: the message or call.
Does it fall back from SMS to voice automatically?
No, you decide. If a carrier refuses the SMS, the start request returns an error that suggests channel voice, and the refused attempt does not use up the number's hourly limit. Start again with channel voice and the same check request works.
What does one code cost?
For SMS, the route's per-segment rate for the destination plus 2%, capped at $0.001. A short code message fits in one segment. For voice, the route's per-minute rate for the billed seconds of a short call, plus the same capped fee. The cost of each send is recorded against the verification.
How do I limit what an SMS pumping attack can cost?
The Verify API already refuses premium-rate and known pumping ranges and caps codes per number and per number range. Add a prepaid balance sized to a few normal days, a daily spend alert, a capped auto-recharge, and bot checks in your sign-up form where you can see the requests.
Which sender or caller ID is used?
SMS codes go from your brand name as an alphanumeric sender unless you pass your own. Every SMS route lists whether it carries alphanumeric, numeric or pre-registered senders before you buy. A voice code presents the caller ID you pass in from.
Can I test the whole flow first?
Yes. A test key runs against invite-only sandbox credit. The start is routed and priced on a real route, nothing is delivered, and the reply includes the code as testCode so you can complete the check in your test suite.