Connect your own scripts and apps to your King Zee Social Hub account
Getting Started
Every King Zee Social Hub user can generate a personal API Key and API Secret from
Settings API Access. These two values authenticate every request
you make to the API — treat them like a password. Your secret is shown once at generation time and never again,
so save it somewhere safe immediately.
All endpoints are scoped to your own account only — you can check your own balance, list available items, view your
own purchase history, and buy items on your own behalf. You cannot access any other user's data.
Jump to a section
Everything below is generated from the endpoints this server actually runs — Boost, Buy Number Server 1, Buy Number Server 2 and the Marketplace.
Live API tester — confirm it works with your own key
Paste your key + secret and this page calls the real endpoints on this server, read-only.
Nothing is bought and your wallet is never charged — it only runs the browse/status calls for Boost,
Server 1 numbers, Server 2 numbers and the Marketplace so you can see exactly which ones answer.
Requests missing either header, or with an invalid/revoked key, get a 401 Unauthorized response.
Base URL
https://your-site.example.com/api/v1
Endpoints
Method
Endpoint
What it does
GET
/me
Your account info (name, email, balance, Game ID)
GET
/balance
Your current wallet balance
GET
/purchases
Your full purchase history
GET
/purchases/:purchaseId
Re-fetch one specific old purchase's full details (account credentials/download link) by its purchase ID
GET
/listings
All available logs/tools (add ?type=tool or ?type=log to filter)
GET
/listings/:id
Details of one specific listing — includes subAccounts array (with _id, label, price, sold) when it's a multi-account folder listing
POST
/buy/:id
Purchase a listing using your wallet balance. For folder listings, optionally pass { "subAccountId": "..." } to pick a specific account — omit it and the cheapest available one is bought automatically
POST
/buy-multi/:id
Buy several accounts from the same folder listing at once — body: { "subAccountIds": ["...", "..."] }. All-or-nothing: if any account was already sold, or your balance can't cover the total, nothing is charged
GET
/numbers/options
Providers/countries currently available to rent a number for, with price
POST
/buy-number
Rent a number — body: provider, country
GET
/get-sms
Poll for the OTP — query: order_id
POST
/cancel-number
Cancel before a code arrives and get refunded — body: order_id
import requests
res = requests.get(
"https://your-site.example.com/api/v1/listings",
params={"type": "tool"},
headers={
"X-API-Key": "your_api_key",
"X-API-Secret": "your_api_secret"
}
)
for tool in res.json():
print(tool["title"], tool["price"])
Example: Buy an item
No platform fee — you're charged exactly the item's listed price via the API, same as buying it through the marketplace. The response includes itemPrice and totalCharged (always equal) so you can confirm this.
import requests
res = requests.post(
"https://your-site.example.com/api/v1/buy/LISTING_ID_HERE",
headers={
"X-API-Key": "your_api_key",
"X-API-Secret": "your_api_secret"
}
)
result = res.json()
print(result["message"], result["newBalance"])
Example: Buying from a folder listing (multiple accounts under one item)
Some listings are "folders" containing several individually-priced accounts (e.g. "Twitter Logs Batch" with 20 accounts inside). Check listingType === "folder" and the subAccounts array from GET /listings/:id first to see what's available and each one's price.
# Step 1 — see what's inside the folder and each account's price
curl https://your-site.example.com/api/v1/listings/FOLDER_ID_HERE \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_api_secret"
# Step 2a — buy ONE specific account from it
curl -X POST https://your-site.example.com/api/v1/buy/FOLDER_ID_HERE \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_api_secret" \
-H "Content-Type: application/json" \
-d '{"subAccountId":"SUB_ACCOUNT_ID_HERE"}'
# Step 2b — or buy SEVERAL accounts from it in one call
curl -X POST https://your-site.example.com/api/v1/buy-multi/FOLDER_ID_HERE \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_api_secret" \
-H "Content-Type: application/json" \
-d '{"subAccountIds":["SUB_ID_1","SUB_ID_2","SUB_ID_3"]}'
const headers = { 'X-API-Key': 'your_api_key', 'X-API-Secret': 'your_api_secret' };
// 1. Look at what's available inside the folder
const listing = await fetch(`https://your-site.example.com/api/v1/listings/${folderId}`, { headers }).then(r => r.json());
const available = listing.subAccounts.filter(sa => !sa.sold);
// 2a. Buy one specific account
const one = await fetch(`https://your-site.example.com/api/v1/buy/${folderId}`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ subAccountId: available[0]._id })
}).then(r => r.json());
// 2b. Or buy several at once — all-or-nothing, nothing charged if any fail
const many = await fetch(`https://your-site.example.com/api/v1/buy-multi/${folderId}`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ subAccountIds: available.slice(0, 3).map(sa => sa._id) })
}).then(r => r.json());
console.log(many.message, many.items); // many.items is an array of the delivered account details
import requests
headers = {"X-API-Key": "your_api_key", "X-API-Secret": "your_api_secret"}
# 1. Look at what's available inside the folder
listing = requests.get(f"https://your-site.example.com/api/v1/listings/{folder_id}", headers=headers).json()
available = [sa for sa in listing["subAccounts"] if not sa["sold"]]
# 2a. Buy one specific account
one = requests.post(
f"https://your-site.example.com/api/v1/buy/{folder_id}",
headers=headers, json={"subAccountId": available[0]["_id"]}
).json()
# 2b. Or buy several at once
many = requests.post(
f"https://your-site.example.com/api/v1/buy-multi/{folder_id}",
headers=headers, json={"subAccountIds": [sa["_id"] for sa in available[:3]]}
).json()
print(many["message"], many["items"])
Example: Getting a past purchase's details again
Lost the account details from an old purchase, or need to look them up again later? Every successful /buy, /buy-multi, or on-site purchase returns a purchaseId — save it, then fetch that exact purchase any time with GET /purchases/:purchaseId.
Numbers are used to receive a one-time verification code from the provider you choose (WhatsApp, Telegram, etc). Prices here are separate from the site's own Buy Numbers page — check /numbers/options for what's currently available and its price. Want to see this exact flow (browse buy poll done) built into a full page? /buy-number on this site is that reference implementation — same four calls below, wired to a UI.
# 1. See what's available
curl https://your-site.example.com/api/v1/numbers/options \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_api_secret"
# 2. Rent a number
curl -X POST https://your-site.example.com/api/v1/buy-number \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_api_secret" \
-H "Content-Type: application/json" \
-d '{"provider":"WhatsApp","country":"United States"}'
# 3. Poll for the code (repeat every few seconds until status is "received")
curl "https://your-site.example.com/api/v1/get-sms?order_id=ORDER_ID_HERE" \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_api_secret"
# 4. If no code arrives, cancel for a refund
curl -X POST https://your-site.example.com/api/v1/cancel-number \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_api_secret" \
-H "Content-Type: application/json" \
-d '{"order_id":"ORDER_ID_HERE"}'
const headers = {
'X-API-Key': 'your_api_key',
'X-API-Secret': 'your_api_secret',
'Content-Type': 'application/json'
};
// 1. See what's available
const options = await (await fetch('https://your-site.example.com/api/v1/numbers/options', { headers })).json();
// 2. Rent a number
const order = await (await fetch('https://your-site.example.com/api/v1/buy-number', {
method: 'POST', headers, body: JSON.stringify({ provider: 'WhatsApp', country: 'United States' })
})).json();
// 3. Poll for the code
async function pollForCode(orderId) {
const r = await (await fetch(`https://your-site.example.com/api/v1/get-sms?order_id=${orderId}`, { headers })).json();
if (r.status === 'received') return r.sms;
await new Promise(res => setTimeout(res, 5000));
return pollForCode(orderId);
}
const code = await pollForCode(order.order_id);
import requests, time
headers = {"X-API-Key": "your_api_key", "X-API-Secret": "your_api_secret"}
# 1. See what's available
options = requests.get("https://your-site.example.com/api/v1/numbers/options", headers=headers).json()
# 2. Rent a number
order = requests.post(
"https://your-site.example.com/api/v1/buy-number",
headers=headers, json={"provider": "WhatsApp", "country": "United States"}
).json()
# 3. Poll for the code
while True:
r = requests.get(
"https://your-site.example.com/api/v1/get-sms",
headers=headers, params={"order_id": order["order_id"]}
).json()
if r["status"] == "received":
print(r["sms"])
break
time.sleep(5)
Buy Number — Server 1 (curated pool)
Server 1 is the hand-picked pool: a short list of providers (WhatsApp, Telegram, …) and countries the admin
has priced and enabled. It is the fastest path — one call to list options, one to rent, then poll for the code.
There are two equivalent ways in:
Developer API (recommended) — /api/v1/* with your X-API-Key + X-API-Secret.
Session endpoints — /api/numbers/*, used by the site's own /buy-number page; these need a signed-in session cookie/token instead of an API key.
Developer API (API key)
Method
Endpoint
Input
Returns
GET
/api/v1/numbers/options
—
Enabled providers + countries with your price each
POST
/api/v1/buy-number
{ provider, country }
{ order_id, number, price } — wallet is debited here
Which servers are switched on right now: { server1: { enabled }, server2: { enabled } }. Call this first and hide the ones that are off.
GET
/api/numbers/options
—
Priced provider/country list for Server 1
GET
/api/numbers/live-services
country (optional)
Live services with stock and price
GET
/api/numbers/live-countries
—
Countries currently holding stock
POST
/api/numbers/buy
{ provider, country }
Rents the number and debits the wallet
GET
/api/numbers/status/:orderId
—
Poll for the OTP for one order
POST
/api/numbers/cancel/:orderId
—
Cancel + refund while no code has arrived
GET
/api/numbers/my-orders
limit (optional)
Your recent number orders
GET
/api/numbers/my-orders/search
q
Search your own orders by number or order id
GET
/api/numbers/health
—
Upstream provider reachability — handy for a status page
Full flow (Server 1, developer API)
# 1. What can I rent, and for how much?
curl "$BASE/api/v1/numbers/options" \
-H "X-API-Key: $KEY" -H "X-API-Secret: $SECRET"
# 2. Rent one (wallet is charged here)
curl -X POST "$BASE/api/v1/buy-number" \
-H "X-API-Key: $KEY" -H "X-API-Secret: $SECRET" \
-H "Content-Type: application/json" \
-d '{"provider":"WhatsApp","country":"United States"}'
# 3. Poll every ~5s until status is "received"
curl "$BASE/api/v1/get-sms?order_id=ORDER_ID" \
-H "X-API-Key: $KEY" -H "X-API-Secret: $SECRET"
# 4. Nothing arrived? Cancel and get refunded
curl -X POST "$BASE/api/v1/cancel-number" \
-H "X-API-Key: $KEY" -H "X-API-Secret: $SECRET" \
-H "Content-Type: application/json" \
-d '{"order_id":"ORDER_ID"}'
Server 1 errors and how to fix them
Code
Message
Fix
400
Server 1 is temporarily switched off
An admin disabled Server 1. Check /api/numbers/servers and use Server 2 meanwhile.
400
provider and country are required
Send both, spelled exactly as returned by /numbers/options. Never hand-type them.
400
Insufficient balance
Top up. The charge is the price field from /numbers/options.
400
Out of stock for that provider/country
Re-read /numbers/options (or /api/numbers/live-services) and pick another. Nothing was charged.
401
Unauthorized
Missing/revoked X-API-Key + X-API-Secret, or you called a /api/numbers/* session endpoint without being signed in.
404
Order not found
Use the order_id exactly as returned by the buy call — it belongs to your account only.
502
Provider unreachable
Upstream hiccup. Retry the same call after a few seconds; nothing was charged.
Server 2 Numbers API (all services & countries)
Server 2 is the large pool — hundreds of services and countries, live stock and live prices.
You always call it in the same order: service → country → provider (price pool) → buy → poll.
These four endpoints are session endpoints on /api/numbers/server2/*; the browse calls
need no auth, the buy call charges your wallet and needs you signed in.
Method
Endpoint
Query / Body
Returns
GET
/api/numbers/server2/services
search (optional)
Array of { code, name, emoji, popular }, most popular first
GET
/api/numbers/server2/countries
service (required — the code from above)
Array of { id, name, flag, count, price } — only countries with stock, cheapest first
GET
/api/numbers/server2/providers
service, country (both required)
Array of price pools { label, maxPrice, price, count, flag }, cheapest first
GET
/api/numbers/server2/operators
service, country
Mobile operators available for that pair — pass nothing and any operator is used
POST
/api/numbers/server2/buy
{ service, country, maxPrice }
The rented number + order id. Stock and price are re-checked live before your wallet is touched
Full flow
# 1. Find the service code (e.g. search for WhatsApp)
curl "https://your-site.example.com/api/numbers/server2/services?search=whatsapp"
# 2. Countries that have stock for that service
curl "https://your-site.example.com/api/numbers/server2/countries?service=wa"
# 3. Price pools ("providers") for that service + country
curl "https://your-site.example.com/api/numbers/server2/providers?service=wa&country=187"
# 4. Buy — maxPrice MUST be the pool's maxPrice value, copied exactly
curl -X POST https://your-site.example.com/api/numbers/server2/buy \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_SESSION_TOKEN" \
-d '{"service":"wa","country":"187","maxPrice":0.42}'
const base = 'https://your-site.example.com/api/numbers/server2';
// 1. service
const services = await (await fetch(`${base}/services?search=whatsapp`)).json();
const service = services[0].code;
// 2. country (already sorted cheapest-first, so [0] is the cheapest in stock)
const countries = await (await fetch(`${base}/countries?service=${service}`)).json();
const country = countries[0].id;
// 3. provider / price pool
const pools = await (await fetch(`${base}/providers?service=${service}&country=${country}`)).json();
const pool = pools[0]; // cheapest
// 4. buy — send pool.maxPrice back untouched
const order = await (await fetch(`${base}/buy`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ service, country, maxPrice: pool.maxPrice })
})).json();
import requests
base = "https://your-site.example.com/api/numbers/server2"
service = requests.get(f"{base}/services", params={"search": "whatsapp"}).json()[0]["code"]
country = requests.get(f"{base}/countries", params={"service": service}).json()[0]["id"]
pool = requests.get(f"{base}/providers", params={"service": service, "country": country}).json()[0]
order = requests.post(
f"{base}/buy",
headers={"Authorization": f"Bearer {token}"},
json={"service": service, "country": country, "maxPrice": pool["maxPrice"]}
).json()
Server 2 errors and how to fix them
Status
Message you'll see
Fix
400
Server 2 is temporarily switched off
An admin disabled Server 2 in the admin panel. Use Server 1, or wait until it's switched back on — retrying won't help.
400
service is required / service and country are required
You skipped a step. The order is fixed: services → countries → providers. Never guess a country id; take it from the countries response.
400
Please re-pick service, country and provider
Your buy body is missing one of service, country, maxPrice, or maxPrice came through as 0. Send the pool's maxPrice exactly as returned (a decimal, not the ₦ price).
400
That provider just sold out — please pick another one
Stock moves constantly. Re-call /providers and pick a fresh pool. Nothing was charged.
400
Insufficient balance
Top up your wallet. The ₦ amount charged is the pool's price field, not maxPrice.
400
Provider did not return a number
Upstream hiccup. Nothing was charged — just retry, and if it repeats pick a different pool or country.
401
Unauthorized
The buy call needs a signed-in session token; the three browse calls do not.
502
Any upstream error text
The upstream number provider is unreachable or rejected the request. Wait a few seconds and retry the same call — this is never caused by your request body.
Golden rule: price and stock are re-verified server-side at the moment of purchase, so a stale
pool can only ever fail the buy — it can never overcharge you. Any failed buy leaves your balance untouched.
Boost API — social media growth (v1)
Resell followers, likes, views and subscribers from your own app. Same catalogue, same prices
and the same upstream panel that powers the King Zee Social Hub Boost page. Every call uses your
X-API-Key / X-API-Secret pair, and orders are charged to your King Zee Social Hub wallet in NGN.
There are no webhooks. The upstream panel does not send callbacks, so neither do we —
poll GET /api/v1/boost/order/:id (it refreshes live from the provider on every call).
Once every 2–5 minutes is plenty.
The response names the field. Quantity must sit between the service's min and max; the link must be a full http(s):// URL.
401
Missing or invalid key pair
Check both headers, or regenerate in Settings → API Access.
402
Insufficient wallet balance
Top up, then retry. Never blind-retry this one.
404
Unknown serviceId or order
Re-fetch /services — the panel drops services regularly.
502
Provider rejected or unreachable
Nothing was charged — your wallet is refunded inside the same request. Retry after a minute.
503
Boosting switched off
An admin disabled it, or the provider key is missing. Retry later.
Idempotency:POST /order is not idempotent. If a request times out on your side,
call GET /api/v1/boost/orders?limit=5 to check whether it landed before sending it again.
Errors
Status
Meaning
What to do
401
Missing or invalid API key/secret
Double-check the X-API-Key/X-API-Secret headers are set and match exactly what's shown in Settings API Access. Regenerating your key there invalidates the old one immediately.
403
Account restricted
Your account has been limited — contact support, this isn't something a retry fixes.
404
Listing not found or no longer available
Re-fetch /listings or /numbers/options — the item may have sold out or been removed since you last checked.
400
Insufficient balance, sold out, or invalid request
Check the response body's error field for the specific reason — it's always a plain-English message, not just the code.
402
Insufficient balance (numbers endpoints)
Deposit funds before retrying — no partial/temporary state is created, so it's safe to just retry once the balance is topped up.
409
No numbers currently available for that provider/country
This is temporary stock, not a config issue — wait a few seconds and retry, or check /numbers/options again in case that provider/country is no longer listed at all.
Numbers specifically: your money is only ever taken if a real number was actually issued — a failed /buy-number call never touches your balance, so it's always safe to just retry. Once you do have an order, it auto-refunds on its own if no code arrives in time — you don't have to call /cancel-number unless you want your balance back sooner than that.