Overview
The ShunVerified API lets you rent virtual phone numbers and receive SMS verification codes programmatically. Build your own verification flow into any app, website, or tool.
https://backend.shunverified.xyzWhat you can build
Automated account creation tools
Rent numbers on-demand, receive codes, complete verification flows automatically.
SaaS verification services
Build your own SMS verification product on top of our infrastructure and resell it.
Browser automation / bots
Pair with Puppeteer, Playwright, or Selenium to complete SMS steps programmatically.
Authentication
All API requests must include your API key in the Authorization header.
Authorization: Bearer sv_live_your_api_key_here
Getting your API key
Go to Settings
Log in to your ShunVerified account and open Settings.
Generate your key
Scroll to the Developer API section and click Generate Key.
Copy it immediately
The key is shown only once. Save it in your environment variables — never in your source code.
process.env.SHUNVERIFIED_API_KEY).Quick Start
Rent a number and get an SMS code in under 5 minutes.
const API_KEY = process.env.SHUNVERIFIED_API_KEY; const BASE = 'https://backend.shunverified.xyz'; async function getWhatsAppCode() { // 1. Check price first const price = await fetch(`${BASE}/api/v1/price?service=whatsapp&country=ng`, { headers: { 'Authorization': `Bearer ${API_KEY}` } }).then(r => r.json()); console.log(`Cost: $${price.data.price}`); // 2. Rent a number const rental = await fetch(`${BASE}/api/v1/numbers`, { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ service: 'whatsapp', country: 'ng' }) }).then(r => r.json()); console.log(`Number: ${rental.data.phoneNumber}`); // Use this number in WhatsApp's signup form // 3. Poll for the SMS code (every 5 seconds) const id = rental.data.id; for (let i = 0; i < 60; i++) { await new Promise(r => setTimeout(r, 5000)); const status = await fetch(`${BASE}/api/v1/numbers/${id}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } }).then(r => r.json()); if (status.data.code) { console.log(`✅ Code: ${status.data.code}`); return status.data.code; } if (status.data.status === 'expired') break; } console.log('No code received — refund issued automatically'); } getWhatsAppCode();
import requests, time, os API_KEY = os.environ['SHUNVERIFIED_API_KEY'] BASE = 'https://backend.shunverified.xyz' HEADERS = {'Authorization': f'Bearer {API_KEY}'} def get_whatsapp_code(): # 1. Rent a number r = requests.post(f'{BASE}/api/v1/numbers', headers=HEADERS, json={'service': 'whatsapp', 'country': 'ng'}) rental = r.json() number_id = rental['data']['id'] phone = rental['data']['phoneNumber'] print(f'Number: {phone}') # 2. Poll for the code for _ in range(60): time.sleep(5) s = requests.get(f'{BASE}/api/v1/numbers/{number_id}', headers=HEADERS).json() if s['data']['code']: print(f'✅ Code: {s["data"]["code"]}') return s['data']['code'] if s['data']['status'] == 'expired': break print('No code received') get_whatsapp_code()
<?php $apiKey = getenv('SHUNVERIFIED_API_KEY'); $base = 'https://backend.shunverified.xyz'; function apiCall($method, $path, $body = null) { global $apiKey, $base; $ch = curl_init($base . $path); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$apiKey}", 'Content-Type: application/json' ], CURLOPT_POSTFIELDS => $body ? json_encode($body) : null ]); $res = curl_exec($ch); curl_close($ch); return json_decode($res, true); } // 1. Rent a number $rental = apiCall('POST', '/api/v1/numbers', ['service' => 'whatsapp', 'country' => 'ng']); $id = $rental['data']['id']; echo "Number: {$rental['data']['phoneNumber']}\n"; // 2. Poll for the code for ($i = 0; $i < 60; $i++) { sleep(5); $s = apiCall('GET', "/api/v1/numbers/{$id}"); if (!empty($s['data']['code'])) { echo "✅ Code: {$s['data']['code']}\n"; break; } if ($s['data']['status'] === 'expired') break; } ?>
# Set your key export API_KEY="sv_live_your_key_here" BASE="https://backend.shunverified.xyz" # Rent a number curl -X POST "$BASE/api/v1/numbers" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"service":"whatsapp","country":"ng"}' # Check status (replace NUMBER_ID) curl "$BASE/api/v1/numbers/NUMBER_ID" \ -H "Authorization: Bearer $API_KEY"
How It Works
Understanding the flow before you build.
Your user wants to verify a service
A user on your site clicks "Get verification number" for WhatsApp, Telegram, etc.
Your backend calls our API
Your server (never frontend) calls POST /api/v1/numbers with the service and country. Credits are deducted from your ShunVerified balance.
You receive a phone number
We return a real phone number instantly. You show it to your user — they enter it in the service they're verifying.
Poll for the SMS code
Your backend polls GET /api/v1/numbers/:id every 5 seconds. When the code field is non-null, the SMS arrived.
Automatic refund if no code
Numbers expire after 5 minutes. If no SMS arrives, your credits are automatically refunded — no action needed.
Balance
{
"success": true,
"data": {
"balance": 14.50,
"currency": "USD",
"tier": "silver"
}
}Services
{
"success": true,
"data": [
{ "code": "whatsapp", "name": "WhatsApp" },
{ "code": "telegram", "name": "Telegram" },
// ... 40+ more
],
"count": 43
}Countries
{
"success": true,
"data": [
{ "code": "us", "name": "United States" },
{ "code": "ng", "name": "Nigeria" },
{ "code": "gh", "name": "Ghana" },
// ... 40+ more
]
}Price Check
Always check the price before renting. Prices vary by country and service in real-time.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| service | string | required | Service code (e.g. whatsapp) |
| country | string | required | Country code (e.g. ng) |
{
"success": true,
"data": {
"available": true,
"price": 2.50,
"currency": "USD",
"provider": "fivesim",
"stock": 42
}
}Rent a Number
Deducts credits from your balance and returns a real phone number immediately.
Request Body (JSON)
| Field | Type | Required | Description |
|---|---|---|---|
| service | string | required | Service code from /api/v1/services |
| country | string | required | Country code from /api/v1/countries |
{
"success": true,
"data": {
"id": "684a2f...", // Save this — needed to check status
"phoneNumber": "+2348012345678",
"service": "whatsapp",
"country": "ng",
"status": "waiting",
"charged": 2.50,
"currency": "USD",
"expiresIn": 300, // seconds
"createdAt": "2026-03-06T..."
}
}{
"success": false,
"error": "insufficient_balance",
"data": { "required": 2.50, "available": 1.20, "shortfall": 1.30 }
}Check Status
Poll this endpoint every 5 seconds after renting until status becomes completed or expired.
{
"success": true,
"data": {
"id": "684a2f...",
"phoneNumber": "+2348012345678",
"status": "completed", // waiting | completed | expired
"code": "483920", // null until received
"charged": 2.50,
"timeRemaining": 187 // seconds left
}
}Cancel a Number
{
"success": true,
"message": "Number cancelled and refunded.",
"data": { "refunded": 2.50, "currency": "USD" }
}Number History
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 20 | Results per page (max 100) |
| page | integer | 1 | Page number |
Polling Guide
Best practices for waiting for the SMS code.
Status values explained
Error Codes
Full Example
A complete production-ready helper function in Node.js.
// shunverified.js — drop this in your project const BASE = 'https://backend.shunverified.xyz'; class ShunVerified { constructor(apiKey) { this.apiKey = apiKey; } async _fetch(method, path, body) { const res = await fetch(`${BASE}${path}`, { method, headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' }, body: body ? JSON.stringify(body) : undefined }); const data = await res.json(); if (!data.success) throw new Error(data.message || data.error); return data.data; } balance() { return this._fetch('GET', '/api/v1/balance'); } services() { return this._fetch('GET', '/api/v1/services'); } price(service, country) { return this._fetch('GET', `/api/v1/price?service=${service}&country=${country}`); } rent(service, country) { return this._fetch('POST', '/api/v1/numbers', { service, country }); } check(id) { return this._fetch('GET', `/api/v1/numbers/${id}`); } cancel(id) { return this._fetch('DELETE', `/api/v1/numbers/${id}`); } /** * Rent a number and wait for the code automatically. * Returns the SMS code string, or null if expired. */ async rentAndWait(service, country, { pollInterval = 5000, maxWait = 300000 } = {}) { const rental = await this.rent(service, country); console.log(`📱 Number: ${rental.phoneNumber}`); const deadline = Date.now() + maxWait; while (Date.now() < deadline) { await new Promise(r => setTimeout(r, pollInterval)); const status = await this.check(rental.id); if (status.code) { console.log(`✅ Code: ${status.code}`); return { phoneNumber: rental.phoneNumber, code: status.code }; } if (status.status === 'expired') { console.log('⏰ Expired — credits refunded'); return null; } } return null; } } // Usage const sv = new ShunVerified(process.env.SHUNVERIFIED_API_KEY); const result = await sv.rentAndWait('whatsapp', 'ng'); // result = { phoneNumber: '+234...', code: '483920' }