Reseller API
Rent virtual numbers, receive SMS, and get real-time webhooks — all over a simple REST API. Resell our 100+ country catalog through your own app.
API Features
Instant Rentals
Numbers provisioned in under a second.
100+ Countries
Full catalog across multiple servers.
Signed Webhooks
HMAC-SHA256 signatures on every event.
Real-time Events
sms.received, rental.expired, rental.cancelled.
Quickstart
- Sign in and visit API Keys to generate a key.
- Top up your wallet — every API rental charges your account balance.
- Send requests to
https://rentalnumbers.com/functions/<endpoint>with headerAuthorization: Bearer <your_key>. - Optionally register a webhook to receive real-time SMS events.
// Node.js — full reseller flow
// All endpoints go to /resellerApi with an "endpoint" field in the body.
const API = 'https://rentalnumbers.com/functions/resellerApi';
const KEY = process.env.RENTALNUMBERS_API_KEY;
const headers = { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' };
const call = (endpoint, params = {}) =>
fetch(API, { method: 'POST', headers, body: JSON.stringify({ endpoint, ...params }) })
.then(r => r.json());
// 1. Check balance
const bal = await call('getBalance');
console.log('Balance:', bal.balance);
// 2. Rent a US WhatsApp number
const rental = await call('rentNumber', { country: 'United States', service: 'WhatsApp', server: 1 });
console.log('Number:', rental.phone_number);
// 3. Poll for SMS (or use webhooks — see below)
let sms = null;
for (let i = 0; i < 30 && !sms; i++) {
await new Promise(r => setTimeout(r, 5000));
const res = await call('getSMS', { rental_id: rental.rental_id });
if (res.sms_messages?.length) sms = res.sms_messages[0];
}
console.log('SMS:', sms);Authentication
All endpoints (except apiCreateKey / apiListKeys / apiRevokeKey which use your session) authenticate via a Bearer token in the Authorization header.
Authorization: Bearer rk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keys are stored as SHA-256 hashes — we cannot recover them. Lost a key? Revoke it and create a new one.
Endpoints
Base URL: https://rentalnumbers.com/functions
Server-14 is available for Walmart in Canada. Use server: 14 with checkPrice or rentNumber.
https://rentalnumbers.com/functions/apiCreateKeyGenerate a new API key. Returns the key once — store it securely.
{
"name": "Production server"
}{
"success": true,
"api_key": "rk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"id": "abc123",
"key_prefix": "rk_live_xxxxxxxx"
}https://rentalnumbers.com/functions/apiGetBalanceReturns the reseller wallet balance in USD.
{
"success": true,
"balance": 42.5,
"currency": "USD",
"email": "reseller@example.com"
}https://rentalnumbers.com/functions/resellerApiCheck live price + availability. Set endpoint="checkPrice".
{
"endpoint": "checkPrice",
"country": "United States",
"service": "WhatsApp",
"server": 1
}{
"success": true,
"country": "United States",
"service": "WhatsApp",
"server": 1,
"price": 0.45,
"stock": 124
}https://rentalnumbers.com/functions/resellerApiRent a virtual number. Set endpoint="rentNumber". Charges your wallet at the displayed price.
{
"endpoint": "rentNumber",
"country": "United States",
"service": "WhatsApp",
"server": 1
}{
"success": true,
"rental_id": "rent_abc123",
"phone_number": "+15551234567",
"service": "WhatsApp",
"country": "United States",
"server": 1,
"expires_at": "2026-05-01T14:00:00Z",
"price": 0.45
}https://rentalnumbers.com/functions/resellerApiFetch SMS messages received on a rented number. Set endpoint="getSMS". Poll every 5–10 seconds, or use webhooks.
{
"endpoint": "getSMS",
"rental_id": "rent_abc123"
}{
"success": true,
"rental_id": "rent_abc123",
"phone_number": "+15551234567",
"status": "active",
"sms_messages": [
{
"from": "WhatsApp",
"text": "Your code is 123-456",
"received_at": "2026-05-01T13:31:00Z"
}
]
}https://rentalnumbers.com/functions/resellerApiDISABLED — Cancellation is no longer supported via the API. Rentals that do not receive an SMS are automatically refunded once the supplier-side window expires (handled by the expiry cron).
{
"endpoint": "cancelRental",
"rental_id": "rent_abc123"
}{
"error": "Cancellation is no longer supported via the API. Rentals are automatically refunded if no SMS is received before the supplier-side expiry.",
"rental_id": "rent_abc123"
}https://rentalnumbers.com/functions/resellerApiRegister an HTTPS webhook URL. Set endpoint="setWebhook". Returns a signing secret used to verify HMAC-SHA256 signatures.
{
"endpoint": "setWebhook",
"webhook_url": "https://your-server.com/rentalnumbers-webhook",
"events": [
"sms.received",
"rental.expired"
]
}{
"success": true,
"webhook_url": "https://...",
"events": [
"sms.received"
],
"secret": "whsec_xxxxxxxxxxxx"
}https://rentalnumbers.com/functions/resellerApiList all valid country names. Set endpoint="listCountries".
{
"endpoint": "listCountries"
}{
"success": true,
"count": 100,
"countries": [
"Argentina",
"Australia",
"Brazil",
"Canada",
"..."
]
}https://rentalnumbers.com/functions/resellerApiList the popular service names accepted by the API. Set endpoint="listServices". Names are case-insensitive.
{
"endpoint": "listServices"
}{
"success": true,
"count": 90,
"services": [
"WhatsApp",
"Telegram",
"Google",
"Facebook",
"..."
]
}Country & service names
checkPrice and rentNumber accept human-readable names for country and service — no need to memorize any codes. Just use the full name as shown by listServices.
- Use
listCountriesto fetch all valid country names per server. - Use
listServicesto fetch the popular service catalog. - Names are case-insensitive (e.g.
"whatsapp"="WhatsApp"). - Use full country names:
"United States"(not "USA"),"United Kingdom"(not "UK"). - If a service isn't available on a given server/country,
checkPricereturnsavailable: 0— try another server.
⚠️ Full service names — except Server-12
On servers 1–6, 9–11, 14, and 15, always pass the full human-readable service name (e.g. "Walmart", "WhatsApp") — short codes are not accepted. Server-12 is the exception: it uses 1–3 letter short codes shown in the table below. When in doubt, call listServices first to get the exact accepted strings for your server.
Tip: listServices with { server: 12 } returns the full live Server-12 short-code catalog, so you always get the current set — including any services added after this doc was written.
Server-12 — short-code service IDs
Server-12 maps services differently from every other server. Pass the short code shown below as the service parameter (case-insensitive). The display name is for reference only.
| Display name | Service ID (pass this) |
|---|---|
wa | |
| Telegram | tg |
| KakaoTalk | kt |
fb | |
ig | |
| TikTok | tt |
| Twitter / X | tw |
| Discord | ds |
| Google / Gmail | go |
| Microsoft | mm |
| Apple | op |
| Amazon | am |
| Netflix | nf |
| Spotify | sf |
| Steam | mt |
tn | |
| Tinder | ti |
| Viber | vi |
wb | |
| Line | me |
| Badoo | bd |
| Bumble | mo |
| Walmart | wr |
// Server-12 example — KakaoTalk in Brazil
const price = await call('checkPrice', { country: 'Brazil', service: 'kt', server: 12 });
// → { success: true, price: 0.01, available: 193627 }
const rental = await call('rentNumber', { country: 'Brazil', service: 'kt', server: 12 });Recommended flow
const API = 'https://rentalnumbers.com/functions/resellerApi';
const call = (endpoint, params = {}) =>
fetch(API, { method: 'POST', headers, body: JSON.stringify({ endpoint, ...params }) })
.then(r => r.json());
// 1. Fetch valid countries for the server you want
const { countries } = await call('listCountries', { server: 3 });
// 2. Fetch service catalog
const { services } = await call('listServices', { server: 3, country: 'United Kingdom' });
// 3. Confirm price + availability before renting
const price = await call('checkPrice', { country: 'United Kingdom', service: 'WhatsApp', server: 3 });
// → { success: true, price: 0.45, available: 124 }Embed on your site
Drop a live price table onto any page with a single script tag. No API key required — prices are public, read-only data, so the widget never embeds a secret. Supports light/dark themes, configurable services & countries, auto-refresh, and a reseller markup so the prices your customers see include your own profit.
1. Copy this snippet
<script src="https://rentalnumbers.com/functions/widget" data-services="WhatsApp,Telegram,Google" data-countries="United States,United Kingdom,Brazil" data-server="1" data-theme="light" data-title="Live SMS Number Prices" data-refresh="60" data-markup="20" async> </script>
Attributes you can change:
data-services— comma-separated service names (seelistServices).data-countries— comma-separated full country names (e.g."United States").data-server— optional server number; omit to let the widget default to Server-1.data-theme—lightordark.data-refresh— auto-refresh seconds (0 disables; default 60).data-markup— your profit: percentage added on top of each price before display (e.g.20shows prices 20% higher). The widget is display-only; you charge the marked-up price through your own checkout.data-target— id of an existing element to render into instead of auto-inserting.
2. Iframe fallback (optional)
If your site's Content-Security-Policy blocks external scripts or cross-origin fetches, use an iframe instead — it renders the same widget hosted on our domain.
<iframe src="https://rentalnumbers.com/functions/widgetFrame?services=WhatsApp,Telegram&countries=United%20States,United%20Kingdom&theme=light&markup=20" title="Live SMS Number Prices" style="width:100%;max-width:760px;height:460px;border:0;border-radius:12px" loading="lazy" referrerpolicy="no-referrer"> </iframe>
🔒 Security notes
The widget calls the public apiCheckPrice endpoint, which returns price data only — it cannot rent numbers, debit wallets, or access your account. Do not add your reseller API key to the snippet; it is not needed and would be visible to anyone viewing your page source. If you want usage attribution, you may pass data-key, but treat that key as public once embedded.
Live preview
Live SMS Number Prices
| Service | United States | United Kingdom | Brazil |
|---|---|---|---|
$3.10 999+ available | $1.32 100 available | $3.00 100 available | |
| Telegram | $1.92 999+ available | $1.08 100 available | $1.20 100 available |
$1.44 477 available | $0.24 100 available | $0.24 100 available |
Webhooks
Register your HTTPS endpoint via apiSetWebhook. We'll POST signed JSON payloads when these events fire:
sms.received— a new SMS landed on a rented numberrental.expired— a rental's time window endedrental.cancelled— a rental was cancelled (refunded if eligible)
Headers we send
X-RentalNumbers-Event: sms.received X-RentalNumbers-Signature: sha256=<hex> Content-Type: application/json
Verifying signatures
Compute HMAC-SHA256 of the raw request body using your signing secret, then compare against the signature header.
// Node.js — verify incoming webhook
const crypto = require('crypto');
const SECRET = process.env.RENTALNUMBERS_WEBHOOK_SECRET; // whsec_...
app.post('/rentalnumbers-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.header('X-RentalNumbers-Signature') || '';
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET)
.update(req.body).digest('hex');
if (sig !== expected) return res.status(401).send('bad signature');
const event = JSON.parse(req.body.toString());
// event.event = "sms.received" | "rental.expired" | "rental.cancelled"
// event.data = { rental_id, phone_number, sms?, ... }
console.log('Webhook:', event.event, event.data);
res.sendStatus(200);
});SMS delivery latency
How fast you receive an SMS depends on two things: how the underlying network delivers it to us, and how you fetch it from us.
How we receive SMS
| Server | Latency |
|---|---|
| Server-1 | ~1–3 s |
| Server-2 | ~1–3 s |
| Server-3 | ~1–3 s |
| Server-4 | ~1–3 s |
| Server-5 | ~1–3 s |
| Server-6 | ~1–3 s |
You can call getSMS as often as you want (up to your key rate limit), or register a webhook for push delivery.
Webhooks vs polling — pick one
Webhooks (recommended)
- Fastest: ~1–2 s from delivery to your endpoint
- Zero polling load on your server
- Won't burn your rate limit
- Signed with HMAC-SHA256
Polling getSMS
- Poll every 10–15 s for a good balance
- Do not poll faster than 5 s — wasteful, hits rate limit
- Default rate limit: 60 req/min per key
- Use exponential backoff once you receive the SMS
Bottom line: if you're building production, register a webhook via setWebhook for the lowest latency. Polling is fine for quick scripts and prototypes.
Error responses
| Status | Meaning |
|---|---|
| 400 | Bad request — missing/invalid parameters |
| 401 | Missing, invalid, or revoked API key |
| 403 | Resource doesn't belong to your account |
| 404 | Resource not found |
| 429 | Rate limit exceeded (default: 60 req/min) |
| 500 | Server error — retry with backoff |
All error responses follow the shape { "error": "message" }.
"Did you mean?" suggestions
When you pass a service or country value that doesn't match any known name, the API attempts a fuzzy match and returns a suggestion so you can self-correct:
// You sent: { endpoint: "checkPrice", service: "wr", server: 1, country: "United States" }
// Response (400):
{
"error": "Service 'wr' not found on Server-1.",
"suggestion": "Did you mean 'Walmart'? Use listServices to fetch all accepted names."
}Ready to integrate?
Generate your first key and start reselling our numbers in minutes.