Rent Virtual Phone Numbers Online – SMS Verification Service

← Back to Home
⚡

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

  1. Sign in and visit API Keys to generate a key.
  2. Top up your wallet — every API rental charges your account balance.
  3. Send requests to https://rentalnumbers.com/functions/<endpoint> with header Authorization: Bearer <your_key>.
  4. 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.

POSThttps://rentalnumbers.com/functions/apiCreateKey

Generate a new API key. Returns the key once — store it securely.

Auth: Session (logged in)
Request body
{
  "name": "Production server"
}
Response
{
  "success": true,
  "api_key": "rk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "id": "abc123",
  "key_prefix": "rk_live_xxxxxxxx"
}
GEThttps://rentalnumbers.com/functions/apiGetBalance

Returns the reseller wallet balance in USD.

Auth: Bearer <api_key>
Response
{
  "success": true,
  "balance": 42.5,
  "currency": "USD",
  "email": "reseller@example.com"
}
POSThttps://rentalnumbers.com/functions/resellerApi

Check live price + availability. Set endpoint="checkPrice".

Auth: Bearer <api_key>
Request body
{
  "endpoint": "checkPrice",
  "country": "United States",
  "service": "WhatsApp",
  "server": 1
}
Response
{
  "success": true,
  "country": "United States",
  "service": "WhatsApp",
  "server": 1,
  "price": 0.45,
  "stock": 124
}
POSThttps://rentalnumbers.com/functions/resellerApi

Rent a virtual number. Set endpoint="rentNumber". Charges your wallet at the displayed price.

Auth: Bearer <api_key>
Request body
{
  "endpoint": "rentNumber",
  "country": "United States",
  "service": "WhatsApp",
  "server": 1
}
Response
{
  "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
}
POSThttps://rentalnumbers.com/functions/resellerApi

Fetch SMS messages received on a rented number. Set endpoint="getSMS". Poll every 5–10 seconds, or use webhooks.

Auth: Bearer <api_key>
Request body
{
  "endpoint": "getSMS",
  "rental_id": "rent_abc123"
}
Response
{
  "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"
    }
  ]
}
POSThttps://rentalnumbers.com/functions/resellerApi

DISABLED — 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).

Auth: Bearer <api_key>
Request body
{
  "endpoint": "cancelRental",
  "rental_id": "rent_abc123"
}
Response
{
  "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"
}
POSThttps://rentalnumbers.com/functions/resellerApi

Register an HTTPS webhook URL. Set endpoint="setWebhook". Returns a signing secret used to verify HMAC-SHA256 signatures.

Auth: Bearer <api_key>
Request body
{
  "endpoint": "setWebhook",
  "webhook_url": "https://your-server.com/rentalnumbers-webhook",
  "events": [
    "sms.received",
    "rental.expired"
  ]
}
Response
{
  "success": true,
  "webhook_url": "https://...",
  "events": [
    "sms.received"
  ],
  "secret": "whsec_xxxxxxxxxxxx"
}
POSThttps://rentalnumbers.com/functions/resellerApi

List all valid country names. Set endpoint="listCountries".

Auth: Bearer <api_key>
Request body
{
  "endpoint": "listCountries"
}
Response
{
  "success": true,
  "count": 100,
  "countries": [
    "Argentina",
    "Australia",
    "Brazil",
    "Canada",
    "..."
  ]
}
POSThttps://rentalnumbers.com/functions/resellerApi

List the popular service names accepted by the API. Set endpoint="listServices". Names are case-insensitive.

Auth: Bearer <api_key>
Request body
{
  "endpoint": "listServices"
}
Response
{
  "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 listCountries to fetch all valid country names per server.
  • Use listServices to 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, checkPrice returns available: 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 nameService ID (pass this)
WhatsAppwa
Telegramtg
KakaoTalkkt
Facebookfb
Instagramig
TikToktt
Twitter / Xtw
Discordds
Google / Gmailgo
Microsoftmm
Appleop
Amazonam
Netflixnf
Spotifysf
Steammt
LinkedIntn
Tinderti
Vibervi
WeChatwb
Lineme
Badoobd
Bumblemo
Walmartwr
// 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 (see listServices).
  • 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 — light or dark.
  • data-refresh — auto-refresh seconds (0 disables; default 60).
  • data-markup — your profit: percentage added on top of each price before display (e.g. 20 shows 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

Live pricing · 3 services · 3 countries · Server-1
ServiceUnited StatesUnited KingdomBrazil
WhatsApp
$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
Google
$1.44
477 available
$0.24
100 available
$0.24
100 available
↻ Refresh

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 number
  • rental.expired — a rental's time window ended
  • rental.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

ServerLatency
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

StatusMeaning
400Bad request — missing/invalid parameters
401Missing, invalid, or revoked API key
403Resource doesn't belong to your account
404Resource not found
429Rate limit exceeded (default: 60 req/min)
500Server 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.

Cookie & Data Privacy

We use cookies and similar technologies to enhance your experience, analyze traffic, and serve relevant ads. By clicking "Accept", you consent to the use of these technologies as described in our Privacy Policy.