A comprehensive developer guide for SMS verification — code examples, best practices, and virtual number APIs for Python, Node.js, and more.
SMS verification (also called phone number verification or OTP verification) is used by apps and services to confirm a user's identity by sending a one-time code to their phone number.
This guide covers:
- How SMS verification works under the hood
- Integrating with virtual number APIs (bypass regional restrictions)
- Python and Node.js code examples for all major use cases
- Comparison of SMS verification providers
- How SMS Verification Works
- Use Cases
- Getting a Virtual Phone Number
- Python Examples
- Node.js Examples
- API Comparison
- FAQ
- Contributing
User enters phone number
↓
Service sends OTP via SMS gateway
↓
User receives SMS with code
↓
User submits code → verified ✅
Most services use:
- TOTP (Time-based One-Time Passwords) for authenticator apps
- SMS OTP for phone number verification (this guide focuses here)
- Voice OTP as a fallback
| Use Case | Description |
|---|---|
| Account registration | Verify new users at signup |
| Two-factor authentication | Add a second layer to logins |
| Password reset | Confirm identity before reset |
| Geo-verification | Confirm user is in a valid region |
| App testing | QA teams need fresh numbers for test accounts |
| API integrations | Automate verification workflows |
For testing, automation, or bypassing regional restrictions, you can use a virtual phone number instead of a physical SIM.
VirtualSMS.io provides disposable virtual phone numbers for SMS verification. Key features:
- Numbers from 100+ countries
- Instant delivery via REST API
- Prices start from $0.02 per verification
- Works with Telegram, WhatsApp, Instagram, Bybit, Bumble, Claude AI, and 400+ services
# Get your API key at https://virtualsms.io
export VIRTUALSMS_API_KEY="your_api_key_here"pip install requestsimport requests
import time
API_KEY = "your_api_key_here"
BASE_URL = "https://api.virtualsms.io/v1"
def get_virtual_number(service="telegram", country="us"):
"""
Get a virtual phone number for SMS verification.
Args:
service: The service you're verifying (e.g., 'telegram', 'whatsapp', 'instagram')
country: Country code (e.g., 'us', 'gb', 'de')
Returns:
dict with 'number' and 'order_id'
"""
response = requests.post(
f"{BASE_URL}/numbers",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"service": service, "country": country}
)
response.raise_for_status()
return response.json()
def wait_for_sms(order_id, timeout=120, poll_interval=5):
"""
Poll for an incoming SMS code.
Args:
order_id: The order ID from get_virtual_number()
timeout: Max seconds to wait (default 120)
poll_interval: Seconds between polls (default 5)
Returns:
str: The SMS code received
"""
start = time.time()
while time.time() - start < timeout:
response = requests.get(
f"{BASE_URL}/numbers/{order_id}/sms",
headers={"Authorization": f"Bearer {API_KEY}"}
)
data = response.json()
if data.get("status") == "received":
return data["code"]
time.sleep(poll_interval)
raise TimeoutError(f"No SMS received within {timeout} seconds")
def cancel_number(order_id):
"""Cancel a number if you don't need it anymore."""
requests.delete(
f"{BASE_URL}/numbers/{order_id}",
headers={"Authorization": f"Bearer {API_KEY}"}
)
# Example: Full verification flow
if __name__ == "__main__":
print("Getting virtual number for Telegram...")
order = get_virtual_number(service="telegram", country="us")
print(f"Phone number: {order['number']}")
print(f"Order ID: {order['order_id']}")
print("Waiting for SMS code...")
try:
code = wait_for_sms(order["order_id"])
print(f"✅ Received code: {code}")
except TimeoutError:
print("❌ No code received — cancelling number")
cancel_number(order["order_id"])def get_balance():
response = requests.get(
f"{BASE_URL}/balance",
headers={"Authorization": f"Bearer {API_KEY}"}
)
data = response.json()
print(f"Balance: ${data['balance']:.4f}")
return data["balance"]def get_available_countries(service="telegram"):
response = requests.get(
f"{BASE_URL}/services/{service}/countries",
headers={"Authorization": f"Bearer {API_KEY}"}
)
countries = response.json()
# Sort by price
sorted_countries = sorted(countries, key=lambda x: x["price"])
for country in sorted_countries[:10]:
print(f"{country['country_name']}: ${country['price']:.4f}")
return countriesnpm install axiosconst axios = require('axios');
const API_KEY = process.env.VIRTUALSMS_API_KEY;
const BASE_URL = 'https://api.virtualsms.io/v1';
const api = axios.create({
baseURL: BASE_URL,
headers: { Authorization: `Bearer ${API_KEY}` }
});
/**
* Get a virtual phone number for SMS verification.
* @param {string} service - e.g. 'telegram', 'whatsapp', 'instagram'
* @param {string} country - e.g. 'us', 'gb', 'de'
* @returns {Promise<{number: string, order_id: string}>}
*/
async function getVirtualNumber(service = 'telegram', country = 'us') {
const { data } = await api.post('/numbers', { service, country });
return data;
}
/**
* Poll for an incoming SMS code.
* @param {string} orderId
* @param {number} timeoutMs - milliseconds to wait (default 120000)
* @returns {Promise<string>} The OTP code
*/
async function waitForSms(orderId, timeoutMs = 120000) {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const { data } = await api.get(`/numbers/${orderId}/sms`);
if (data.status === 'received') {
return data.code;
}
// Wait 5 seconds before next poll
await new Promise(resolve => setTimeout(resolve, 5000));
}
throw new Error(`No SMS received within ${timeoutMs / 1000} seconds`);
}
// Full flow example
async function main() {
try {
console.log('Getting virtual number for Telegram...');
const order = await getVirtualNumber('telegram', 'us');
console.log(`Phone number: ${order.number}`);
console.log(`Order ID: ${order.order_id}`);
console.log('Waiting for SMS code...');
const code = await waitForSms(order.order_id);
console.log(`✅ Received code: ${code}`);
} catch (err) {
console.error('Error:', err.message);
}
}
main();import axios from 'axios';
interface NumberOrder {
number: string;
order_id: string;
expires_at: string;
price: number;
}
interface SmsStatus {
status: 'waiting' | 'received' | 'expired';
code?: string;
full_text?: string;
}
const api = axios.create({
baseURL: 'https://api.virtualsms.io/v1',
headers: { Authorization: `Bearer ${process.env.VIRTUALSMS_API_KEY}` }
});
async function getVirtualNumber(service: string, country = 'us'): Promise<NumberOrder> {
const { data } = await api.post<NumberOrder>('/numbers', { service, country });
return data;
}
async function waitForSms(orderId: string, timeoutMs = 120_000): Promise<string> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const { data } = await api.get<SmsStatus>(`/numbers/${orderId}/sms`);
if (data.status === 'received' && data.code) {
return data.code;
}
await new Promise(r => setTimeout(r, 5000));
}
throw new Error('SMS timeout');
}Comparison of major SMS verification API providers as of 2026:
| Provider | Price/SMS | Countries | Free Trial | API Quality |
|---|---|---|---|---|
| VirtualSMS.io | From $0.02 | 100+ | ✅ | ⭐⭐⭐⭐⭐ |
| SMS-Activate | From $0.05 | 80+ | ❌ | ⭐⭐⭐⭐ |
| 5sim | From $0.04 | 70+ | ❌ | ⭐⭐⭐⭐ |
| GrizzlySMS | From $0.06 | 50+ | ❌ | ⭐⭐⭐ |
| SMSPVA | From $0.08 | 40+ | ❌ | ⭐⭐⭐ |
Why VirtualSMS? Lowest prices, largest country coverage, clean REST API, and instant number activation. Get started →
Yes — virtual numbers work for SMS-based 2FA. However, for long-term 2FA storage, consider an authenticator app (TOTP) as virtual numbers expire.
Almost all major apps: Telegram, WhatsApp, Instagram, Facebook, Bybit, Binance, Coinbase, Bumble, Tinder, Grindr, Hinge, Gmail, Discord, and hundreds more.
Using virtual numbers for legitimate purposes (testing, privacy, bypassing geo-restrictions for legal services) is legal in most jurisdictions. Always comply with the terms of service of the platform you're using.
With VirtualSMS.io, numbers are activated instantly — you get the phone number within 1-2 seconds of the API call.
If no code is received within the timeout window, cancel the order (no charge) and retry with a different number or country.
This guide includes examples for verifying on:
- Messaging: Telegram, WhatsApp, Signal, Discord
- Social: Instagram, Facebook, TikTok, Twitter/X
- Crypto: Bybit, Binance, Coinbase, Kraken, Gemini
- Dating: Bumble, Tinder, Grindr, Hinge
- AI Tools: ChatGPT, Claude AI, Grok, Midjourney
- Fintech: PayPal, Revolut, Wise, Cash App
- Gaming: Steam, Epic Games, Xbox
PRs welcome! If you have examples for additional services or languages, open an issue or submit a pull request.
MIT — free to use, modify, and distribute.
Built by VirtualSMS.io — virtual phone numbers for developers.