PVADeals API
Build powerful SMS verification solutions with our non-VoIP phone number API. Get access to real carrier phone numbers for account verification across hundreds of services.
v3.3 · Last updated: May 31, 2026 (UTC+00:00)
Introduction
The PVADeals API provides programmatic access to our SMS verification service. Use it to purchase phone numbers, receive SMS codes, and manage your verification workflow.
Non-VoIP Numbers
Real carrier phone numbers that work with services that block VoIP.
Instant Delivery
SMS codes typically arrive within seconds of being sent.
Authentication
All API requests require authentication using your API key. Include it in the Authorization header with the Bearer prefix.
Authorization: Bearer API-xxxxx...
🔑 Getting Your API Key
Example Request with Authentication
curl -X GET "https://prod-v3.pvadeals.com/v3/api/balance" \
-H "Authorization: Bearer API-xxxxx..."
Quick Start
Follow this typical integration flow to purchase a number and receive an SMS verification code.
Step 1: Get Available Services
curl -X GET "https://prod-v3.pvadeals.com/v3/api/services/all" \
-H "Authorization: Bearer API-xxxxx..."
Step 2: Purchase a Number
curl -X POST "https://prod-v3.pvadeals.com/v3/api/purchase" \
-H "Authorization: Bearer API-xxxxx..." \
-H "Content-Type: application/json" \
-d '{"services": [{"serviceId": "697139f7fe5460ddc2f27214"}]}'
Step 3: Receive SMS via Webhook
Configure your webhook URL in the dashboard. When an SMS arrives, you'll receive a POST request with the verification code.
Rate Limits
The API enforces two layers of rate limiting: a per-IP token bucket that protects the platform from any one source, and a per-API-key bucket that applies a separate cap on each endpoint.
Per-IP limits (apply to every caller from the same IP)
| Scope | Limit | Window | Notes |
|---|---|---|---|
All /v3/api/* | 300 requests | 60s rolling | Token bucket. Refills continuously. |
Purchase paths (/v3/api/purchase, /v3/api/purchase-ltr) | 60 requests | 60s rolling | Extra bucket on top of the all-/v3/api/* bucket. |
Per-API-key limits (apply per endpoint)
| Endpoint | Limit | Window |
|---|---|---|
GET /v3/api/services/all | 10 | 60s |
GET /v3/api/service/:id | 60 | 60s |
GET /v3/api/balance | 60 | 60s |
GET /v3/api/requests | 60 | 60s |
GET /v3/api/request/:id | 120 | 60s |
POST /v3/api/purchase | 50 | 60s |
POST /v3/api/purchase-ltr | 30 | 60s |
POST /v3/api/flag/:id, POST /v3/api/reuse/:id, POST /v3/api/renew-ltr/:id | 30 | 60s |
| Any other endpoint (default) | 120 | 60s |
Purchase safety brake
If 15 purchase calls fail within 60 seconds for the same API key, the purchase endpoints pause for that key for 5 minutes. Successful calls don't count toward this. Returns 429 with a Retry-After header.
Rate Limit Response
{
"message": "API rate limit exceeded. Maximum 60 requests per 60 seconds for GET /v3/api/balance.",
"status": 429,
"retryAfter": 60
}
Public probes share the per-IP bucket
/v3/api/health and /v3/api/version count against the 300/min per-IP limit. A 30s polling interval (12 req/min) leaves 96% of the bucket free for your real API calls.
Error Handling
The API uses standard HTTP status codes to indicate success or failure of requests.
| Status Code | Meaning | Common Causes |
|---|---|---|
| 200 OK | Success | Request completed successfully |
| 400 Bad Request | Invalid Request | Missing required fields, invalid JSON |
| 401 Unauthorized | Authentication Failed | Missing or invalid API key |
| 403 Forbidden | Account Blocked / Action Disallowed | Account suspended, purchase block active, or insufficient privileges |
| 404 Not Found | Resource Not Found | Request ID or Service ID doesn't exist |
| 422 Unprocessable | Business Rule Violation | Flag/Reuse not allowed for this request |
| 429 Too Many Requests | Rate Limit Exceeded | Too many requests, wait and retry |
| 503 Service Unavailable | Maintenance or Backend Unavailable | Maintenance window is active, or a critical dependency is down. Also the response for /v3/api/health when status is maintenance or unavailable. |
Error Response Format
{"success": false, "message": "Unauthorized Access! Invalid API key", "status": 401}
Health Check
Public, unauthenticated liveness probe for customer-side monitoring tools (Pingdom, UptimeRobot, BetterStack, Datadog HTTP checks, custom dashboards). No API key required.
Response is NOT wrapped in the standard envelope
Unlike every other endpoint, the body below is the entire response — no {success, data, message} wrapper. Customers who write a generic envelope unwrapper need a separate code path for this endpoint.
Example Request
curl -i "https://prod-v3.pvadeals.com/v3/api/health"
Response: operational
{
"status": "operational",
"version": "1.6.0",
"timestamp": "2026-05-31T14:22:01.123Z"
}
Response: maintenance window
{
"status": "maintenance",
"version": "1.6.0",
"timestamp": "2026-05-31T14:22:01.123Z"
}
Response: backend unavailable
{
"status": "unavailable",
"version": "1.6.0",
"timestamp": "2026-05-31T14:22:01.123Z"
}
Status Values
| Status | HTTP Code | Meaning |
|---|---|---|
| operational | 200 OK | All checks passing. Safe to call other endpoints. |
| maintenance | 503 | Operator-set maintenance window. Every authenticated endpoint will also return 503 right now. Wait and retry. |
| unavailable | 503 | Backend cannot service requests right now. Retry with backoff. |
Response Fields
Integration tips
Key your monitoring off the HTTP status code (200 = up, 503 = down), not the body string — that is what every standard monitoring tool checks. We deliberately do not return 200 with a "down" body. Subsystem detail is intentionally not exposed in the body. Recommended polling interval: 30 seconds (the endpoint is cached server-side for 5 seconds, so polling faster does not get fresher data).
Version
Public, unauthenticated. Returns the deployed backend version — useful for detecting when a new release has rolled out without making an authenticated call (CI/CD smoke checks, incident-response timelines).
Response is NOT wrapped in the standard envelope
Same convention as /v3/api/health: the body below is the entire response, no {success, data, message} wrapper.
Example Request
curl "https://prod-v3.pvadeals.com/v3/api/version"
Response
{
"status": "ok",
"version": "1.6.0"
}
Response Fields
Get Balance
Retrieve your current account balance and credit information.
Example Request
curl -X GET "https://prod-v3.pvadeals.com/v3/api/balance" \
-H "Authorization: Bearer API-xxxxx..."
Response
{"success": true, "data": {"credits": 100.00}, "message": "Balance retrieved successfully"}
Get All Services
Retrieve a list of all available services for SMS verification.
Example Request
curl -X GET "https://prod-v3.pvadeals.com/v3/api/services/all" \
-H "Authorization: Bearer API-xxxxx..."
Response
{
"success": true,
"data": {
"services": [
{
"_id": "697139f7fe5460ddc2f27214",
"name": "Airbnb",
"country": "USA",
"STRprice": 0.09,
"LTR3price": 0.7,
"LTR7price": 1.25,
"LTR14price": 1.75,
"LTR30price": 2.25,
"STRstock": 142,
"LTRstock": 28
}
]
}
}
Response Fields
Get Service
Retrieve a single service by its ID. Useful for resolving the serviceId from a request response back to its display name / pricing without paging through the full catalog.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | string | The service _id (from Get All Services) |
Example Request
curl -X GET "https://prod-v3.pvadeals.com/v3/api/service/697139f7fe5460ddc2f27214" \
-H "Authorization: Bearer API-xxxxx..."
Response
{
"success": true,
"data": {
"_id": "697139f7fe5460ddc2f27214",
"name": "Airbnb",
"country": "USA",
"price": 0.09,
"LTR3price": 0.7,
"LTR7price": 1.25,
"LTR14price": 1.75,
"LTR30price": 2.25,
"STRstock": 142,
"LTRstock": 28
},
"message": "Service retrieved successfully"
}
Response Fields
Caching
The response is cached for 5 minutes per API key, so repeated lookups of the same service are nearly free.
STR - Purchase Number
Purchase one or more phone numbers for SMS verification. Numbers are available for 20 minutes.
Request Body
Example Request
curl -X POST "https://prod-v3.pvadeals.com/v3/api/purchase" \
-H "Authorization: Bearer API-xxxxx..." \
-H "Content-Type: application/json" \
-d '{"services": [{"serviceId": "697139f7fe5460ddc2f27214", "areaCode": "215"}]}'
Response
{
"success": true,
"data": {
"requests": [{
"_id": "697a90d25ef1873ef44f48bc",
"serviceName": "Airbnb",
"serviceId": "697139f7fe5460ddc2f27214",
"number": "+13130001234",
"status": "RESERVED",
"country": "US",
"amount": 0.2,
"numberType": "STR",
"allowFlag": true,
"allowReuse": false,
"reuseCounter": 0,
"endTime": "2026-01-28T23:02:26.292Z",
"createdAt": "2026-01-28T22:42:26.452Z"
}]
},
"message": "Numbers purchased successfully"
}
Webhook Notification
A number_purchased webhook event will be sent to your configured webhook URL after a successful purchase.
STR - Flag Number
Flag a number to cancel it and receive a refund (if eligible). Only available when allowFlag: true.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | string | The service request ID to flag |
Example Request
curl -X POST "https://prod-v3.pvadeals.com/v3/api/flag/697a90d25ef1873ef44f48bc" \
-H "Authorization: Bearer API-xxxxx..."
Response
{"success": true, "data": true, "message": "Number flagged successfully"}
Flag Availability
Check the allowFlag field before attempting to flag. If allowFlag: false, the request will return a 422 error.
STR - Reuse Number
Reuse a previously purchased number to receive additional SMS codes. Only available when allowReuse: true.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | string | The service request ID to reuse |
Example Request
curl -X POST "https://prod-v3.pvadeals.com/v3/api/reuse/697a90d25ef1873ef44f48bc" \
-H "Authorization: Bearer API-xxxxx..."
Response
{
"success": true,
"data": {
"_id": "697a90d25ef1873ef44f48bc",
"serviceName": "Airbnb",
"serviceId": "697139f7fe5460ddc2f27214",
"number": "+13130001234",
"status": "RESERVED",
"country": "US",
"amount": 0,
"numberType": "STR",
"allowFlag": false,
"allowReuse": true,
"reuseCounter": 2,
"endTime": "2026-01-28T23:07:17.000Z"
},
"message": "Number reused successfully"
}
Reuse
Reuse is time-limited and not guaranteed for all numbers. In some cases, additional charges may apply.
LTR - Purchase Number
Purchase a long-term rental phone number for extended verification needs.
Request Body
Example Request
curl -X POST "https://prod-v3.pvadeals.com/v3/api/purchase-ltr" \
-H "Authorization: Bearer API-xxxxx..." \
-H "Content-Type: application/json" \
-d '{"duration": 30, "serviceId": "697139f4fe5460ddc2f271db", "areaCode": "619"}'
Response
{
"success": true,
"data": {
"_id": "6985cc7b9ecd8f1260b47a39",
"serviceName": "2RedBeans",
"serviceId": "697139f4fe5460ddc2f271db",
"number": "+13130001234",
"status": "RESERVED",
"country": "US",
"amount": 0.7,
"endTime": "2026-02-09T11:11:55.324Z",
"allowFlag": true,
"autoRenewEnable": false,
"numberType": "LTR",
"createdAt": "2026-02-06T11:11:55.354Z",
"updatedAt": "2026-02-06T11:11:55.384Z"
},
"message": "LTR number purchased successfully"
}
Duration Options
| Duration | Price | Description |
|---|---|---|
| 3 | $0.70 | 3-day rental |
| 7 | $1.25 | 7-day rental |
| 14 | $1.75 | 14-day rental |
| 30 | $2.25 | 30-day rental |
LTR - Flag Number
Flag a LTR number to cancel it and receive a refund (if eligible). Only available when allowFlag: true.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | string | The LTR request ID to flag |
Example Request
curl -X POST "https://prod-v3.pvadeals.com/v3/api/flag/6985cc7b9ecd8f1260b47a39" \
-H "Authorization: Bearer API-xxxxx..."
Response
{"success": true, "data": true, "message": "Number flagged successfully"}
Flag Availability
Check the allowFlag field before attempting to flag. If allowFlag: false, the request will return a 422 error.
LTR - Renew Number
Toggle auto-renew for a LTR number. The same endpoint enables or disables auto-renewal.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | string | The LTR request ID to toggle auto-renew |
Example Request
curl -X POST "https://prod-v3.pvadeals.com/v3/api/renew-ltr/6985ce2e9ecd8f1260b47a7e" \
-H "Authorization: Bearer API-xxxxx..."
Response
{
"success": true,
"data": {
"_id": "6985ce2e9ecd8f1260b47a7e",
"serviceName": "2RedBeans",
"serviceId": "697139f4fe5460ddc2f271db",
"number": "+13130001234",
"status": "RESERVED",
"country": "US",
"amount": 0.7,
"endTime": "2026-02-09T11:19:10.769Z",
"allowFlag": true,
"autoRenewEnable": true,
"numberType": "LTR",
"createdAt": "2026-02-06T11:19:10.795Z",
"updatedAt": "2026-02-06T11:19:16.699Z"
},
"message": "Auto-renew enabled successfully"
}
Auto-Renew Toggle
Call this endpoint again to disable auto-renew. Response message will change to "Auto-renew disabled successfully".
LTR - Reuse Number
Reuse a previously purchased LTR number to receive additional SMS codes. Only available when allowReuse: true.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | string | The LTR request ID to reuse |
Example Request
curl -X POST "https://prod-v3.pvadeals.com/v3/api/reuse/6985cc7b9ecd8f1260b47a39" \
-H "Authorization: Bearer API-xxxxx..."
Response
{
"success": true,
"data": {
"_id": "6985cc7b9ecd8f1260b47a39",
"serviceName": "2RedBeans",
"serviceId": "697139f4fe5460ddc2f271db",
"number": "+13130001234",
"status": "RESERVED",
"country": "US",
"amount": 0,
"numberType": "LTR",
"allowFlag": false,
"allowReuse": true,
"reuseCounter": 2,
"endTime": "2026-02-09T11:11:55.324Z"
},
"message": "Number reused successfully"
}
Reuse Availability
Check the allowReuse field before attempting to reuse. If allowReuse: false, the request will return a 422 error.
LTR - Purchase All Service Number
Purchase a 28-day LTR number that can receive SMS from any service - not restricted to a single website.
Request Body
Example Request
curl -X POST "https://prod-v3.pvadeals.com/v3/api/purchase-ltr" \
-H "Authorization: Bearer API-xxxxx..." \
-H "Content-Type: application/json" \
-d '{"duration": 28, "serviceId": "ALL_SERVICES"}'
Response
{
"success": true,
"data": {
"_id": "6985d496e49f9312f4cc934e",
"serviceName": "ALL_SERVICES",
"serviceId": "ALL_SERVICES",
"number": "+13130001234",
"status": "RESERVED",
"country": "US",
"amount": 12.99,
"endTime": "2026-03-06T11:11:55.324Z",
"allowFlag": false,
"autoRenewEnable": false,
"numberType": "LTR",
"createdAt": "2026-02-06T11:11:55.354Z",
"updatedAt": "2026-02-06T11:11:55.384Z"
},
"message": "LTR number purchased successfully"
}
List Service Requests
Get a paginated list of your service requests (purchased numbers) using cursor-based pagination. Returns both STR and LTR requests.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| first | number | 20 | Number of items to fetch |
| after | string | - | Cursor for forward pagination |
| before | string | - | Cursor for backward pagination |
| orderField | string | _id | Field to sort by |
| orderBy | string | desc | Sort order: asc or desc |
Example Request
curl -X GET "https://prod-v3.pvadeals.com/v3/api/requests?first=20&orderBy=desc" \
-H "Authorization: Bearer API-xxxxx..."
Response
{
"success": true,
"data": {
"data": {
"edges": [{
"cursor": "Njk3YTkwZDI1ZWYxODczZWY0NGY0OGJj",
"node": {
"_id": "697a90d25ef1873ef44f48bc",
"serviceName": "Airbnb",
"serviceId": "697139f7fe5460ddc2f27214",
"number": "+13130001234",
"status": "COMPLETED",
"country": "US",
"amount": 0.2,
"numberType": "STR",
"allowFlag": false,
"allowReuse": true,
"reuseCounter": 0,
"endTime": "2026-01-28T23:02:26.292Z",
"createdAt": "2026-01-28T22:42:26.452Z"
}
}]
}
}
}
Get Service Request
Get detailed information about a specific service request by its ID. Works for both STR and LTR requests.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | string | The service request ID |
Example Request
curl -X GET "https://prod-v3.pvadeals.com/v3/api/request/697a90d25ef1873ef44f48bc" \
-H "Authorization: Bearer API-xxxxx..."
Response
{
"success": true,
"data": {
"_id": "697a90d25ef1873ef44f48bc",
"serviceName": "Airbnb",
"serviceId": "697139f7fe5460ddc2f27214",
"number": "+13130001234",
"status": "COMPLETED",
"country": "US",
"amount": 0.2,
"numberType": "STR",
"allowFlag": false,
"allowReuse": true,
"reuseCounter": 0,
"messageCounter": 15,
"endTime": "2026-01-28T23:02:26.292Z",
"createdAt": "2026-01-28T22:42:26.452Z",
"updatedAt": "2026-01-28T22:57:35.016Z"
},
"message": "Service request retrieved successfully"
}
Status Values
| Status | Description |
|---|---|
| RESERVED | Number purchased, waiting for SMS |
| COMPLETED | SMS received successfully |
| FLAGGED | Number was flagged/cancelled |
| TIMEOUT | Number expired without receiving SMS |
Webhooks
Configure a webhook URL in your dashboard to receive real-time notifications about events.
Webhook Retry Policy
Webhooks are retried up to 5 times with a 5-second delay between each attempt if delivery fails.
sms_received
Triggered when an SMS is received on a purchased number.
{"event": "sms_received", "timestamp": "2026-01-28T22:57:35.001Z", "requestId": "697a90d25ef1873ef44f48bc", "serviceId": "697139f7fe5460ddc2f27214", "number": "+13130001234", "message": "Your Airbnb verification code is 2200."}
number_purchased
Triggered when a number is successfully purchased.
{"event": "number_purchased", "timestamp": "2026-01-28T22:42:26.452Z", "requestId": "697a90d25ef1873ef44f48bc", "serviceId": "697139f7fe5460ddc2f27214", "number": "+13130001234", "message": ""}
number_flagged
Triggered when a number is flagged/cancelled.
{"event": "number_flagged", "timestamp": "2026-01-28T22:42:25.673Z", "requestId": "697a8d4f8130528198e0c0eb", "serviceId": "697a8d4f8130528198e0c0f4", "number": "+13130001234", "message": ""}
number_reused
Triggered when a number is reused.
{"event": "number_reused", "timestamp": "2026-01-28T21:50:36.765Z", "requestId": "697a82bf27787d2db20a29a0", "serviceId": "697a82c127787d2db20a29a6", "number": "+13130001234", "message": ""}
number_renewed
Triggered when an LTR number is automatically renewed. Contains the new request ID which must be used for future API calls.
{"event": "number_renewed", "timestamp": "2026-03-09T21:39:19.195Z", "requestId": "69af3e07d317de1936d907fa", "serviceId": "697139f4fe5460ddc2f271b2", "number": "+13130001234", "message": "", "oldRequestId": "69af3a8246b1683d46342bcc", "serviceName": "Chalkboard", "amount": 0.7, "newExpiresAt": "2026-03-12T21:38:42.212Z"}
sms_blocked
Triggered when an inbound SMS arrives on one of your rented numbers but does not match the service the number was rented for. The SMS is not credited to your request and not delivered to you; this event tells you it happened so you can adjust your matching / detection logic.
{"event": "sms_blocked", "timestamp": "2026-05-12T18:04:01.211Z", "requestId": "6a1f3e4c4a8f9d1b2c7f0e23", "serviceId": "697139f7fe5460ddc2f27214", "number": "+13130001234", "blockedService": "Discord", "matchType": "keyword", "matchedValue": "discord", "message": ""}