SMS Verification API Guide for Developers - PapSMS
Modern applications require phone number verification to prevent fake accounts, reduce fraud, and secure user access. But building SMS verification infrastructure from scratch — carrier agreements, international routing, delivery tracking — takes months. An SMS verification API lets developers add phone verification to any app in minutes with a few lines of code. This guide covers everything you need to know about SMS verification APIs, from architecture to implementation, with working code examples.
What Is an SMS Verification API?
An SMS verification API is a RESTful (or gRPC) service that handles the entire phone verification flow programmatically. You send a request with a phone number, the API sends a verification code via SMS, and you verify the code the user enters against the API. No telecom contracts, no gateway setup, no international carrier negotiations.
A well-designed SMS verification API provides three core endpoints:
- Request — Receive a virtual number and trigger SMS reception
- Status — Poll for the incoming code or set up webhooks
- Release/Cancel — Release the number back to the pool or cancel an unused verification
SMS Verification API vs Rolling Your Own
| Capability | SMS API (PapSMS) | Build Yourself |
|---|---|---|
| Setup time | 15 minutes | 3-6 months |
| Upfront cost | $0 | $50,000+ |
| Per-SMS cost | $0.10 - $3.00 | $0.005 - $0.05 (at scale) |
| Countries supported | 85+ out of the box | Negotiate each carrier |
| Delivery rate | 95%+ managed | Varies, needs tuning |
| Maintenance | Zero | Dedicated team |
| Compliance (GDPR, TCPA) | Handled | Your responsibility |
| Best for | <100k verifications/month | Millions/month at scale |
For most applications, using an SMS verification API is the correct choice until you reach massive scale. Only companies sending millions of SMS per month see cost benefits from building internal infrastructure.
PapSMS API Overview
The PapSMS API provides programmatic access to virtual phone numbers for verification purposes. It is designed for developers building:
- Testing frameworks — Automated testing of signup flows that require phone verification
- QA pipelines — Verifying third-party integrations that send SMS
- Multi-account managers — Applications managing multiple verified accounts across platforms
- Bulk verification tools — Services that need to verify many accounts programmatically
Authentication
All API requests use an API key passed in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Get your API key from your account dashboard after signing up. Keys should be stored in environment variables, never committed to source control.
Base URL
https://papsms.com/api/v1/
Core API Endpoints
1. Purchase a Virtual Number
Request a virtual number for a specific service and country:
POST /api/v1/numbers/purchase
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
{
"country": "us",
"service": "whatsapp"
}
Response:
{
"success": true,
"data": {
"activation_id": "12345678",
"phone_number": "+14155550123",
"country": "us",
"service": "whatsapp",
"expires_at": "2026-04-19T15:30:00Z",
"cost": 0.80
}
}
2. Check SMS Status
Poll for the incoming SMS code:
GET /api/v1/numbers/status/12345678
Authorization: Bearer YOUR_API_KEY
Response while waiting:
{
"success": true,
"data": {
"status": "waiting",
"sms": null
}
}
Response after SMS received:
{
"success": true,
"data": {
"status": "received",
"sms": {
"code": "847291",
"full_text": "Your WhatsApp code: 847291",
"received_at": "2026-04-19T14:35:22Z"
}
}
}
3. Cancel or Complete Activation
POST /api/v1/numbers/finish/12345678
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
{
"status": "completed"
}
Use status: "completed" if the code was received and used successfully, or status: "cancelled" to cancel an unused activation and get a partial refund.
Code Examples
Python
import requests
import time
import os
API_KEY = os.environ["PAPSMS_API_KEY"]
BASE_URL = "https://papsms.com/api/v1"
def verify_phone(service: str, country: str = "us") -> str:
"""Request a virtual number and wait for SMS code."""
headers = {"Authorization": f"Bearer {API_KEY}"}
# 1. Purchase a number
resp = requests.post(
f"{BASE_URL}/numbers/purchase",
json={"country": country, "service": service},
headers=headers,
)
resp.raise_for_status()
activation = resp.json()["data"]
print(f"Got number: {activation['phone_number']}")
# 2. Use the number in your signup flow
# ... your code to submit the number to the target service ...
# 3. Poll for the SMS code
activation_id = activation["activation_id"]
for _ in range(60): # Poll for 60 seconds
resp = requests.get(
f"{BASE_URL}/numbers/status/{activation_id}",
headers=headers,
)
data = resp.json()["data"]
if data["status"] == "received":
code = data["sms"]["code"]
# 4. Mark as completed
requests.post(
f"{BASE_URL}/numbers/finish/{activation_id}",
json={"status": "completed"},
headers=headers,
)
return code
time.sleep(2)
raise TimeoutError("SMS code did not arrive")
# Usage
code = verify_phone(service="whatsapp", country="us")
print(f"Verification code: {code}")
Node.js
const axios = require("axios");
const API_KEY = process.env.PAPSMS_API_KEY;
const BASE_URL = "https://papsms.com/api/v1";
const client = axios.create({
baseURL: BASE_URL,
headers: { Authorization: `Bearer ${API_KEY}` },
});
async function verifyPhone(service, country = "us") {
// 1. Purchase number
const { data: purchase } = await client.post("/numbers/purchase", {
country,
service,
});
const { activation_id, phone_number } = purchase.data;
console.log(`Got number: ${phone_number}`);
// 2. Use number in signup flow...
// 3. Poll for SMS
for (let i = 0; i < 60; i++) {
const { data: status } = await client.get(
`/numbers/status/${activation_id}`
);
if (status.data.status === "received") {
await client.post(`/numbers/finish/${activation_id}`, {
status: "completed",
});
return status.data.sms.code;
}
await new Promise((r) => setTimeout(r, 2000));
}
throw new Error("SMS timeout");
}
// Usage
verifyPhone("whatsapp", "us").then(console.log);
PHP
<?php
$apiKey = getenv("PAPSMS_API_KEY");
$baseUrl = "https://papsms.com/api/v1";
function apiRequest($method, $endpoint, $data = null) {
global $apiKey, $baseUrl;
$ch = curl_init($baseUrl . $endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $apiKey",
"Content-Type: application/json",
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Purchase
$purchase = apiRequest("POST", "/numbers/purchase", [
"country" => "us",
"service" => "whatsapp",
]);
$activationId = $purchase["data"]["activation_id"];
echo "Number: " . $purchase["data"]["phone_number"] . PHP_EOL;
// Poll
for ($i = 0; $i < 60; $i++) {
$status = apiRequest("GET", "/numbers/status/$activationId");
if ($status["data"]["status"] === "received") {
echo "Code: " . $status["data"]["sms"]["code"] . PHP_EOL;
apiRequest("POST", "/numbers/finish/$activationId", [
"status" => "completed",
]);
break;
}
sleep(2);
}
Best Practices
Production Integration Tips
- Use webhooks when possible — Polling wastes API calls. Configure webhook URLs to receive SMS events in real-time
- Handle timeouts gracefully — Always set a maximum wait time (60-120 seconds) and release unused numbers
- Store activation IDs — Keep track of active verifications in your database for debugging and recovery
- Implement retry logic — If a number fails, try a different country or service-specific number
- Cache country availability — Check /api/v1/countries once per hour rather than per request
- Use environment variables — Never hardcode API keys. Use .env files and process environment
- Monitor your usage — Track API calls and spending via the dashboard to catch anomalies
Security Considerations
- API key rotation — Rotate keys every 90 days and immediately if suspected compromised
- IP whitelisting — Restrict API key usage to your server IPs when possible
- Rate limiting on your side — Prevent abuse by rate-limiting verification requests per user
- Log for audit — Log all verification attempts for compliance and debugging
- Handle errors safely — Do not expose API errors directly to end users — log internally and show generic messages
Common Use Cases
Automated QA Testing
QA teams testing signup flows that require phone verification can use the API to programmatically acquire verified accounts. This enables full end-to-end testing including the phone verification step, which would otherwise require manual intervention.
Test Account Provisioning
Development and staging environments need verified accounts that match production conditions. An SMS API provides fresh verified accounts on demand without needing real phone numbers for every developer.
Multi-Tenant Applications
SaaS applications offering "verified account" features to their customers can integrate PapSMS to provide verified accounts as a service. See our SMS verification service guide for business model examples.
Market Research Tools
Tools that analyze app signup flows, verification requirements, or regional availability differences use SMS APIs to programmatically interact with target services while maintaining clean account separation.
Pricing & Rate Limits
PapSMS API pricing is pay-as-you-go with no monthly commitment:
- Per-verification — $0.10 to $3.00 depending on country and service
- No API subscription fee — You only pay for successful verifications
- Partial refunds — Cancelled verifications (no SMS received) are automatically refunded
- Rate limits — 60 requests per minute default, higher limits available on request
- Volume discounts — Custom pricing for >10,000 verifications/month
Start Building with PapSMS API
Free API key, 85+ countries, 5600+ services supported. Pay only for successful verifications.
Get Free API AccessFrequently Asked Questions
PapSMS does not offer free SMS (real verifications cost real money), but new accounts can test the API with a small deposit ($5) which is enough for 50+ verifications in cheaper countries like India or Indonesia. The documentation endpoints are freely accessible.
The PapSMS API targets 99.9% uptime with geographically distributed infrastructure. SMS delivery rates vary by country (85-95%+) and target service. The status page shows real-time availability and live statistics display current delivery rates per service.
Yes. Instead of polling /status endpoint, configure a webhook URL in your account settings. The API will POST to your webhook when an SMS arrives, including the activation ID, code, and full message text. This reduces API calls and provides instant notifications.
Yes. PapSMS supports WhatsApp, WhatsApp Business, and the WhatsApp Business API platform verifications. See our WhatsApp verification guide for details on country-specific success rates.
The default rate limit is 60 requests/minute. Implement exponential backoff when you receive a 429 response. For high-volume applications (>1000 verifications/hour), contact support to increase your rate limit. Batch operations and webhooks reduce the need for high rate limits.