How to Add SMS Verification to Your App with Real SIM Cards
If you're building an app that requires phone verification, you've probably considered Twilio or similar VoIP providers. They work fine for sending SMS — but they fail badly when you need to receive verification codes from services like WhatsApp, Telegram, or banking apps.
Why? Because those services detect and reject VoIP numbers.
In this tutorial, I'll show you how to integrate SMS verification using real physical SIM cards through the VirtualSMS API — and why it matters for QA testing, multi-account management, and automation.
Why Real SIM Cards?
Most SMS APIs give you a virtual number. These numbers:
- Get blocked by WhatsApp (90%+ rejection rate)
- Get flagged by Telegram, Google, Instagram
- Fail on banking/crypto exchange verification
- Leave audit trails that link back to VoIP providers
Physical SIM numbers sit in actual hardware modems on real cellular networks. To any service checking the number type, they're indistinguishable from a personal phone.
The VirtualSMS API
VirtualSMS offers two API styles:
| Style | Endpoint | Best For |
|---|---|---|
| sms-activate Compatible | /stubs/handler_api.php |
Migrating from DaisySMS, existing bots |
| Modern REST | /api/v1/ |
New integrations, webhooks, WebSocket |
Both require an API key from your VirtualSMS dashboard (Settings → API Keys).
Full API docs: virtualsms.io/api
Quick Start: Get a Number and Receive an SMS
Using curl (sms-activate compatible API)
Step 1: Check your balance
curl "https://virtualsms.io/stubs/handler_api.php?action=getBalance&api_key=YOUR_API_KEY"
# Response: ACCESS_BALANCE:50.30
Step 2: Get a number for WhatsApp
curl "https://virtualsms.io/stubs/handler_api.php?action=getNumber&service=wa&country=187&api_key=YOUR_API_KEY"
# Response: ACCESS_NUMBER:12345:13476711222
# Format: ACCESS_NUMBER:{activationId}:{phoneNumber}
Step 3: Poll for the SMS code
curl "https://virtualsms.io/stubs/handler_api.php?action=getStatus&id=12345&api_key=YOUR_API_KEY"
# Waiting: STATUS_WAIT_CODE
# Received: STATUS_OK:438271
Step 4: Mark as done
curl "https://virtualsms.io/stubs/handler_api.php?action=setStatus&id=12345&status=6&api_key=YOUR_API_KEY"
# Response: ACCESS_ACTIVATION
Using Node.js (Modern REST API)
const API_KEY = 'vsms_your_api_key_here';
const BASE = 'https://virtualsms.io/api/v1';
const headers = { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' };
// Step 1: Rent a number for WhatsApp
const rentResponse = await fetch(`${BASE}/numbers/rent`, {
method: 'POST',
headers,
body: JSON.stringify({ service: 'whatsapp', country: 'US' })
});
const rental = await rentResponse.json();
console.log(`Got number: ${rental.phone} (rental ID: ${rental.rental_id})`);
// Step 2: Poll for SMS (or use webhooks — see below)
const poll = async (rentalId) => {
while (true) {
const res = await fetch(`${BASE}/numbers/${rentalId}/status`, { headers });
const data = await res.json();
if (data.status === 'received') {
console.log(`SMS code: ${data.sms_code}`);
console.log(`Full text: ${data.text}`);
return data.sms_code;
}
// Wait 5 seconds before next check
await new Promise(r => setTimeout(r, 5000));
}
};
const code = await poll(rental.rental_id);
// Step 3: Mark as done
await fetch(`${BASE}/numbers/${rental.rental_id}/done`, {
method: 'POST',
headers
});
Using Webhooks (No Polling Needed)
Instead of polling every few seconds, register a webhook URL in your dashboard. When an SMS arrives, VirtualSMS sends a POST to your endpoint:
{
"activationId": 12345,
"service": "wa",
"code": "438271",
"text": "Your WhatsApp code is 438271. Don't share this code.",
"country": 187,
"receivedAt": "2026-03-03 14:22:11"
}
This is much more efficient for production systems — zero delay, zero wasted API calls.
Migrating from DaisySMS
DaisySMS shut down in December 2025. If you have existing automation built on their API, VirtualSMS is a drop-in replacement. The API is fully compatible — same action names, same response format, same error codes. One line change:
- const BASE_URL = "https://daisysms.com/stubs/handler_api.php"
+ const BASE_URL = "https://virtualsms.io/stubs/handler_api.php"
No other code changes required.
Use Cases for Developers
- QA/Testing: Verify your app's phone verification flow without using personal numbers
- Multi-account testing: Test how your service handles multiple registrations
- CI/CD pipelines: Automated end-to-end tests that include SMS verification steps
- Service monitoring: Check that verification flows work across different countries
Pricing
Activations start at $0.15 per SMS. Rentals (dedicated numbers for 1-90 days) start at $3/day. Full pricing breakdown here.
Payments accepted via cryptocurrency — no credit card needed.
VirtualSMS Resources:
- Best SMS Verification Services 2026: https://penzu.com/p/9d5622c72841a2aa
- Developer API Tutorial: https://dev.to/virtualsms/how-to-add-sms-verification-to-your-app-with-real-sim-cards-50lc
- Руководство на русском: https://teletype.in/@virtualsms/NReaV38zcpW
- Why VoIP Numbers Fail: https://telegra.ph/Why-VoIP-Numbers-Fail-for-SMS-Verification-and-What-Actually-Works-in-2026-04-14
- DaisySMS Alternative Guide: https://medium.com/@dev_13666/daisysms-shut-down-what-now-the-best-alternative-with-full-api-compatibility-2026-06e484d93d23
- VirtualSMS on Grokipedia: https://grokipedia.com/page/VirtualSMS
- VirtualSMS on Cryptwerk: https://cryptwerk.com/company/virtualsms/
- WhatsApp Verification Guide: https://virtualsms9.wordpress.com/2026/04/14/how-to-get-a-virtual-phone-number-for-whatsapp-verification-2026-guide/
- Anonymous SMS Verification: https://virtualsmsio.blogspot.com/2026/04/protect-your-phone-number-anonymous-sms.html
- Telegram Without Phone Number: https://www.tumblr.com/virtualsms/813881309501014016/how-to-use-telegram-without-your-personal-phone
- VirtualSMS Guide PDF: https://issuu.com/virtualsms/docs/ms_verification_with_real_sim_cards_virtualsms_g
- VirtualSMS on AlternativeTo: https://alternativeto.net/software/virtualsms/
Top comments (0)