ShunVerified API v1
Home

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.

Base URL: All API requests go to https://backend.shunverified.xyz
Credits required: Your ShunVerified account balance is used for all API requests. Top up at shunverified.xyz/deposit.html before integrating.

What you can build

1

Automated account creation tools

Rent numbers on-demand, receive codes, complete verification flows automatically.

2

SaaS verification services

Build your own SMS verification product on top of our infrastructure and resell it.

3

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.

HTTP Header
Authorization: Bearer sv_live_your_api_key_here

Getting your API key

1

Go to Settings

Log in to your ShunVerified account and open Settings.

2

Generate your key

Scroll to the Developer API section and click Generate Key.

3

Copy it immediately

The key is shown only once. Save it in your environment variables — never in your source code.

Never commit your API key to Git or expose it in frontend JavaScript. Store it in environment variables on your server (process.env.SHUNVERIFIED_API_KEY).

Quick Start

Rent a number and get an SMS code in under 5 minutes.

Node.js
Python
PHP
cURL
javascript
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();
python
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
<?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;
}
?>
bash
# 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 balance = your users' purchasing power. When a user on your platform rents a number, credits are deducted from YOUR ShunVerified account. You top up once, your app uses it. Think of it like how Twilio works — you prepay, your code spends it.
1

Your user wants to verify a service

A user on your site clicks "Get verification number" for WhatsApp, Telegram, etc.

2

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.

3

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.

4

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.

5

Automatic refund if no code

Numbers expire after 5 minutes. If no SMS arrives, your credits are automatically refunded — no action needed.

Balance

GET /api/v1/balance Get your current credit balance
200 OK
response
{
  "success": true,
  "data": {
    "balance": 14.50,
    "currency": "USD",
    "tier": "silver"
  }
}

Services

GET /api/v1/services List all supported services
200 OK
response
{
  "success": true,
  "data": [
    { "code": "whatsapp", "name": "WhatsApp" },
    { "code": "telegram", "name": "Telegram" },
    // ... 40+ more
  ],
  "count": 43
}

Countries

GET /api/v1/countries List all supported countries
200 OK
response
{
  "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.

GET /api/v1/price Get price for a service + country

Query Parameters

ParameterTypeRequiredDescription
servicestringrequiredService code (e.g. whatsapp)
countrystringrequiredCountry code (e.g. ng)
200 Available
response
{
  "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.

POST /api/v1/numbers Rent a number for SMS verification

Request Body (JSON)

FieldTypeRequiredDescription
servicestringrequiredService code from /api/v1/services
countrystringrequiredCountry code from /api/v1/countries
201 Created
response
{
  "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..."
  }
}
402 Insufficient Balance
response
{
  "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.

GET /api/v1/numbers/:id Get number status and SMS code
200 — code received
response
{
  "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

DEL /api/v1/numbers/:id Cancel and get refunded
Cannot cancel once the SMS code has been received.
200 Refunded
response
{
  "success": true,
  "message": "Number cancelled and refunded.",
  "data": { "refunded": 2.50, "currency": "USD" }
}

Number History

GET /api/v1/numbers?limit=20&page=1 Your paginated number history
ParameterTypeDefaultDescription
limitinteger20Results per page (max 100)
pageinteger1Page number

Polling Guide

Best practices for waiting for the SMS code.

Poll every 5 seconds. Numbers expire after 5 minutes (300 seconds). That gives you a maximum of 60 polls.

Status values explained

StatusMeaningAction
waitingNumber rented, waiting for SMSKeep polling
completedSMS received — code is in the code fieldRead the code, stop polling
expired5 minutes passed, no SMS — balance refundedStop polling, credits returned

Error Codes

HTTPerror fieldMeaning
401missing_api_keyNo Authorization header sent
401invalid_api_keyKey not found or revoked
401api_key_disabledKey exists but is disabled
400invalid_paramsMissing or invalid service/country
402insufficient_balanceNot enough credits — top up at shunverified.xyz
404no_stockNo numbers available right now — retry in 1-2 min
404not_foundNumber ID doesn't exist or doesn't belong to you
400already_receivedTried to cancel after code arrived
503no_numbers_availableProvider has no stock — balance refunded automatically
500server_errorSomething went wrong on our end — retry

Full Example

A complete production-ready helper function in Node.js.

javascript — production helper
// 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' }