Virtual Phone Numbers for Developers: Complete API Guide 2026
Virtual phone numbers aren't just a privacy tool — for developers, they're infrastructure. From integration testing to multi-account management to production SMS workflows, a reliable virtual number API is something every developer building with phone-based services eventually needs.
This guide covers everything: how virtual numbers work at the carrier level, what the API landscape looks like, and a practical reference for integrating them into your stack.
How Virtual Phone Numbers Work
A virtual phone number is a PSTN-routable number without a physical SIM. The carrier assigns a DID (Direct Inward Dial) number that routes to an internet endpoint instead of a handset.
SMS Sender → Mobile Carrier Network
↓
DID Routing Table
↓
Virtual Number Provider API
↓
Your Webhook / Dashboard
VoIP vs. Non-VoIP Numbers
This distinction matters enormously for verification use cases:
| Type | How it works | Platform acceptance |
|---|---|---|
| VoIP | Internet-based (SIP/VoIP) | Blocked by Google, WhatsApp, Meta |
| Non-VoIP | Real SIM-backed carrier | Accepted everywhere |
Services like Google Voice and Skype numbers are VoIP. NumberOTP provides SIM-backed non-VoIP numbers — which pass on platforms that reject VoIP.
Use Cases by Developer Type
Integration Testing
def test_phone_verification_flow():
# Get a test number instead of hardcoding
number_data = numberotp_client.get_number(country="us")
# Trigger SMS verification in your app
response = requests.post("/api/verify/send", json={
"phone": number_data["number"]
})
assert response.status_code == 200
# Verify the SMS was actually sent and received
sms = numberotp_client.wait_for_sms(number_data["id"])
assert "verification" in sms.lower()
# Complete verification
code = extract_otp(sms)
complete = requests.post("/api/verify/confirm", json={"code": code})
assert complete.status_code == 200
Multi-Account Management
Developers building tools that manage multiple accounts across platforms need unique, reliable phone numbers per account. NumberOTP's virtual numbers are private (not shared), so each account gets a distinct number.
Webhook-Driven SMS Workflows
// Express.js webhook endpoint
app.post('/webhook/sms', express.json(), (req, res) => {
const { from, to, body, timestamp } = req.body;
const handler = smsRoutes.get(to);
if (handler) {
handler({ from, body, timestamp });
}
res.status(200).json({ received: true });
});
NumberOTP API Reference
The NumberOTP API follows standard REST conventions. Base URL: https://numberotp.com/api
Authentication
All endpoints require an X-API-Key header:
X-API-Key: your_api_key_here
Core Endpoints
GET /numbers — Request a virtual number
curl -X GET "https://numberotp.com/api/numbers" \
-H "X-API-Key: YOUR_KEY" \
-G \
--data-urlencode "country=us" \
--data-urlencode "service=google"
Response:
{
"id": "num_abc123",
"number": "+12025551234",
"country": "us",
"service": "google",
"expires_at": "2026-05-19T15:30:00Z",
"credits_used": 0.10
}
GET /sms/{number_id} — Poll for incoming SMS
curl "https://numberotp.com/api/sms/num_abc123" \
-H "X-API-Key: YOUR_KEY"
Response:
{
"number_id": "num_abc123",
"messages": [
{
"id": "sms_xyz789",
"from": "+12125550000",
"body": "Your Google verification code is 847291",
"received_at": "2026-05-19T15:12:33Z"
}
]
}
DELETE /numbers/{number_id} — Cancel / trigger refund
curl -X DELETE "https://numberotp.com/api/numbers/num_abc123" \
-H "X-API-Key: YOUR_KEY"
Returns 200 with refund confirmation if no SMS was received.
Country Coverage Reference
| Country | Code | Notes |
|---|---|---|
| United States | us |
Widest service support |
| United Kingdom | gb |
Good EU service support |
| India | in |
Required for IN-only apps |
| Germany | de |
Strong EU banking support |
| Canada | ca |
US-adjacent coverage |
| Brazil | br |
Large inventory |
NumberOTP covers 150+ countries →
Comparing Virtual Number APIs
| Provider | API Docs | Non-VoIP | Auto-Refund | Free Credits | Per-Use Pricing |
|---|---|---|---|---|---|
| NumberOTP | Full REST | Yes | Yes | $0.10 | Yes |
| Twilio | Full REST | Yes | No | $15 trial | Monthly rental |
| 5sim | Basic | Some | No | No | Yes |
| TextVerified | Yes | Yes | No | Limited | Yes |
For verification use cases (one-time numbers, OTP testing): NumberOTP is the cleaner option — no monthly number rental, pay per use, auto-refund on failure.
For production inbound SMS infrastructure (permanent numbers, high volume): Twilio or Plivo are better suited.
TypeScript SDK Example
class SMSVerificationService {
private baseUrl = 'https://numberotp.com/api';
constructor(private apiKey: string) {}
async getNumber(country = 'us', service = 'any'): Promise<{ id: string; number: string }> {
const res = await fetch(
`${this.baseUrl}/numbers?country=${country}&service=${service}`,
{ headers: { 'X-API-Key': this.apiKey } }
);
if (!res.ok) throw new Error(`API error: ${res.status}`);
return res.json();
}
async waitForOTP(numberId: string, timeoutMs = 120000): Promise<string | null> {
const deadline = Date.now() + timeoutMs;
let interval = 3000;
while (Date.now() < deadline) {
await new Promise(r => setTimeout(r, interval));
const res = await fetch(`${this.baseUrl}/sms/${numberId}`, {
headers: { 'X-API-Key': this.apiKey }
});
const data = await res.json();
if (data.messages?.length > 0) {
const otp = data.messages[0].body.match(/\b(\d{4,8})\b/)?.[1];
return otp ?? null;
}
interval = Math.min(interval * 1.5, 15000);
}
// Trigger refund on timeout
await fetch(`${this.baseUrl}/numbers/${numberId}`, {
method: 'DELETE',
headers: { 'X-API-Key': this.apiKey }
});
return null;
}
}
// Usage
const sms = new SMSVerificationService(process.env.NUMBEROTP_API_KEY!);
const { id, number } = await sms.getNumber('us', 'google');
console.log(`Use: ${number}`);
const otp = await sms.waitForOTP(id);
console.log(`OTP: ${otp}`);
Frequently Asked Questions
What is a virtual phone number API?
A virtual phone number API lets you programmatically request, manage, and receive SMS to temporary or permanent phone numbers without physical SIM cards. Services like NumberOTP expose REST endpoints that return real carrier numbers you can use for any SMS verification or inbound SMS workflow.
How do I get a non-VoIP number for Google verification via API?
Use a service that specifically provides SIM-backed non-VoIP numbers. NumberOTP guarantees this — VoIP numbers from standard telephony providers will fail Google's verification check.
What is the difference between a virtual number API and Twilio?
Twilio is a full-scale communications platform with long-term number rentals, voice, SMS, and many features. Virtual number APIs like NumberOTP are optimized for one-time verification use cases — lower cost per verification, no monthly number fees, and auto-refund if no SMS arrives.
Is there a free virtual phone number API?
Most services offer trial credits. NumberOTP gives $0.10 in free credits on signup. Twilio gives $15 in trial credits. There is no sustainably free unlimited API, but free-tier credits are sufficient for development testing.
Can virtual phone numbers receive calls as well as SMS?
It depends on the provider. Most SMS verification APIs (including NumberOTP) focus on inbound SMS only. For two-way voice and SMS, providers like Twilio and Plivo are more appropriate.
Conclusion
Virtual number APIs have matured significantly. For developers building anything that touches SMS verification — integration tests, automation pipelines, multi-account tools, or just testing your own OTP flow — the NumberOTP API is the fastest path to a working implementation.
Clean REST interface, real SIM-backed numbers, auto-refund on failure, and $0.10 free to try. Get your API key →
Top comments (0)