Developers
Temporary SMS API for QA and staging
GetVerifyCode gives your server temporary virtual numbers for inbound SMS. Use shared free numbers for public inbox checks, or paid private activations when you need isolation. Lawful QA, staging, privacy, and travel only.
Base URL https://getverifycode.com/api/v1. Money is integer USD cents. The client cannot set the price. Retail cents on /api/v1 use API markup from admin Pricing and profit, so they can differ from website shop prices. Machine spec: /openapi.yaml.
Responses expose retail cents, stock, phone numbers, SMS, and vendor-neutral package labels only. They never name wholesale suppliers, upstream order ids, adapter names, or cost. Errors stay generic (no_stock, provider_unavailable, and similar).
- Shared free numbers are public inboxes. No wallet and no API key. Anyone can read the same messages.
- Private activations need a bearer token, confirmed email, and wallet credits (crypto top-ups from $1). Unused holds return if no SMS arrives before the timeout.
- There is no free paid-activation quota. Many apps reject VoIP or recycled numbers. Delivery is not guaranteed.
- Allowed use is in the Acceptable Use Policy.
Quick start
- Call GET /catalog/meta and check free_numbers and that the v1 surface is live.
- For a public demo, list free-number countries, pick a number, and poll messages. No Authorization header.
- For a private OTP, create an account, confirm email, create an API key, top up, then quote → buy → poll → cancel on /activations.
Prefer private activations for secrets. A personal SIM in staging leaks OTPs into production accounts and breaks CI.
Create a key
You need an account. We show the token once. A new key replaces the old one. Keys last one year.
Sign in first. Then you can create a key.
Authentication
Send this header on private requests. Catalog, health, packages, and free numbers stay public when their product flags allow them.
Authorization: Bearer YOUR_API_KEY
Errors
Domain failures use this shape. Upstream payloads, keys, and internal hostnames are never included. Validation failures from form requests use Laravel's message and errors object instead.
{
"error": {
"code": "insufficient_funds",
"message": "Insufficient available balance."
}
}HTTP 503
{
"error": {
"code": "public_api_off",
"message": "The public API is paused."
}
}Missing or invalid tokens return HTTP 401 with Unauthenticated. Another user's activation, top-up, or key returns HTTP 404.
Health
Liveness for the public v1 surface. No token. Still returns 503 when the public API flag is off.
/healthPubliccurl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/health'
{
"status": "ok"
}Catalog
Public. No token. Same surface the website buy panel uses. Retail cents and live stock only. No wholesale fields and no supplier names. Sample prices below are examples. fresh=1 on offers and routes busts the short cache.
/catalog/servicesPubliccurl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/catalog/services'
{
"services": [
{
"slug": "whatsapp",
"name": "WhatsApp",
"from_cents": 52,
"in_stock": true,
"stock": 12
}
]
}/catalog/countriesPublicFull country directory. No retail required.
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/catalog/countries'
/catalog/countries/{country}/servicesPublicServices with retail cents and stock for one country.
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/catalog/countries/US/services'
/catalog/services/{service}/countriesPublicOptional query operator, default any. Prefer /destinations when you want the shop-style country list (including Virtual and Rent rows).
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/catalog/services/whatsapp/countries?operator=any'
{
"service": "whatsapp",
"countries": [
{
"iso": "US",
"slug": "usa",
"name": "United States",
"code": "US",
"dial": "+1",
"in_stock": true,
"retail_cents": 52
}
]
}/catalog/services/{service}/destinationsPublicShop country picker. Each row includes destination (any, virtual, or rent), product_kind, and a display package_label. When you call routes, send the ISO in country and map rent or virtual rows with destination.
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/catalog/services/whatsapp/destinations'
{
"service": "whatsapp",
"destinations": [
{
"iso": "US",
"slug": "usa",
"name": "United States",
"code": "US",
"dial": "+1",
"in_stock": true,
"retail_cents": 111,
"stock": 449,
"destination": "any",
"package_label": "United States",
"operator_hint": "any",
"product_kind": "activation",
"duration_label": null
}
]
}/catalog/services/{service}/offersPublicLegacy flat offer rows. Prefer /routes for the package cards the buy panel shows.
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/catalog/services/whatsapp/offers'
Free numbers
Shared public inboxes for lawful QA demos. No bearer token. No wallet charge. Anyone can read the same messages, so never use these for private accounts. When catalog/meta.free_numbers is false, these routes return HTTP 404. Shop cookie routes under /api/free-numbers match the same payloads. UI mirror: /free-numbers.
/free-numbers/countriesPublic, flag oncurl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/free-numbers/countries'
{
"countries": [
{
"code": "1",
"iso": "US",
"slug": "united-states",
"name": "United States",
"dial": "+1",
"number_count": 12
}
]
}/free-numbers/countries/{country}/numbersPublic, flag oncountry may be a dial code (1), ISO, or slug (united-states).
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/free-numbers/countries/1/numbers'
{
"country": "1",
"numbers": [
{
"number": "2025550100",
"phone": "+12025550100",
"last_seen": "1 min",
"is_archive": false
}
]
}/free-numbers/countries/{country}/numbers/{number}/messagesPublic, flag onOptional page query (default 1). Bodies may include an extracted otp_code when present.
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/free-numbers/countries/1/numbers/2025550100/messages?page=1'
{
"country": "1",
"number": "2025550100",
"messages": [
{
"id": "msg-1",
"sender": "WhatsApp",
"body": "Your code is 482913",
"otp_code": "482913",
"received_at": "2026-09-26T10:00:00Z",
"received_label": "1 min"
}
],
"page": 1,
"last_page": 1
}Packages
After the user picks a service and country, call routes. That is the same package list as the right-hand buy panel: Open stock, Ranked delivery, Virtual, Wide stock, US mobile (US only), and Rent day packages when rentals are on. Only in-stock packages are returned. Labels and operators match the panel. Retail cents follow API markup, not the website shop markup. Pass the chosen operator into quote and buy.
| Operator | Buy panel label | Notes |
|---|---|---|
| route_open | Open stock | Any working number from open stock. |
| route_ranked | Ranked delivery | Prefers stronger delivery history. |
| route_virtual | Virtual | Hosted numbers. Some apps reject them. |
| route_wide | Wide stock | Extra stock when other routes are thin. |
| route_us | US mobile | United States only. |
| rent_{days} | Rent N days | When destination=rent and rentals are enabled. |
/catalog/services/{service}/routesPublicRequired query country (ISO-2). Optional destination=any|rent and fresh=1. Each card is live-verified. Empty or out-of-stock packages are omitted. Fields match the shop: label, lane, blurb, package_label, product_kind, retail_cents, stock, optional rate (delivery success percent when known).
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/catalog/services/whatsapp/routes?country=US&destination=any'
{
"service": "whatsapp",
"country": "US",
"destination": "any",
"routes": [
{
"service": "whatsapp",
"operator": "route_ranked",
"iso": "US",
"slug": "usa",
"name": "USA",
"code": "US",
"label": "Ranked delivery",
"lane": "Ranked delivery",
"blurb": "Prefers numbers with stronger delivery history.",
"product_kind": "activation",
"duration_label": null,
"duration_days": null,
"retail_cents": 111,
"stock": 449,
"in_stock": true,
"rate": 6.52,
"package_label": "US · Ranked delivery",
"verified": true
},
{
"service": "whatsapp",
"operator": "route_open",
"iso": "US",
"slug": "usa",
"name": "USA",
"code": "US",
"label": "Open stock",
"lane": "Open stock",
"blurb": "Any working number from open stock.",
"product_kind": "activation",
"duration_label": null,
"duration_days": null,
"retail_cents": 312,
"stock": 113184,
"in_stock": true,
"rate": null,
"package_label": "US · Open stock",
"verified": true
}
]
}For rentals, call the same routes endpoint with destination=rent. Each returned operator looks like rent_7 with duration_days and product_kind: "rental". Feature-flagged helpers also exist:
/catalog/rentals/servicesPublic/catalog/rentals/countriesPublicEmpty when rentals are off. Optional country on services and service on countries.
/catalog/quotePublicRequired service and country. Pass the package operator from routes (for example route_open). Default any still works as a best-available pick. Response includes the same lane and product_kind the panel shows.
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/catalog/quote?service=whatsapp&country=US&operator=route_open'
{
"service": "whatsapp",
"country": "US",
"operator": "route_open",
"retail_cents": 312,
"in_stock": true,
"currency": "USD",
"lane": "Open stock",
"product_kind": "activation",
"duration_label": null,
"duration_days": null
}Account
Token required. Same wallet as the website. available_cents can be spent. held_cents are reserved for a live number.
/meBearercurl -s -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/me'
{
"user": {
"id": 1,
"name": "Ada",
"email": "[email protected]",
"email_verified": true,
"banned": false,
"max_purchase_cents": null,
"google": false,
"referral_code": "ADA12",
"totp_enabled": false,
"wallet": {
"available_cents": 1000,
"held_cents": 0,
"currency": "USD"
}
}
}Wallet
/walletBearercurl -s -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/wallet'
{
"available_cents": 1000,
"held_cents": 0,
"currency": "USD"
}/wallet/ledgerBearerLaravel pagination, 25 rows per page. Use ?page=2. Amounts are integer cents.
curl -s -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/wallet/ledger'
{
"data": [
{
"id": 10,
"type": "top_up",
"amount_cents": 500,
"available_delta": 500,
"held_delta": 0,
"available_after": 1500,
"held_after": 0,
"reference_type": "top_up",
"reference_id": 3,
"created_at": "2026-09-18T12:00:00+00:00"
}
],
"current_page": 1,
"per_page": 25,
"total": 1
}/wallet/packagesPublicNo token. Package cents come from shop settings. Soft floor is min_custom_cents (default 100). Each coin may require more via min_credit_cents. Max is max_topup_cents (default 50000). crypto_services lists live Cryptomus coins and networks for invoice creation.
curl -s -H 'Accept: application/json' 'https://getverifycode.com/api/v1/wallet/packages'
{
"packages_cents": [500, 1000, 2500, 5000, 10000],
"currency": "USD",
"min_cents": 100,
"custom_topup_enabled": true,
"min_custom_cents": 100,
"min_custom_cents": 100,
"crypto_enabled": true,
"withdrawals_enabled": false,
"min_withdraw_cents": 500,
"withdraw_currency": "USDT",
"withdraw_network": "TRON",
"crypto_fee_bps": 65,
"crypto_min_usdt": "0.5",
"crypto_pay_currency": "USDT",
"crypto_pay_network": "TRON",
"crypto_invoice_lifetime_seconds": 3600,
"crypto_services": [
{
"currency": "USDT",
"network": "TRON",
"is_available": true,
"min_amount": "1",
"max_amount": "100000",
"fee_percent": "0.65",
"rate_usd": "1",
"min_credit_cents": 100,
"blockchain_label": "TRC20"
}
]
}Top-ups
Token required. Email must be verified. amount_cents may be a listed package or any custom amount in the allowed min/max range. Optional to_currency and network pick the Cryptomus pay method from crypto_services. Omit them to use the preferred USDT TRON default when available.
The response includes a payment object with the deposit address, network, exact payer amount, and expiry. Build your own QR from payment.address. checkout_url may still be present for reference. Credits land after the Cryptomus payment webhook, not when the invoice is created. Crypto top-ups are non-refundable. Repeat the same idempotency_key (8 to 80 characters) to avoid a double invoice.
/wallet/top-ups/cryptomusBearer{
"amount_cents": 500,
"idempotency_key": "topup-2026-09-18-01",
"to_currency": "USDT",
"network": "TRON"
}curl -s -X POST -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"amount_cents":500,"idempotency_key":"topup-2026-09-18-01","to_currency":"USDT","network":"TRON"}' \
'https://getverifycode.com/api/v1/wallet/top-ups/cryptomus'{
"top_up": {
"id": 12,
"provider": "cryptomus",
"status": "pending",
"amount_cents": 500,
"credited_cents": null,
"commission_cents": null,
"merchant_net_cents": null,
"paid_usd_cents": null,
"checkout_url": "https://pay.cryptomus.com/pay/...",
"paid_at": null,
"payment": {
"address": "TXyz...",
"network": "TRON",
"payer_amount": "5.033",
"payer_currency": "USDT",
"amount_usd": "5.00",
"expired_at": "2026-09-22T02:00:00+00:00",
"fee_bps": 65,
"min_usdt": "0.5",
"lifetime_seconds": 3600
}
}
}/wallet/top-upsBearerLatest 20 top-ups for the authenticated account.
curl -s -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/wallet/top-ups'
/wallet/top-ups/{id}Bearer, ownercurl -s -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/wallet/top-ups/12'
/wallet/top-ups/{id}/cancelBearer, ownerCancels a pending invoice only. No credits were added. Send a new create call if the user changes coin or amount.
curl -s -X POST -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' -d '{}' \
'https://getverifycode.com/api/v1/wallet/top-ups/12/cancel'Top-up status is pending, paid, failed, cancelled, or reversed.
Withdrawals
Cash-out is paused on GetVerifyCode. Wallet credit is for buying numbers. When enabled later, withdrawals will be USDT on TRON (TRC20), minimum 500 cents, behind the wallet_withdrawals flag. Endpoints below stay in the contract for that future toggle.
/wallet/withdrawalsBearer{
"amount_cents": 500,
"address": "TDD97yguPESTpcrJMqU6h2ozZbibv4Vaqm",
"idempotency_key": "wd-2026-09-21-01"
}curl -s -X POST -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"amount_cents":500,"address":"TDD97yguPESTpcrJMqU6h2ozZbibv4Vaqm","idempotency_key":"wd-2026-09-21-01"}' \
'https://getverifycode.com/api/v1/wallet/withdrawals'/wallet/withdrawalsBearer/wallet/withdrawals/{id}Bearer, ownerStatus values: pending_review, processing, needs_review, paid, rejected, failed, cancelled. Idempotency keys are unique per user.
Activations
Holds retail cents, then returns a GetVerifyCode activation id and phone. Use the same operator from the packages list (for example route_open). Repeat the same idempotency key to avoid a double buy. A sent retail_cents field is ignored. Responses never include wholesale cents, supplier names, or upstream order ids.
/activationsBearer{
"service": "whatsapp",
"country": "US",
"operator": "route_open",
"idempotency_key": "buy-2026-09-18-01"
}curl -s -X POST -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"service":"whatsapp","country":"US","operator":"route_open","idempotency_key":"buy-1"}' \
'https://getverifycode.com/api/v1/activations'{
"activation": {
"id": 88,
"status": "pending",
"service": "whatsapp",
"country": "US",
"operator": "route_open",
"phone_number": "+12015550148",
"retail_cents": 312,
"currency": "USD",
"product_kind": "activation",
"duration_label": null,
"expires_at": "2026-09-18T12:20:00+00:00",
"completed_at": null,
"sms": []
}
}/activations/{id}Bearer, ownerPoll about every 2.5 seconds while status is pending. When an SMS arrives, sms[].otp_code is the parsed code. Unused holds return if you cancel in time or if the 20 minute window ends with no SMS.
curl -s -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/activations/88'
/activations/{id}/cancelBearer, ownercurl -s -X POST -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' \ 'https://getverifycode.com/api/v1/activations/88/cancel'
/activationsBearerOwner list. Latest 50 rows.
curl -s -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/activations'
{
"activations": [
{
"id": 88,
"status": "received",
"service": "whatsapp",
"country": "US",
"operator": "route_open",
"phone_number": "+12015550148",
"retail_cents": 312,
"currency": "USD",
"product_kind": "activation",
"duration_label": null,
"expires_at": "2026-09-18T12:20:00+00:00",
"completed_at": "2026-09-18T12:04:12+00:00",
"sms": [
{
"sender": "WhatsApp",
"body": "Your code is 482193",
"otp_code": "482193"
}
]
}
]
}After purchase
- GET destinations, then GET routes for the chosen country.
- Optional GET quote with that operator to confirm retail cents.
- POST activations with the same operator. Save the id and phone_number.
- Paste the number only where you are allowed to complete a check.
- GET activations/id until sms has otp_code, or until timeout.
- POST cancel if you do not want to wait. That releases the hold.
Status values
- pending. Number is live. Wallet cents are held.
- received. SMS stored. Hold captured.
- canceled. You or support released the hold.
- timeout. Window ended. Hold returned.
- failed. Stock or price check refused the sale. Hold returned.
- refunded. Admin force refund after pending or received.
Refunds
POST /activations/{id}/cancel is the developer refund. It only works while status is pending. The unused hold returns to available cents. The 20 minute timeout does the same and sets status to timeout. After received, there is no public refund route. Poll until status is refunded if an admin credits the captured amount. The website Ask for a refund path opens a billing ticket. Do not add a client-side force refund.
Wallet ledger rows stay integer cents. A cancel writes a release. An admin refund after received writes a credit.
API keys
Create the first key on the website. These routes manage that key after you have one.
/api-keysBearercurl -s -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/api-keys'
{
"keys": [
{
"id": 4,
"name": "developer",
"last_used_at": null,
"expires_at": "2027-09-18T12:00:00+00:00",
"created_at": "2026-09-18T12:00:00+00:00"
}
]
}/api-keysBearercurl -s -X POST -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/api-keys'
{
"key": {
"id": 5,
"name": "developer",
"last_used_at": null,
"expires_at": "2027-09-18T12:00:00+00:00",
"created_at": "2026-09-18T12:00:00+00:00"
},
"token": "5|plaintext-shown-once"
}/api-keys/{id}Bearer, ownercurl -s -X DELETE -H 'Accept: application/json' -H 'Authorization: Bearer YOUR_API_KEY' 'https://getverifycode.com/api/v1/api-keys/5'
{
"ok": true
}Limits
- Catalog, health, packages, and free numbers: 60 requests per minute.
- Buy a number: 30 per hour per account.
- Cancel: 20 per minute.
- Create a key: 10 per minute. Revoke: 20 per minute.
- Start a top-up: 30 per minute.
- Number window is 20 minutes unless shop settings change it.
Error codes
- insufficient_funds. Add credits first. HTTP 422.
- wallet_frozen. Admin froze the wallet. HTTP 403.
- banned. The account is suspended. HTTP 403.
- max_purchase_exceeded. Raise or clear max purchase price.
- negative_margin. That number is unavailable at a safe price.
- provider_unavailable. Numbers are paused or busy right now.
- provider_error. The sale could not be completed. Retry or pick another package.
- no_stock. That package is out of stock.
- invalid_country. That country name is not in the catalog.
- invalid_number. That free number is not available.
- invalid_operator. That package operator is not available.
- not_cancellable. The number is no longer pending.
- public_api_off. The v1 surface is paused. HTTP 503.
Email [email protected] with a token prefix, never the full secret.