AI Automation & Developer API Documentation
Turnkey Code & Bot Blueprints for Virtual SMS OTP Numbers, Social Media SMM Panels & VTU utility automation.
Turnkey Telegram Bot & Wholesale API Documentation
Welcome to the official developer documentation for the VansOTP wholesale platform. Build automated Telegram bot stores, SMM reseller portals, and VTU utility billing platforms with sub-50ms order creation and guaranteed carrier delivery.
140+ Countries
WhatsApp, Telegram, Google, ChatGPT, Instagram, etc.
1,000+ Services
Followers, Likes, Views for TikTok, IG, YouTube, X.
100% Automated
If no SMS arrives in 15 mins, wallet is refunded automatically.
verified Key Reseller Architecture Overview
- β’ No Inventory Cost: You don't need physical SIM cards or modem pools. Connect via API to our global carrier pool.
- β’ Set Your Own Prices: We charge you wholesale prices (e.g. $0.60 per WhatsApp number). You charge your customers retail prices (e.g. β¦1,500 or $1.50) and keep 100% of the profit.
- β’ Fully Turnkey: We provide the complete Python bot code with SQLite database, admin approval buttons, and customer payment methods.
How a Telegram Bot Actually Works
Understanding the difference between the Telegram App (the Shop Window) and bot.py (the Brain/Server).
β οΈ Critical Concept to Understand:
You do NOT copy or paste the bot.py code inside the Telegram app on your phone. Telegram is only a messaging app β it does not execute Python code.
1. Telegram App = The SHOP FRONT
This is what you and your customers see on phones or desktops.
- β Create the bot handle with @BotFather
- β Get your BOT TOKEN (a password string)
- β Customers browse menus, choose countries, and receive SMS OTPs here
2. bot.py = The BRAIN / ENGINE
This is the Python script that you save and run on a computer or cloud VPS server.
- β Listens for customer button clicks & orders
- β Calls VansOTP Wholesale API in real-time
- β Manages customer wallet balances in local SQLite database
- β Sends automated SMS OTP codes back to the customer
End-to-End Automation Flow:
[Customer in Telegram App]
β¬ (types /start, clicks "Buy WhatsApp USA")
[Your bot.py running on VPS Server / PC]
β¬ (checks customer balance in sqlite db, calls VansOTP API)
[VansOTP Direct Wholesale Carrier API]
β¬ (provisions number & reads incoming SMS in sub-50ms)
[bot.py delivers SMS OTP Code to Customer in Telegram automatically! π]
How to Set Your Profit Margins & Earn via API
VansOTP acts as your silent wholesale provider. You decide the retail selling price to charge your customers on your Telegram bot or website.
Step-by-Step Telegram Bot Build Guide
Follow these 5 simple steps to get your automated Telegram bot selling numbers and services.
Create Your Telegram Bot & Get Bot Token
- Open Telegram and search for @BotFather (the official verified Telegram bot creator).
- Send
/newbot. - Give your bot a friendly name (e.g. Fast OTP Store) and a unique username ending in
bot(e.g. FastOtpStore_bot). - @BotFather will give you an API Token like:
7123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ. Copy this token.
Get Your VansOTP Wholesale API Key
- Log in to your Developer API Dashboard.
- Click Generate API Key (requires 2FA Google Authenticator enabled for security).
- Copy your secret API key (e.g.
otp_live_abc123...). - Go to Wallet → Deposit and fund your balance (e.g. $10 or β¦5,000) so your bot can buy numbers.
Save and Configure `bot.py`
Open any code editor (VS Code, Notepad, etc.), create a file named bot.py, paste the code from the next section, and edit these 4 lines at the top:
BOT_TOKEN = "7123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ" # From @BotFather API_KEY = "otp_live_your_secret_key_here" # From VansOTP Dashboard BASE_URL = "https://www.vansotp.com/api/v1" # Platform API Endpoint ADMIN_TELEGRAM_ID = 123456789 # Your Telegram numeric ID (get from @userinfobot)
Option A: Run Locally on Your PC/Mac (Free)
Install dependencies and run the bot directly in your command prompt or terminal:
pip install requests python-telegram-bot python bot.py
Note: The bot only runs while your computer is turned on and connected to the internet.
Option B & C: Run 24/7 on Cloud VPS (Recommended)
To keep your bot selling 24 hours a day without keeping your computer on, run it on a Linux cloud server:
# 1. Connect via SSH ssh ubuntu@your_vps_ip # 2. Run bot in the background (stays online after closing terminal) nohup python3 bot.py > bot.log 2>&1 &
π‘ Don't want to manage Linux servers? Get our Turnkey Managed VPS (β¦5,000 / $3.50 for 3 months, or 100% FREE with the Reseller SMPP plan).
Production Telegram Bot Script (`bot.py`)
Complete with SQLite customer database, 1-tap admin deposit approval buttons, `/fund`, `/deduct`, and automated OTP polling.
import time
import requests
import sqlite3
import logging
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (
ApplicationBuilder, CommandHandler, CallbackQueryHandler,
ContextTypes, MessageHandler, filters
)
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
level=logging.INFO
)
# ==========================================
# βοΈ CONFIGURATION SETTINGS (EDIT THESE)
# ==========================================
BOT_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN_FROM_BOTFATHER"
API_KEY = "YOUR_VansOTP_API_KEY"
BASE_URL = "https://www.vansotp.com/api/v1"
ADMIN_TELEGRAM_ID = 123456789 # Replace with your numeric Telegram User ID (from @userinfobot)
# Retail Prices in NGN (You set these retail prices - keep 100% markup profit!)
RETAIL_PRICES = {
"whatsapp_us": 1200.0,
"whatsapp_uk": 1400.0,
"telegram_us": 1000.0,
"google_us": 800.0,
"tiktok_views_1k": 500.0,
"ig_followers_1k": 1800.0
}
# Your Bank Account for Manual Customer Transfers
BANK_NAME = "Opay / Moniepoint / Palmpay"
ACCOUNT_NUMBER = "1234567890"
ACCOUNT_NAME = "VansOTP Reseller Store"
# ==========================================
# ποΈ SQLITE DATABASE INITIALIZATION
# ==========================================
def init_db():
conn = sqlite3.connect("bot_store.db")
cursor = conn.cursor()
# Users table
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
telegram_id INTEGER PRIMARY KEY,
username TEXT,
full_name TEXT,
balance REAL DEFAULT 0.0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
# Deposit requests table
cursor.execute("""
CREATE TABLE IF NOT EXISTS deposit_requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
telegram_id INTEGER,
username TEXT,
amount REAL,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
# Transactions log (purchases & deposits)
cursor.execute("""
CREATE TABLE IF NOT EXISTS transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
telegram_id INTEGER,
type TEXT,
amount REAL,
description TEXT,
status TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.commit()
conn.close()
init_db()
def get_or_create_user(user):
conn = sqlite3.connect("bot_store.db")
cursor = conn.cursor()
cursor.execute("SELECT balance FROM users WHERE telegram_id = ?", (user.id,))
row = cursor.fetchone()
if not row:
cursor.execute(
"INSERT INTO users (telegram_id, username, full_name, balance) VALUES (?, ?, ?, ?)",
(user.id, user.username or "", user.full_name or "", 0.0)
)
conn.commit()
balance = 0.0
else:
balance = row[0]
cursor.execute(
"UPDATE users SET username = ?, full_name = ? WHERE telegram_id = ?",
(user.username or "", user.full_name or "", user.id)
)
conn.commit()
conn.close()
return balance
def update_user_balance(telegram_id, delta, tx_type="admin", description=""):
conn = sqlite3.connect("bot_store.db")
cursor = conn.cursor()
cursor.execute("UPDATE users SET balance = balance + ? WHERE telegram_id = ?", (delta, telegram_id))
cursor.execute(
"INSERT INTO transactions (telegram_id, type, amount, description, status) VALUES (?, ?, ?, ?, ?)",
(telegram_id, tx_type, delta, description, "completed")
)
conn.commit()
cursor.execute("SELECT balance FROM users WHERE telegram_id = ?", (telegram_id,))
new_bal = cursor.fetchone()[0]
conn.close()
return new_bal
# ==========================================
# π USER COMMANDS & HANDLERS
# ==========================================
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
user = update.effective_user
bal = get_or_create_user(user)
keyboard = [
[InlineKeyboardButton("π± Buy WhatsApp USA (β¦1,200)", callback_data="buy:whatsapp:US:1200")],
[InlineKeyboardButton("βοΈ Buy Telegram USA (β¦1,000)", callback_data="buy:telegram:US:1000")],
[InlineKeyboardButton("π Buy Google/Gmail USA (β¦800)", callback_data="buy:google:US:800")],
[InlineKeyboardButton("π³ Fund Wallet", callback_data="menu_deposit"), InlineKeyboardButton("π° My Balance", callback_data="check_bal")],
[InlineKeyboardButton("π My Transactions", callback_data="my_tx"), InlineKeyboardButton("π Support", url="https://t.me/YourSupportUsername")]
]
await update.message.reply_text(
f"π Welcome {user.first_name} to **VansOTP Automated Store**!\n\n"
f"π° Your Balance: **β¦{bal:,.2f}**\n\n"
f"Select a service below for instant delivery:",
reply_markup=InlineKeyboardMarkup(keyboard),
parse_mode="Markdown"
)
async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
user = query.from_user
data = query.data
# Check Balance
if data == "check_bal":
bal = get_or_create_user(user)
await query.message.reply_text(f"π³ Your Wallet Balance is: **β¦{bal:,.2f}**", parse_mode="Markdown")
# View User Transactions
elif data == "my_tx":
conn = sqlite3.connect("bot_store.db")
c = conn.cursor()
c.execute("SELECT type, amount, description, created_at FROM transactions WHERE telegram_id = ? ORDER BY id DESC LIMIT 5", (user.id,))
rows = c.fetchall()
conn.close()
if not rows:
await query.message.reply_text("βΉοΈ You have no transaction history yet.")
return
tx_text = "π **Your Recent Transactions:**\n\n"
for r in rows:
sign = "+" if r[1] > 0 else ""
tx_text += f"β’ `{r[3][:16]}` | {r[0].upper()} | **{sign}β¦{r[1]:,.2f}**\n _{r[2]}_\n\n"
await query.message.reply_text(tx_text, parse_mode="Markdown")
# Deposit Menu
elif data == "menu_deposit":
keyboard = [
[InlineKeyboardButton("β¦1,000", callback_data="req_dep:1000"), InlineKeyboardButton("β¦2,000", callback_data="req_dep:2000")],
[InlineKeyboardButton("β¦5,000", callback_data="req_dep:5000"), InlineKeyboardButton("β¦10,000", callback_data="req_dep:10000")]
]
await query.message.reply_text(
f"π¦ **BANK TRANSFER DEPOSIT INSTRUCTIONS**\n\n"
f"β’ Bank: **{BANK_NAME}**\n"
f"β’ Account Number: `{ACCOUNT_NUMBER}`\n"
f"β’ Account Name: **{ACCOUNT_NAME}**\n\n"
f"Select the amount you want to transfer below:",
reply_markup=InlineKeyboardMarkup(keyboard),
parse_mode="Markdown"
)
# Customer Logs Deposit Request
elif data.startswith("req_dep:"):
amount = float(data.split(":")[1])
conn = sqlite3.connect("bot_store.db")
cursor = conn.cursor()
cursor.execute(
"INSERT INTO deposit_requests (telegram_id, username, amount) VALUES (?, ?, ?)",
(user.id, user.username or user.first_name, amount)
)
req_id = cursor.lastrowid
conn.commit()
conn.close()
await query.message.reply_text(
f"β
**Deposit Request #{req_id} Submitted!**\n\n"
f"Please transfer **β¦{amount:,.2f}** to `{ACCOUNT_NUMBER}` ({BANK_NAME}).\n"
f"Our admin is verifying your transfer and will approve your balance shortly!",
parse_mode="Markdown"
)
# Send 1-Tap Approval + Rejection to Admin
admin_markup = InlineKeyboardMarkup([
[InlineKeyboardButton(f"β
Approve β¦{amount:,.0f}", callback_data=f"adm_app:{req_id}:{user.id}:{amount}")],
[InlineKeyboardButton("β Reject", callback_data=f"adm_rej:{req_id}:{user.id}:{amount}")]
])
await context.bot.send_message(
chat_id=ADMIN_TELEGRAM_ID,
text=f"π **NEW DEPOSIT ALERT!**\n\n"
f"π€ User: {user.full_name} (@{user.username})\n"
f"π Telegram ID: `{user.id}`\n"
f"π° Amount: **β¦{amount:,.2f}**\n"
f"π Request ID: `#{req_id}`\n\n"
f"Tap below to approve or reject:",
reply_markup=admin_markup,
parse_mode="Markdown"
)
# Admin 1-Tap APPROVE Handler
elif data.startswith("adm_app:"):
if user.id != ADMIN_TELEGRAM_ID:
await query.answer("β Unauthorized!", show_alert=True)
return
_, req_id, cust_id, amt = data.split(":")
cust_id, amt, req_id = int(cust_id), float(amt), int(req_id)
conn = sqlite3.connect("bot_store.db")
c = conn.cursor()
c.execute("UPDATE deposit_requests SET status = 'approved' WHERE id = ?", (req_id,))
conn.commit()
conn.close()
new_bal = update_user_balance(cust_id, amt, tx_type="deposit", description=f"Deposit Request #{req_id}")
await query.edit_message_text(f"β
Approved Request #{req_id}. User `{cust_id}` credited β¦{amt:,.2f}. New balance: β¦{new_bal:,.2f}")
try:
await context.bot.send_message(
chat_id=cust_id,
text=f"π **PAYMENT CONFIRMED & CREDITED!**\n\n"
f"β¦{amt:,.2f} has been added to your wallet!\n"
f"π° New Balance: **β¦{new_bal:,.2f}**\n\n"
f"You can now order OTP numbers using /start.",
parse_mode="Markdown"
)
except Exception as e:
logging.error(f"Failed to notify user {cust_id}: {e}")
# Admin 1-Tap REJECT Handler
elif data.startswith("adm_rej:"):
if user.id != ADMIN_TELEGRAM_ID:
await query.answer("β Unauthorized!", show_alert=True)
return
_, req_id, cust_id, amt = data.split(":")
cust_id, amt, req_id = int(cust_id), float(amt), int(req_id)
conn = sqlite3.connect("bot_store.db")
c = conn.cursor()
c.execute("UPDATE deposit_requests SET status = 'rejected' WHERE id = ?", (req_id,))
conn.commit()
conn.close()
await query.edit_message_text(f"β Rejected Deposit Request #{req_id} (β¦{amt:,.2f}) for user `{cust_id}`.")
try:
await context.bot.send_message(
chat_id=cust_id,
text=f"β **Deposit Request #{req_id} Rejected**\n\n"
f"We could not verify your transfer of β¦{amt:,.2f}.\n"
f"If you made this payment, please contact support.",
parse_mode="Markdown"
)
except Exception as e:
pass
# Order Virtual Number Handler
elif data.startswith("buy:"):
_, service_slug, country_iso, price = data.split(":")
price = float(price)
bal = get_or_create_user(user)
if bal < price:
await query.message.reply_text(
f"β Insufficient balance (β¦{bal:,.2f}). This number costs β¦{price:,.2f}.\n"
f"Please click 'Fund Wallet' to top up.",
parse_mode="Markdown"
)
return
# Deduct wallet & call VansOTP Wholesale API
update_user_balance(user.id, -price, tx_type="purchase", description=f"{service_slug.upper()} ({country_iso}) Number")
msg = await query.message.reply_text("π Ordering virtual number from wholesale pool...")
headers = {"X-API-KEY": API_KEY, "Accept": "application/json"}
resp = requests.post(f"{BASE_URL}/numbers/order", json={"service_slug": service_slug, "country_iso": country_iso}, headers=headers)
if resp.status_code != 200 or not resp.json().get("success"):
update_user_balance(user.id, price, tx_type="refund", description=f"Refund: {service_slug} unavailable")
await msg.edit_text("β Carrier pool busy or no numbers available. Wallet refunded 100%.")
return
order_data = resp.json()["data"]["order"]
order_id = order_data["order_id"]
phone_num = order_data["phone_number"]
await msg.edit_text(
f"π **NUMBER READY!**\n\n"
f"π Number: `{phone_num}`\n"
f"π± Service: **{service_slug.upper()} ({country_iso})**\n\n"
f"β³ Waiting for incoming SMS code (will auto-poll for 15 minutes)...",
parse_mode="Markdown"
)
# Auto-poll for SMS OTP code (up to 5 minutes / 60 iterations of 5s)
for _ in range(60):
time.sleep(5)
chk = requests.get(f"{BASE_URL}/numbers/{order_id}/sms", headers=headers)
if chk.status_code == 200:
sms_res = chk.json()["data"]
if sms_res.get("sms_received"):
code = sms_res.get("latest_code")
full_text = sms_res.get("latest_message")
await query.message.reply_text(
f"π¬ **YOUR VERIFICATION CODE HAS ARRIVED!**\n\n"
f"π Code: `{code}`\n\n"
f"Full Message:\n_{full_text}_",
parse_mode="Markdown"
)
return
# Timeout: Auto-cancel and auto-refund
requests.post(f"{BASE_URL}/numbers/{order_id}/cancel", headers=headers)
update_user_balance(user.id, price, tx_type="refund", description=f"Refund: OTP Timeout on {phone_num}")
await query.message.reply_text("β οΈ No SMS received within timeout. Your wallet has been 100% refunded.")
# ==========================================
# π ADMIN COMMANDS (Full Control Panel)
# ==========================================
async def admin_fund(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Credit a user's wallet: /fund """
if update.effective_user.id != ADMIN_TELEGRAM_ID: return
try:
target_id, amount = int(context.args[0]), float(context.args[1])
new_bal = update_user_balance(target_id, amount, tx_type="admin_credit", description="Manual Admin Credit")
await update.message.reply_text(f"β
Credited `{target_id}` with β¦{amount:,.2f}. New Balance: β¦{new_bal:,.2f}")
await context.bot.send_message(chat_id=target_id, text=f"π Admin credited your wallet with β¦{amount:,.2f}!\nπ° Balance: β¦{new_bal:,.2f}")
except Exception as e:
await update.message.reply_text("Usage: `/fund `\nExample: `/fund 123456789 5000`", parse_mode="Markdown")
async def admin_deduct(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Deduct from a user's wallet: /deduct """
if update.effective_user.id != ADMIN_TELEGRAM_ID: return
try:
target_id, amount = int(context.args[0]), float(context.args[1])
new_bal = update_user_balance(target_id, -amount, tx_type="admin_debit", description="Manual Admin Debit")
await update.message.reply_text(f"β
Deducted β¦{amount:,.2f} from `{target_id}`. New Balance: β¦{new_bal:,.2f}")
await context.bot.send_message(chat_id=target_id, text=f"β οΈ Admin deducted β¦{amount:,.2f} from your wallet.\nπ° Balance: β¦{new_bal:,.2f}")
except Exception as e:
await update.message.reply_text("Usage: `/deduct `\nExample: `/deduct 123456789 1000`", parse_mode="Markdown")
async def admin_transactions(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""View recent transactions: /transactions"""
if update.effective_user.id != ADMIN_TELEGRAM_ID: return
conn = sqlite3.connect("bot_store.db")
c = conn.cursor()
c.execute("SELECT id, telegram_id, type, amount, description, created_at FROM transactions ORDER BY id DESC LIMIT 10")
rows = c.fetchall()
conn.close()
if not rows:
await update.message.reply_text("No transactions found.")
return
text = "π **Recent 10 Transactions:**\n\n"
for r in rows:
sign = "+" if r[3] > 0 else ""
text += f"β’ `#{r[0]}` | User: `{r[1]}` | {r[2].upper()}\n Amount: **{sign}β¦{r[3]:,.2f}** | _{r[4]}_\n Date: `{r[5][:16]}`\n\n"
await update.message.reply_text(text, parse_mode="Markdown")
async def admin_users(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""View registered users: /users"""
if update.effective_user.id != ADMIN_TELEGRAM_ID: return
conn = sqlite3.connect("bot_store.db")
c = conn.cursor()
c.execute("SELECT telegram_id, username, full_name, balance FROM users ORDER BY balance DESC LIMIT 15")
rows = c.fetchall()
c.execute("SELECT COUNT(*), SUM(balance) FROM users")
total_users, total_bal = c.fetchone()
conn.close()
text = f"π₯ **Bot User Directory:**\nTotal Users: **{total_users}** | Total User Balances: **β¦{(total_bal or 0):,.2f}**\n\n"
for r in rows:
text += f"β’ `{r[0]}` | @{r[1] or 'NoUser'} ({r[2]})\n π° Balance: **β¦{r[3]:,.2f}**\n\n"
await update.message.reply_text(text, parse_mode="Markdown")
async def admin_broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Broadcast announcement to all users: /broadcast """
if update.effective_user.id != ADMIN_TELEGRAM_ID: return
msg = " ".join(context.args)
if not msg:
await update.message.reply_text("Usage: `/broadcast `", parse_mode="Markdown")
return
conn = sqlite3.connect("bot_store.db")
c = conn.cursor()
c.execute("SELECT telegram_id FROM users")
users = c.fetchall()
conn.close()
sent = 0
for (u_id,) in users:
try:
await context.bot.send_message(chat_id=u_id, text=f"π’ **ANNOUNCEMENT:**\n\n{msg}", parse_mode="Markdown")
sent += 1
except Exception:
pass
await update.message.reply_text(f"β
Broadcast sent to **{sent}/{len(users)}** users!")
# ==========================================
# π MAIN ENTRY POINT
# ==========================================
if __name__ == "__main__":
print("π€ VansOTP Reseller Telegram Bot Engine is running...")
app = ApplicationBuilder().token(BOT_TOKEN).build()
# User Handlers
app.add_handler(CommandHandler("start", start))
app.add_handler(CallbackQueryHandler(handle_callback))
# Admin Handlers
app.add_handler(CommandHandler("fund", admin_fund))
app.add_handler(CommandHandler("deduct", admin_deduct))
app.add_handler(CommandHandler("transactions", admin_transactions))
app.add_handler(CommandHandler("users", admin_users))
app.add_handler(CommandHandler("broadcast", admin_broadcast))
app.run_polling()
24/7 Turnkey Managed Cloud VPS
Keep your Telegram reseller bot selling 24/7 on dedicated cloud servers with 99.9% uptime, auto-restarts, and zero coding required.
3-Month Turnkey Bot Cloud VPS
PRO RESELLER PERK: This 3-Month Managed VPS is included 100% FREE when you subscribe to the Reseller SMPP Plan!
Connecting Your Website or SMM Panel to VansOTP API
Step-by-step guide for resellers who want to integrate our Virtual Number, OTP, and Social SMM services directly into their own websites, apps, or SMM reseller panels.
What is the API Key and How Does It Work?
Think of your Secret API Key as your website's password identity card to our server. Every time your website needs to order a virtual number or check an OTP, it sends this key along with the request. Our server reads it, verifies your wallet has funds, fulfills the order, deducts the cost β and replies with the result in under 1 second.
4-Step Website Setup Plan
Fund Your Wallet on VansOTP
Before any API call can succeed, your wallet must have a balance. Log in β Dashboard β Wallet β Add Funds (Paystack / Bank Transfer / Crypto). This balance is your wholesale stock β you will charge your customers a higher retail price and keep the difference as profit.
Generate Your Secret API Key
Go to Dashboard β Developer API. Enable 2FA (Google Authenticator), then click "Generate API Key". Copy the key β it looks like:
otp_live_xxxxxxxxxxxxxxxxxxxxxxxx
β οΈ Keep this key secret! Never share it publicly or put it in front-end JavaScript.
Add the API Key to Your Website's Config
On your own website/server, store the key in a safe config file β never in public HTML or JavaScript. Common methods:
VansOTP_API_KEY=otp_live_xxxxxxxxxxxxxxxxxxxxxxxx VansOTP_BASE_URL=https://www.vansotp.com/api/v1
VansOTP_API_KEY=otp_live_xxxxxxxxxxxxxxxxxxxxxxxx VansOTP_BASE_URL=https://www.vansotp.com/api/v1
Go to your SMM panel admin β Settings β API Providers β Add Provider. Fill in the API URL and paste your key. Done!
Test With a Live API Call
Make your first API call to confirm everything is connected. The quickest test is checking your wallet balance:
curl -H "X-API-KEY: otp_live_xxxxx" \
https://www.vansotp.com/api/v1/user/balance
β If you get back a JSON with your balance, your website is now fully connected to VansOTP!
What Can You Sell on Your Website Using This API?
PHP Website Integration (Laravel / Plain PHP)
Complete production-ready PHP code to buy virtual numbers, check OTP codes, and list services from your own website backend.
<?php
/**
* ============================================================
* VansOTP PHP Integration Client
* Drop this file into your project and use the functions below
* ============================================================
*/
define('VansOTP_API_KEY', 'otp_live_YOUR_SECRET_KEY_HERE');
define('VansOTP_BASE_URL', 'https://www.vansotp.com/api/v1');
/**
* Make authenticated GET or POST request to VansOTP API
*/
function VansOTP_request(string $endpoint, array $body = [], string $method = 'GET'): array {
$url = VansOTP_BASE_URL . '/' . ltrim($endpoint, '/');
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-KEY: ' . VansOTP_API_KEY,
'Accept: application/json',
'Content-Type: application/json',
],
]);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$raw = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ['code' => $code, 'data' => json_decode($raw, true)];
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 1. CHECK YOUR WALLET BALANCE
// βββββββββββββββββββββββββββββββββββββββββββββ
function VansOTP_get_balance(): float {
$res = VansOTP_request('/user/balance');
return (float) ($res['data']['balance'] ?? 0);
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 2. LIST ALL AVAILABLE SERVICES (Virtual Numbers)
// βββββββββββββββββββββββββββββββββββββββββββββ
function VansOTP_get_services(): array {
$res = VansOTP_request('/numbers/services');
return $res['data']['services'] ?? [];
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 3. ORDER A VIRTUAL NUMBER (e.g. for WhatsApp US)
// βββββββββββββββββββββββββββββββββββββββββββββ
function VansOTP_order_number(string $country_iso, string $service_slug): array {
$res = VansOTP_request('/numbers/order', [
'country_iso' => $country_iso, // e.g. "US", "NG", "GB"
'service_slug' => $service_slug, // e.g. "whatsapp", "telegram", "google"
], 'POST');
return $res['data']['order'] ?? [];
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 4. CHECK FOR RECEIVED SMS / OTP CODE
// βββββββββββββββββββββββββββββββββββββββββββββ
function VansOTP_check_sms(string $order_id): array {
$res = VansOTP_request("/numbers/{$order_id}/sms");
return $res['data'] ?? [];
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 5. CANCEL A NUMBER (triggers auto-refund)
// βββββββββββββββββββββββββββββββββββββββββββββ
function VansOTP_cancel_number(string $order_id): bool {
$res = VansOTP_request("/numbers/{$order_id}/cancel", [], 'POST');
return ($res['code'] === 200);
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 6. ORDER SOCIAL SMM SERVICE
// βββββββββββββββββββββββββββββββββββββββββββββ
function VansOTP_order_social(int $service_id, string $link, int $quantity): array {
$res = VansOTP_request('/social/order', [
'service_id' => $service_id,
'link' => $link, // e.g. Instagram profile URL
'quantity' => $quantity, // e.g. 1000 followers
], 'POST');
return $res['data'] ?? [];
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// β
EXAMPLE USAGE ON YOUR WEBSITE PAGE:
// βββββββββββββββββββββββββββββββββββββββββββββ
// 1. Show balance:
$balance = VansOTP_get_balance();
echo "My Reseller Wallet: $" . number_format($balance / 100, 2);
// 2. Order a WhatsApp number for a customer:
$order = VansOTP_order_number('US', 'whatsapp');
$phone = $order['phone_number'] ?? '';
$order_id = $order['id'] ?? '';
echo "Customer's number: " . $phone;
// 3. Poll for the OTP code (check every 5 seconds):
for ($i = 0; $i < 60; $i++) {
sleep(5);
$sms = VansOTP_check_sms($order_id);
if (!empty($sms['latest_code'])) {
echo "OTP Code: " . $sms['latest_code'];
break;
}
}
// 4. If no OTP arrived, cancel and auto-refund customer:
VansOTP_cancel_number($order_id);
echo "No OTP β number cancelled and customer refunded.";
Laravel-Specific Setup (if you use Laravel)
For Laravel apps, add to your .env file, then use Laravel's built-in Http facade:
# .env
VansOTP_API_KEY=otp_live_xxxxxxxxxxxxxxxxxxxxxxxx
VansOTP_BASE_URL=https://www.vansotp.com/api/v1
# Then in your Controller:
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders([
'X-API-KEY' => env('VansOTP_API_KEY'),
'Accept' => 'application/json',
])->post(env('VansOTP_BASE_URL') . '/numbers/order', [
'country_iso' => 'US',
'service_slug' => 'whatsapp',
]);
$order = $response->json('order');
echo "Phone: " . $order['phone_number'];
Node.js / Express / React Website Integration
Complete Node.js backend client for Express API servers and Next.js/React apps. Use this on your server-side code only β never expose your API key in front-end browser code.
β οΈ IMPORTANT β Never Put Your API Key in React/Vue/Angular Front-End Code!
Your API key must only exist in your server-side Node.js/Express code β not in any front-end JavaScript that runs in the browser. Anyone who sees your front-end code can steal the key and drain your wallet. Always call VansOTP from your backend, never directly from React components.
// ============================================================
// VansOTP Node.js Integration Client
// Save as: lib/VansOTP.js (SERVER SIDE ONLY!)
// ============================================================
require('dotenv').config();
const axios = require('axios');
const API_KEY = process.env.VansOTP_API_KEY; // from .env
const BASE_URL = process.env.VansOTP_BASE_URL || 'https://www.vansotp.com/api/v1';
const client = axios.create({
baseURL: BASE_URL,
timeout: 30000,
headers: {
'X-API-KEY': API_KEY,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
});
// βββββββββββββββββββββββββββββββββββββββββββββ
// 1. CHECK WALLET BALANCE
// βββββββββββββββββββββββββββββββββββββββββββββ
async function getBalance() {
const { data } = await client.get('/user/balance');
return data.balance; // in minor units (cents/kobo)
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 2. GET AVAILABLE SERVICES (Virtual Numbers)
// βββββββββββββββββββββββββββββββββββββββββββββ
async function getServices() {
const { data } = await client.get('/numbers/services');
return data.services;
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 3. ORDER A VIRTUAL NUMBER
// βββββββββββββββββββββββββββββββββββββββββββββ
async function orderNumber(countryIso, serviceSlug) {
// countryIso: 'US', 'NG', 'GB', 'IN' etc.
// serviceSlug: 'whatsapp', 'telegram', 'google', 'facebook' etc.
const { data } = await client.post('/numbers/order', {
country_iso: countryIso,
service_slug: serviceSlug,
});
return data.order; // { id, phone_number, expires_at, ... }
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 4. POLL FOR SMS OTP CODE
// βββββββββββββββββββββββββββββββββββββββββββββ
async function checkSms(orderId) {
const { data } = await client.get(`/numbers/${orderId}/sms`);
return data; // { sms_received: bool, latest_code: '123456', latest_message: '...' }
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 5. CANCEL NUMBER (auto-refunds wallet)
// βββββββββββββββββββββββββββββββββββββββββββββ
async function cancelNumber(orderId) {
await client.post(`/numbers/${orderId}/cancel`);
return true;
}
// βββββββββββββββββββββββββββββββββββββββββββββ
// 6. WAIT FOR OTP (helper β polls until code arrives or timeout)
// βββββββββββββββββββββββββββββββββββββββββββββ
async function waitForOtp(orderId, timeoutSeconds = 300) {
const deadline = Date.now() + timeoutSeconds * 1000;
while (Date.now() < deadline) {
await new Promise(r => setTimeout(r, 5000)); // wait 5s
const sms = await checkSms(orderId);
if (sms.sms_received && sms.latest_code) {
return sms.latest_code;
}
}
// Timeout β cancel and get refund
await cancelNumber(orderId);
return null;
}
module.exports = { getBalance, getServices, orderNumber, checkSms, cancelNumber, waitForOtp };
// βββββββββββββββββββββββββββββββββββββββββββββ
// β
EXAMPLE: Express.js Route on Your Website
// βββββββββββββββββββββββββββββββββββββββββββββ
// File: routes/numbers.js
const express = require('express');
const router = express.Router();
const otp = require('../lib/VansOTP');
// Your customer clicks "Buy WhatsApp Number" on your website:
router.post('/buy-number', async (req, res) => {
const { country, service } = req.body;
try {
const order = await otp.orderNumber(country, service);
const code = await otp.waitForOtp(order.id, 300);
if (code) {
res.json({ success: true, phone: order.phone_number, otp: code });
} else {
res.json({ success: false, message: 'No OTP received. Refund issued.' });
}
} catch (err) {
res.status(500).json({ error: err.message });
}
});
module.exports = router;
// βββββββββββββββββββββββββββββββββββββββββββββ
// .env file for your Node.js server:
// βββββββββββββββββββββββββββββββββββββββββββββ
// VansOTP_API_KEY=otp_live_xxxxxxxxxxxxxxxxxxxxxxxx
// VansOTP_BASE_URL=https://www.vansotp.com/api/v1
Using a Ready-Made SMM Panel Script? (No Coding Needed)
If you purchased or are using a ready-made SMM reseller panel (like Perfect Panel, SMMKing, or a custom PHP SMM script), integration is even simpler β no coding required:
Log in to your SMM Panel Admin Dashboard
Go to Settings β API Providers β Add New Provider
Enter API URL: https://www.vansotp.com/api/v1
Enter API Key: paste your otp_live_xxxx key from your dashboard
Click Save β your panel now imports and sells our services automatically at your markup prices!
How Customers Pay You on Telegram & Web
Choose the payment method that best fits your customers. All revenue goes 100% to your accounts.
Manual Bank Transfer + 1-Tap Admin Approval (Included in bot.py)
The customer sends money to your bank account (Opay, Moniepoint, GTBank, etc.) and clicks their deposit amount in the bot. You receive an instant Telegram alert with an [Approve] button. When you tap Approve, the customer's bot balance is credited instantly!
Automated Card & USSD Gateway (Paystack / Flutterwave)
Your bot generates a dynamic Paystack payment checkout link. Once the customer completes payment with debit card or USSD, the webhook automatically credits their bot balance without any admin action.
Telegram Stars (Native In-App Currency)
Customers pay using Apple Pay, Google Pay, or Telegram Stars directly inside the chat interface without leaving Telegram.
Automated Cryptocurrency (USDT TRC-20 / TON)
Ideal for international customers. Provide your USDT address or use CryptoPay bot for instant block-explorer payment verification.
Admin Deposit Approvals & Management
How you (the bot owner) manually credit users, approve transfers, and manage your customer database.
touch_app 1. One-Tap Inline Approval Buttons (Easiest)
Whenever a customer sends a deposit request in the bot, bot.py forwards an alert directly to your Telegram chat with two buttons:
Simply tap Approve. The customer is credited immediately in SQLite, and the bot sends them a celebratory notification.
terminal 2. Telegram Admin Slash Commands
/fund <USER_TELEGRAM_ID> <AMOUNT>
Example: /fund 987654321 5000 — Credits β¦5,000 to user wallet.
/deduct <USER_TELEGRAM_ID> <AMOUNT>
Example: /deduct 987654321 1000 — Deducts β¦1,000 from user.
/transactions
View the last 10 purchases, deposits, and refunds across all users.
/users
List all bot users, their usernames, and their current wallet balances.
/broadcast <MESSAGE>
Send a live marketing push notification / announcement to all bot users.
Authentication & Base URL
All endpoints require standard HTTPS and API Key authentication.
curl -X GET "https://www.vansotp.com/api/v1/user/balance" \ -H "X-API-KEY: YOUR_SECRET_API_KEY" \ -H "Accept: application/json"
Check Wallet Balance & Tier
Returns your live USD balance, formatted string, and discount percentages.
{
"success": true,
"balance_usd": 45.50,
"balance_minor": 4550,
"currency": "USD",
"formatted_balance": "$45.50",
"discounts": {
"numbers_discount_percent": 20,
"social_discount_percent": 15,
"vtu_discount_percent": 2
}
}
Countries & Services Catalog
Fetch carrier country codes, flags, service slugs, and real-time inventory.
{
"success": true,
"data": [
{ "id": 1, "name": "United States", "iso": "US", "code": "+1" },
{ "id": 2, "name": "United Kingdom", "iso": "GB", "code": "+44" },
{ "id": 3, "name": "Nigeria", "iso": "NG", "code": "+234" }
]
}
Order Virtual SMS OTP Number
Instant provisioning of real carrier number. Debits wholesale cost from wallet.
{
"country_iso": "US",
"service_slug": "whatsapp"
}
{
"success": true,
"message": "Number provisioned successfully",
"data": {
"order": {
"order_id": "8c459fa8-9b88-4bf6-90da-8d769dfb40d1",
"phone_number": "+12025550198",
"country": "United States",
"service": "WhatsApp",
"expires_at": "2026-10-05T14:45:00Z"
}
}
}
Poll for Inbound SMS Verification Code
Call this every 3-5 seconds to check if the SMS OTP code has arrived.
{
"success": true,
"data": {
"sms_received": true,
"latest_code": "847291",
"latest_message": "Your WhatsApp code is 847-291. Do not share this code.",
"status": "completed"
}
}
Cancel Order & 100% Instant Refund
If no SMS was received, release the number and instantly credit your wallet.
{
"success": true,
"message": "Order cancelled successfully. 100% refunded to wallet.",
"data": {
"refund_amount_usd": 0.60,
"new_balance_usd": 45.50
}
}
Social Media SMM Boost API
Create follower, like, view, and comment orders for Instagram, TikTok, YouTube, and X.
{
"service_id": 14,
"target_url": "https://instagram.com/your_profile",
"quantity": 1000
}
Code SDKs & Website Integration
Connect VansOTP API directly from Python, Node.js, PHP, or cURL on your web servers.
import requests
API_KEY = "otp_live_your_api_key"
headers = {"X-API-KEY": API_KEY, "Accept": "application/json"}
# 1. Order Number
res = requests.post("https://www.vansotp.com/api/v1/numbers/order", json={"country_iso": "US", "service_slug": "whatsapp"}, headers=headers)
order = res.json()["data"]["order"]
print(f"Number provisioned: {order['phone_number']}")
const axios = require('axios');
const client = axios.create({
baseURL: 'https://www.vansotp.com/api/v1',
headers: { 'X-API-KEY': 'otp_live_your_api_key' }
});
async function orderNumber() {
const res = await client.post('/numbers/order', { country_iso: 'US', service_slug: 'whatsapp' });
console.log('Phone Number:', res.data.data.order.phone_number);
}
orderNumber();
<?php
$ch = curl_init("https://www.vansotp.com/api/v1/numbers/order");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['country_iso' => 'US', 'service_slug' => 'whatsapp']),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-KEY: otp_live_your_api_key'
]
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
echo "Phone Number: " . $response['data']['order']['phone_number'];
curl -X POST "https://www.vansotp.com/api/v1/numbers/order" \
-H "Content-Type: application/json" \
-H "X-API-KEY: YOUR_API_KEY" \
-d '{"country_iso":"US","service_slug":"whatsapp"}'
HTTP Status Codes, Rate Limits & FAQ
Complete reference for standard HTTP response codes and platform guarantees.
Standard plans allow 10 requests per second. Reseller SMPP Enterprise accounts have unlimited concurrent connections (up to 100 req/sec).
Your account wallet balance is too low for the wholesale cost. Top up via Dashboard → Wallet → Deposit.
The selected carrier pool is temporarily out of fresh stock. Retry after 60 seconds or choose another country.