REST API / VERSION 1
Build verification
into your product.
Rent numbers, check live pricing and retrieve SMS codes through a compact JSON API.
GETTING STARTED
Authentication
Include your API key in the X-API-KEY header on every authenticated request. Keep keys server-side and never expose them in browser code.
https://rentnumber.net/api/v1curl -H "X-API-KEY: rn_your_key_here" \
https://rentnumber.net/api/v1/balance
Endpoints marked no authentication required can be called without a key.
WALLET
GET /balance
Return the current wallet balance. Authentication is required.
{
"success": true,
"balance": 5000,
"currency": "NGN"
}
balance is a JSON number, so a whole amount has no decimal part. Do not rely on two decimal places being present.
DIRECTORY
GET /services
List all available services. No authentication required.
{
"success": true,
"data": [{
"id": 1,
"slug": "whatsapp",
"name": "WhatsApp",
"icon": "assets/img/services/whatsapp.svg",
"provider_count": 3,
"min_price": "241.96"
}]
}
icon is a path relative to the site root, or null. min_price is the lowest price across providers, returned as a decimal string — see number formats.
DIRECTORY
GET /countries
List supported countries and their identifiers. No authentication required.
{
"success": true,
"data": [
{ "id": 1, "iso_code": "NG", "name": "Nigeria", "flag": "🇳🇬" }
]
}
LIVE AVAILABILITY
GET /services/{slug}/prices
Get live pricing options for a service. Filter by country using the optional ?country_id= query.
GET /api/v1/services/whatsapp/prices?country_id=1| PARAMETER | TYPE | DESCRIPTION |
|---|---|---|
slug | string | Service slug, such as whatsapp |
country_id | integer | Optional country filter |
{
"success": true,
"service": { "id": 1, "slug": "whatsapp", "name": "WhatsApp" },
"data": [{
"id": 42,
"final_price": "450.00",
"stock": 120,
"rental_type": "short",
"provider_id": 3,
"provider_name": "Premium",
"country_id": 1,
"country_name": "Nigeria",
"iso_code": "NG",
"flag": "🇳🇬"
}]
}
Pass data[].id as pricing_id to POST /rent. stock may be null when a provider does not report inventory, and rental_type is short, long or both.
CREATE RENTAL
POST /rent
Rent a number using a pricing option returned by the prices endpoint. Authentication is required.
| PARAMETER | TYPE | DESCRIPTION |
|---|---|---|
pricing_id | integer | ID from data[].id in the prices response |
curl -X POST -H "X-API-KEY: rn_your_key" \
-d "pricing_id=42" \
https://rentnumber.net/api/v1/rent
{
"success": true,
"rental_id": 101,
"phone": "+2348012345678",
"cost": 450,
"status": "waiting",
"expires_at": "2026-04-12T15:30:00Z"
}
RETRIEVE SMS
GET /rental/{id}
Check rental status and retrieve the SMS code. This endpoint polls the provider, so call it while you wait rather than re-renting.
{
"success": true,
"rental_id": 101,
"phone": "+2348012345678",
"status": "received",
"code": "123456",
"sms": "Your verification code is 123456",
"cost": 450,
"expires_at": "2026-04-12T15:30:00Z"
}
code and sms are null until a message arrives.
pendingwaitingreceivedcompletedcancelledexpiredrefundedHISTORY
GET /rentals
List your most recent short-term rentals, newest first. Authentication is required.
{
"success": true,
"data": [{
"rental_id": 101,
"service": "WhatsApp",
"country": "Nigeria",
"provider": "Premium",
"phone": "+2348012345678",
"status": "completed",
"cost": 450,
"created_at": "2026-04-12 15:10:00"
}]
}
END RENTAL
POST /rental/{id}/cancel
Cancel a rental that has not received an SMS. Eligible rentals are refunded to your wallet automatically.
curl -X POST -H "X-API-KEY: rn_your_key" \
https://rentnumber.net/api/v1/rental/101/cancel
{
"success": true,
"status": "refunded",
"refunded": true,
"amount": 450
}
RELIABILITY
Errors & rate limits
HTTP status codes communicate the request result. Error responses include a JSON message suitable for logging.
Every failure returns the same envelope, so one error path handles them all:
{
"success": false,
"error": "Invalid API key."
}
Check success rather than the message text — messages are meant for your logs and may change. Provider failures are sanitised before they reach you, so upstream vendor names never appear in an error.
| FIELD | JSON TYPE | NOTE |
|---|---|---|
balance, cost, amount | number | Trailing zeros are dropped — 450, not 450.00 |
final_price, min_price | string | Decimal string such as "450.00". Cast before doing arithmetic. |
stock | integer or null | null when the provider reports no inventory figure |
ACCOUNTS
Account endpoints
These four need no API key — they are how a client obtains one. All accept either a JSON body or form-encoded fields.
POST /auth/register
| PARAMETER | TYPE | DESCRIPTION |
|---|---|---|
email | string | Required, must be unique |
username | string | Required, must be unique |
password | string | Required |
phone | string | Optional |
agree_terms | boolean | Required — must be true |
{
"success": true,
"api_key": "rn_live_...",
"user": {
"id": 793,
"username": "yourname",
"email": "you@example.com",
"phone": "08011112222",
"wallet_balance": 0
}
}
An API key is issued at registration — store it; this is the only response that returns a freshly generated one.
POST /auth/login
Takes email and password and returns the same envelope as register, including the account's existing api_key.
POST /auth/forgot-password
Takes email. Emails a 6-digit code valid for 15 minutes, rate-limited to one request every two minutes.
{
"success": true,
"message": "A 6-digit reset code has been sent to your email."
}
POST /auth/reset-password
Takes email, code and the new password. Returns { "success": true, "message": "Password updated successfully." }.
WALLET
Transactions & deposits
Both list endpoints page with ?limit= (1–100, default 50) and ?offset=, and report has_more so you know when to stop.
GET /transactions
{
"success": true,
"data": [{
"id": 5001,
"type": "debit",
"amount": "450.00",
"balance_after": "4550.00",
"source": "rental",
"description": "WhatsApp — Nigeria",
"created_at": "2026-04-12 15:10:00"
}],
"limit": 50,
"offset": 0,
"has_more": false
}
GET /deposits
Same envelope plus the current wallet balance. Rows carry id, gateway, order_id, amount_ngn, amount_usd, status, paid_at and created_at.
GET /deposits/methods
Available funding gateways, plus the accepted range and the USD conversion rate. Authenticated like every other endpoint.
{
"success": true,
"data": [
{ "gateway": "cryptomus", "label": "Cryptocurrency",
"description": "BTC, ETH, USDT, LTC and more", "currency": "USD" },
{ "gateway": "paymentpoint", "label": "Virtual bank account",
"description": "Permanent NGN account \u2014 transfer to top up any time", "currency": "NGN" },
{ "gateway": "monnify", "label": "Monnify (card or bank)",
"description": "Alternative NGN card or bank transfer", "currency": "NGN" }
],
"min_ngn": 500,
"max_ngn": 1000000,
"usd_ngn_rate": 1450
}
Starting a deposit
Each gateway has its own endpoint, all authenticated and all taking amount_ngn: POST /deposits/cryptomus and POST /deposits/monnify create a pending deposit and return the gateway's checkout URL and reference. POST /deposits/paymentpoint/account takes no amount — it returns a permanent virtual bank account for the user, reusing the existing one when there is one ("reused": true).
LONG-TERM RENTALS
Keeping a number
A long-term rental holds the same number for days or months and receives unlimited incoming messages, rather than expiring after one code.
GET /long-term/services
Services available for long-term rental. No authentication required.
{
"success": true,
"data": [{
"id": 1, "slug": "whatsapp", "name": "WhatsApp",
"icon": "assets/img/services/whatsapp.svg",
"option_count": 12, "from_price": "12000.00"
}]
}
GET /long-term/services/{slug}/tiers
Durations and prices, grouped by provider and country. Each group's tiers[].id is the provider_service_id you rent with.
{
"success": true,
"service": { "id": 1, "slug": "whatsapp", "name": "WhatsApp" },
"data": [{
"provider_id": 3, "provider_name": "Premium",
"country_id": 1, "country_name": "Nigeria",
"iso_code": "NG", "flag": "🇳🇬",
"tiers": [
{ "id": 9001, "duration_days": 30, "final_price": 12000 }
]
}]
}
POST /long-term/rent
Takes provider_service_id (a tier id from above). Authentication required.
GET /long-term/rentals
Your long-term rentals: rental_id, service, country, provider, phone, status, cost, created_at and expires_at.
GET /long-term/rentals/{id}/poll
Fetch messages received on a long-term number. Returns the rental plus a messages array; poll this rather than re-renting.
POST /long-term/rentals/{id}/extend
Takes days. Charges the wallet for the extra period and pushes expires_at out.
EMAIL VERIFICATION
Temporary email addresses
Order a disposable email address scoped to a website, then poll it for the verification code or the full message it receives. You pass the website you are signing up to (a link is fine — the host is extracted for you) and pick one of the domains offered for it.
GET /email/domains
List the email domains available for a website, with live pricing and availability. Pass the site as a bare host or a full link in ?site=. Authentication is required.
GET /api/v1/email/domains?site=https://example.com/signup{
"success": true,
"site": "example.com",
"data": [
{ "domain": "gmail.com", "price": 120, "available": 253701 },
{ "domain": "outlook.com", "price": 95, "available": null }
]
}
price is the final price in NGN (a JSON number — see number formats). available is the provider's inventory count for that domain, or null when it does not report one; domains are ordered most-used first.
POST /email/order
Order a temporary address for a site and domain. The wallet is charged up front, and the order auto-expires and refunds if no message arrives within ~20 minutes. Requires a verified email account.
| PARAMETER | TYPE | DESCRIPTION |
|---|---|---|
site | string | The website you are verifying against — a host or full link |
domain | string | A domain from the domains response, such as gmail.com |
curl -X POST -H "X-API-KEY: rn_your_key" \
-H "Content-Type: application/json" \
-d '{"site":"example.com","domain":"gmail.com"}' \
https://rentnumber.net/api/v1/email/order
{
"success": true,
"order_id": 501,
"email": "quiet.harbor@gmail.com",
"site": "example.com",
"domain": "gmail.com",
"status": "waiting",
"cost": 120,
"expires_at": "2026-04-12T15:30:00Z"
}
GET /email/orders/{id}
Poll one order for its status and, once received, the extracted code and the full message body. Call this while you wait rather than re-ordering.
{
"success": true,
"order_id": 501,
"email": "quiet.harbor@gmail.com",
"site": "example.com",
"domain": "gmail.com",
"status": "received",
"code": "123456",
"message": "Your verification code is 123456",
"cost": 120,
"created_at": "2026-04-12 15:10:00",
"expires_at": "2026-04-12T15:30:00Z"
}
code and message are null until a message arrives.
waitingreceivedcancelledexpiredrefundedPOST /email/orders/{id}/cancel
Cancel an order that has not received a message. The refund happens only when the provider actually accepts the cancellation — cancellation is refused in the first ~2 minutes, so an early call leaves the order live and unrefunded.
{
"success": true,
"refunded": true,
"status": "refunded",
"message": "Order cancelled and refunded.",
"amount": 120
}
When the provider refuses, refunded is false, amount is 0 and status keeps the order's current value.
GET /email/orders
List your email orders, newest first. Authentication is required.
{
"success": true,
"data": [{
"order_id": 501,
"email": "quiet.harbor@gmail.com",
"site": "example.com",
"domain": "gmail.com",
"status": "received",
"code": "123456",
"cost": 120,
"created_at": "2026-04-12 15:10:00"
}]
}
QUICK START
Code examples
const API_KEY = 'rn_your_key';
const BASE = 'https://rentnumber.net/api/v1';
const response = await fetch(`${BASE}/balance`, {
headers: { 'X-API-KEY': API_KEY }
});
const data = await response.json();
console.log(data.balance);import requests
API_KEY = "rn_your_key"
BASE = "https://rentnumber.net/api/v1"
headers = {"X-API-KEY": API_KEY}
response = requests.get(f"{BASE}/balance", headers=headers)
print(response.json())$apiKey = 'rn_your_key';
$base = 'https://rentnumber.net/api/v1';
$ch = curl_init("$base/balance");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: $apiKey"]
]);
echo curl_exec($ch);