Keys24x7 Reseller API
Resell our full digital-goods catalog on your own website, SMM panel or WooCommerce store. Orders debit your wallet and auto-deliver — stock credentials arrive instantly, manual items follow within their ETA. All delivery notifications go out by email.
These docs are public — read everything before you create an account.
Overview & Authentication
There are two API surfaces over the same account, wallet and catalog:
| API | Base URL | Auth | Best for |
|---|---|---|---|
| SMM Panel API | https://keys24x7.com/api/v2 |
key parameter (form or JSON) |
SMM panels (Perfect Panel, SmartPanel, Rental panels…) via the standard provider format |
| REST API v1 | https://keys24x7.com/api/v1 |
Authorization: Bearer <api_key> (or ?key=) |
Custom websites, AI-built storefronts, the WordPress plugin, scripts |
Get your API key by creating a reseller account — the key is issued instantly and shown on your dashboard, where you also top up your wallet.
402 and v2 responds {"error":"..."}. Suspended accounts get {"error":"Account suspended"} on every call.
SMM Panel API — POST /api/v2
The industry-standard SMM provider format: one endpoint, POST only, form-encoded (or JSON) body, an action parameter, responses in JSON. Errors always come back as {"error":"message"} with HTTP 200, as SMM panels expect.
POST action=services
Returns the full catalog as SMM services. service is a stable integer id; rate is the INR price per unit.
| Parameter | Required | Description |
|---|---|---|
key | Yes | Your API key |
action | Yes | services |
curl -X POST https://keys24x7.com/api/v2 \
-d 'key=YOUR_API_KEY' \
-d 'action=services'[
{
"service": 12,
"name": "Netflix Premium — 1 Month",
"type": "Package",
"category": "Streaming",
"rate": "99.00",
"min": 1,
"max": 14,
"refill": false,
"cancel": false
}
]max reflects available stock for instant-delivery products (falls back to 1000 for manual delivery).
POST action=add
Places an order: checks the product is active and your balance covers rate × quantity, debits the wallet atomically, then delivers instantly when stock is available.
| Parameter | Required | Description |
|---|---|---|
key | Yes | Your API key |
action | Yes | add |
service | Yes | Service id from action=services |
quantity | No | Defaults to 1 |
customer_email | No | Non-standard extra: your buyer's email — credentials are emailed there (you get a copy). Defaults to your reseller email. |
curl -X POST https://keys24x7.com/api/v2 \
-d 'key=YOUR_API_KEY' \
-d 'action=add' \
-d 'service=12' \
-d 'quantity=1' \
-d '[email protected]'{ "order": 5731 }POST action=status
Check one order. credentials is included only once the order is Completed.
| Parameter | Required | Description |
|---|---|---|
key | Yes | Your API key |
action | Yes | status |
order | Yes | Order id returned by add |
curl -X POST https://keys24x7.com/api/v2 \
-d 'key=YOUR_API_KEY' \
-d 'action=status' \
-d 'order=5731'{
"charge": "99.00",
"start_count": "0",
"status": "Completed",
"remains": "0",
"currency": "INR",
"credentials": "[email protected]:password123"
}| Status | Meaning |
|---|---|
Completed | Paid and delivered — credentials included |
In progress | Paid, delivery being prepared |
Pending | Awaiting fulfillment (manual-delivery products) |
Refunded | Charge returned to your wallet |
Canceled | Order cancelled |
POST action=status (multiple orders)
Pass orders as a comma-separated list (instead of order) to check up to 100 orders in one call.
curl -X POST https://keys24x7.com/api/v2 \
-d 'key=YOUR_API_KEY' \
-d 'action=status' \
-d 'orders=5731,5732,5733'{
"5731": { "charge": "99.00", "start_count": "0", "status": "Completed", "remains": "0", "currency": "INR", "credentials": "[email protected]:password123" },
"5732": { "charge": "199.00", "start_count": "0", "status": "In progress", "remains": "0", "currency": "INR" },
"5733": { "error": "Incorrect order ID" }
}POST action=balance
curl -X POST https://keys24x7.com/api/v2 \
-d 'key=YOUR_API_KEY' \
-d 'action=balance'{ "balance": "1234.50", "currency": "INR" }{ "error": "Not enough funds in the balance" }POST action=sync
Catalog sync for panels that prefer the v2 surface. Returns a version fingerprint alongside the services — store it, and skip the next import while it is unchanged. Add updated_since (ISO 8601) to receive only what changed since your last sync; rows that were deactivated come back with "active": false so you can disable them in your panel.
curl -X POST https://keys24x7.com/api/v2 \
-d 'key=YOUR_API_KEY' \
-d 'action=sync' \
-d 'updated_since=2026-08-05T00:00:00Z'{
"version": "9f2c1a77b0e34d51",
"product_count": 148,
"updated_at": "2026-08-05 12:41:03",
"services": [
{
"service": 12,
"name": "Netflix Premium — 1 Month",
"type": "Package",
"category": "Streaming",
"rate": "99.00",
"min": 1,
"max": 14,
"refill": false,
"cancel": false,
"active": true,
"updated_at": "2026-08-05 12:41:03"
}
]
}REST API v1
Clean JSON over proper HTTP verbs and status codes. Authenticate with Authorization: Bearer <api_key> (a ?key= query parameter also works for quick tests).
| Code | Meaning |
|---|---|
401 | Missing or invalid API key |
402 | Insufficient wallet funds |
404 | Unknown product or order |
409 | Price below supplier cost — the order was refused and your wallet was not debited. See below. |
409 | Out of stock (out_of_stock) — more requested than we hold; nothing debited. See below. |
409 | Retry still running (idempotency_in_flight) — see Idempotency. |
422 | Validation error (bad quantity, missing fields…) |
429 | Rate limit exceeded — see Rate limits |
GET /api/v1/products
Full catalog, including live stock for instant-delivery products.
curl https://keys24x7.com/api/v1/products \
-H 'Authorization: Bearer YOUR_API_KEY'[
{
"id": "netflix_premium_1m",
"api_service_id": 12,
"name": "Netflix Premium — 1 Month",
"category": "Streaming",
"description": "4K UHD private profile, instant delivery",
"price_inr": 99,
"price_usdt": 1.2,
"image_url": "https://keys24x7.com/images/netflix.png",
"delivery_type": "auto",
"eta_minutes": 0,
"in_stock": true,
"stock_count": 14,
"active": true,
"updated_at": "2026-08-05 12:41:03"
}
]Supports ?updated_since=, ETag and If-None-Match — see Keeping in sync.
GET /api/v1/products/:id
A single product — price, live stock, in_stock and the delivery type/ETA. Accepts the string id or the integer api_service_id. Use it to refresh one product without pulling the whole catalog (for example right before you take a payment).
curl https://keys24x7.com/api/v1/products/netflix_premium_1m \
-H 'Authorization: Bearer YOUR_API_KEY'{
"id": "netflix_premium_1m",
"api_service_id": 12,
"name": "Netflix Premium — 1 Month",
"price_inr": 99,
"delivery_type": "auto",
"eta_minutes": 0,
"in_stock": true,
"stock_count": 14,
"active": true,
"updated_at": "2026-08-05 12:41:03"
}POST /api/v1/orders
Create an order. Pass either product_id (string id) or service (integer id from the SMM services list). If the product delivers instantly, credentials is already in the response.
curl -X POST https://keys24x7.com/api/v1/orders \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order-8f21c9a4' \
-d '{
"product_id": "netflix_premium_1m",
"quantity": 1,
"customer_email": "[email protected]"
}'{
"order_id": 5731,
"charge": 99,
"balance_after": 1135.5,
"status": "Completed",
"credentials": "[email protected]:password123"
}{ "error": "Insufficient funds" }{ "error": "Price below supplier cost — contact support" }{ "error": "Only 2 in stock", "error_code": "out_of_stock", "available": 2 }GET /api/v1/products/:id) and, if it still fails, contact support. Products we own outright are never affected.
available tells you how many you could have taken. This is the same number as stock_count on the product and max in the SMM services list, so a client that respects those will rarely see it.
Idempotency
Send an Idempotency-Key header with every order you create. If a request times out you cannot tell whether it was received, and retrying without a key places a second order and takes a second debit. With a key, the retry returns the original order instead.
- Use a fresh, unique value per order you intend to place — a UUID is ideal. Reusing one key across genuinely different orders is a bug, and we report it rather than guess.
- A retry of a completed order returns the original response with
"idempotent_replay": trueand HTTP 200 (the first reply is 201). Sameorder_id, no extra charge. - A retry while the first is still running returns 409
idempotency_in_flight— wait and pollGET /api/v1/orders/:idrather than ordering again. - Reusing a key with a different payload returns 422
idempotency_key_reuse. - Rejected orders (insufficient funds, out of stock, bad quantity) release the key — nothing was created, so you may fix the request and retry with the same key.
- Keys are remembered for 24 hours. After that the same value starts a new order.
- Omitting the key preserves the old behaviour exactly: two identical calls create two real orders. SMM panels cannot send headers — they may pass
idempotency_keyas a form field instead.
White-label delivery
Your customers can receive their credentials under your brand, with nothing identifying us. Turn it on under White-label delivery in your dashboard — no API changes are needed, it applies automatically to every order you place.
What changes. Delivery and order-confirmation emails to your buyer carry your brand name, your support address and your site — no logo, no footer domain and no tracking link back to us.
The sender address. Without your own mail server we send through ours: the email shows your brand name, but our sending domain stays visible in the address. Add your SMTP username and password in the dashboard and the mail goes out from your own domain instead — fully unbranded.
Why we don't just forge it. Putting your address on mail sent through our server fails SPF and DKIM checks, which lands your customer's credentials in spam. Your own SMTP is the only way to get both your domain and reliable delivery.
Use Send me a sample email in the dashboard to see exactly what your buyer will receive before a real order goes out. Your own copy of each delivery is unaffected and still carries our branding and tracking link.
GET /api/v1/orders/:id
Order details. Poll this until status is Completed for manual-delivery products.
curl https://keys24x7.com/api/v1/orders/5731 \
-H 'Authorization: Bearer YOUR_API_KEY'{
"order_id": 5731,
"product": "Netflix Premium — 1 Month",
"status": "Completed",
"charge": 99,
"credentials": "[email protected]:password123",
"created_at": "2026-08-05 12:41:03"
}GET /api/v1/orders?limit=50
Your most recent orders, newest first. limit defaults to 50.
curl 'https://keys24x7.com/api/v1/orders?limit=10' \
-H 'Authorization: Bearer YOUR_API_KEY'[
{ "order_id": 5731, "product": "Netflix Premium — 1 Month", "status": "Completed", "charge": 99, "created_at": "2026-08-05 12:41:03" },
{ "order_id": 5729, "product": "Spotify Premium — 3 Months", "status": "Pending", "charge": 249, "created_at": "2026-08-05 11:02:47" }
]GET /api/v1/balance
curl https://keys24x7.com/api/v1/balance \
-H 'Authorization: Bearer YOUR_API_KEY'{ "balance": 1135.5, "currency": "INR" }GET /api/v1/me
Account details — handy as a "test connection" call.
curl https://keys24x7.com/api/v1/me \
-H 'Authorization: Bearer YOUR_API_KEY'{
"name": "Acme Digital",
"email": "[email protected]",
"status": "active",
"created_at": "2026-07-01 09:15:00"
}Keeping in sync
Prices and stock move. Three tools, cheapest first — combine them and a store of any size stays current with a handful of requests per hour.
- 1Poll
/api/v1/catalog/versionevery few minutes. One tiny JSON response; ifversionmatches what you stored, nothing has changed and you are done. - 2When it changes, pull only the delta with
?updated_since=your last sync time. - 3Subscribe to webhooks so deliveries and catalog changes are pushed to you instead of polled at all.
GET /api/v1/catalog/version
version is a short hash of the catalog's newest change plus its size. It changes whenever any product's price, stock state or availability changes.
curl https://keys24x7.com/api/v1/catalog/version \
-H 'Authorization: Bearer YOUR_API_KEY'{
"version": "9f2c1a77b0e34d51",
"product_count": 148,
"updated_at": "2026-08-05 12:41:03"
}GET /api/v1/products?updated_since=…
Only products whose price, stock or availability changed after that instant. The value is any ISO 8601 timestamp (a bad one returns 422). In this mode deactivated products are included with "active": false — that is how your store learns something was withdrawn. A plain GET /api/v1/products lists active products only.
curl 'https://keys24x7.com/api/v1/products?updated_since=2026-08-05T00:00:00Z' \
-H 'Authorization: Bearer YOUR_API_KEY'updated_at of the newest row you received (or the time you started the request) as the next updated_since. Overlapping slightly is safe — updates are idempotent.
ETag & 304 Not Modified
Every GET /api/v1/products and GET /api/v1/catalog/version response carries an ETag. Send it back as If-None-Match and an unchanged answer costs you a 304 with an empty body instead of the whole catalog.
# first call — note the ETag header
curl -i https://keys24x7.com/api/v1/products \
-H 'Authorization: Bearer YOUR_API_KEY'
# → ETag: "3d9a…"
# next call — 304, nothing transferred
curl -i https://keys24x7.com/api/v1/products \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'If-None-Match: "3d9a…"'
# → HTTP/1.1 304 Not ModifiedWebhooks
Register an HTTPS endpoint and we push events to it — no polling, no delay. This is what makes automatic delivery on your own store possible: the moment credentials exist, they are on your server.
POST /api/v1/webhooks
Returns the signing secret once — store it immediately, it is never shown again. Omit events to subscribe to all of them. Up to 10 endpoints per account.
curl -X POST https://keys24x7.com/api/v1/webhooks \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://yourstore.example/hooks/keys",
"events": ["order.delivered", "order.failed", "catalog.updated"]
}'{
"id": 3,
"url": "https://yourstore.example/hooks/keys",
"events": ["order.delivered", "order.failed", "catalog.updated"],
"active": true,
"created_at": "2026-08-05 13:20:11",
"secret": "6f1b…"
}GET /api/v1/webhooks lists yours (without secrets), GET /api/v1/webhooks/:id fetches one, DELETE /api/v1/webhooks/:id removes it (204).
What we send & how to verify it
POST /hooks/keys HTTP/1.1
Content-Type: application/json
X-Keys-Event: order.delivered
X-Keys-Delivery: 918
X-Keys-Timestamp: 1785935112
X-Keys-Signature: sha256=4b0f8c…
{"event":"order.delivered","created_at":"2026-08-05T13:25:12.004Z","data":{ … }}The signature is an HMAC-SHA256 of the raw request body keyed with your webhook secret. Always verify it before trusting a payload, and always compare in constant time.
import crypto from 'crypto';
// IMPORTANT: use the RAW body, not a re-serialised object.
app.post('/hooks/keys', express.raw({ type: 'application/json' }), (req, res) => {
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.KEYS_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
const got = String(req.headers['x-keys-signature'] || '');
const ok = got.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
if (!ok) return res.status(401).end();
const { event, data } = JSON.parse(req.body.toString('utf8'));
// … deliver to your buyer, update your catalog …
res.status(200).end(); // respond 2xx quickly; do the work after
});$raw = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $raw, getenv('KEYS_WEBHOOK_SECRET'));
if (!hash_equals($expected, $_SERVER['HTTP_X_KEYS_SIGNATURE'] ?? '')) {
http_response_code(401); exit;
}
$payload = json_decode($raw, true);Events
| Event | Fired when | data |
|---|---|---|
order.delivered | Credentials are attached to one of your orders | order_id, reference, product, charge, status:"Completed", credentials |
order.failed | One of your orders was moved to manual review | order_id, reference, product, charge, status:"manual_review", reason, message |
catalog.updated | A catalog import or auto-sync changed prices/stock | version, product_count, updated_at, source |
2xx counts as accepted. A non-2xx reply, a connection error or a response slower than 10 seconds is retried 3 times with 1 s → 5 s → 15 s backoff, then dropped. Reply 200 first and process afterwards. Retries mean the same event can arrive twice — key your handling on X-Keys-Delivery or the order reference and make it idempotent. On catalog.updated, treat the payload as a hint: re-pull with updated_since rather than trusting the counts.
Rate limits
240 requests per minute per API key, counted across /api/v1 and /api/v2 together in a fixed 60-second window. That is far more than a healthy integration needs — polling catalog/version every 10 seconds plus normal order traffic uses a fraction of it.
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in the window |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | Unix time when the window resets |
Retry-After | Seconds to wait (sent with 429 only) |
Over the limit, v1 answers 429 {"error":"Rate limit exceeded"}; v2 answers the same {"error":…} body with HTTP 200, as the SMM convention requires. Back off and retry — nothing is charged or created. If you genuinely need a higher ceiling, ask support.
Connect from an SMM Panel
Keys24x7 speaks the standard SMM provider protocol, so any panel software (Perfect Panel, SmartPanel, rental panels, custom scripts) can plug us in as a provider:
- 1In your panel's admin area open Providers (sometimes called API Providers or Services → Providers).
- 2Click Add provider.
- 3API URL:
https://keys24x7.com/api/v2 - 4API key: paste the key from your reseller dashboard.
- 5Save, then import services — our catalog appears with INR rates. Set your own markup per service.
- 6Map order delivery: when an order completes, fetch
action=status— thecredentialsfield contains what your buyer receives (also emailed automatically when you passcustomer_emailonadd).
Integrate with an AI Website Builder
Building a storefront with Lovable, v0, Bolt or a similar AI builder? Paste the prompt below, replace YOUR_API_KEY, and the builder will wire a complete storefront against our REST API — with the key safely kept server-side.
You are building a digital-goods storefront that resells products from the
Keys24x7 Reseller API.
API BASE URL: https://keys24x7.com/api/v1
API KEY: YOUR_API_KEY
AUTH: every request to the Keys24x7 API needs the header
Authorization: Bearer <API KEY>
SECURITY (most important requirement):
Create server-side API routes (for example /api/licenses/*) that forward
requests to the Keys24x7 API and inject the Authorization header from an
environment variable named RESELLER_API_KEY. The browser must only ever call
these proxy routes. Never place the key in client-side code, bundles, or
public config — if the framework only supports client code, generate a small
serverless/edge function for the proxy.
BUILD THIS:
1. Catalog page
- GET {base}/products returns an array of:
{ id, api_service_id, name, category, description, price_inr,
price_usdt, image_url, delivery_type, eta_minutes, in_stock,
stock_count }
- Render a responsive product grid grouped by category with name, image,
description, delivery ETA and an "In stock" badge.
- Show a selling price of price_inr with my margin applied (make the
margin a single constant I can edit, default 20%).
- Grey out / disable buying when in_stock is false.
2. Checkout
- Collect the buyer's email address, then POST {base}/orders with JSON:
{ "product_id": "<id>", "quantity": 1, "customer_email": "<buyer email>" }
- A 201 response returns { order_id, charge, balance_after, status,
credentials? }. If credentials is present, the item was delivered
instantly — show it on a success page with a copy button.
- Error handling: 402 means my reseller wallet is out of funds — show the
buyer "temporarily unavailable, try again soon" (never mention the
wallet); 404 unknown product; 422 validation error; show friendly toasts.
3. Order status page
- For orders that are not yet Completed, poll GET {base}/orders/{order_id}
every 15 seconds. The response is { order_id, product, status, charge,
credentials?, created_at }.
- While status is "Pending" or "In progress" show a friendly "your order
is being prepared" state with the ETA. When it becomes "Completed",
stop polling and reveal the credentials with a copy button.
- Give the buyer a link they can bookmark to come back to this page.
4. Utilities (server-side only)
- GET {base}/balance returns { balance, currency } — log a server-side
warning when the balance drops below 500 so I know to top up.
- GET {base}/me verifies the key works; use it in a health-check route.
GENERAL: mobile-first responsive design, clear loading and empty states,
no exposure of wholesale price_inr or my wallet anywhere in the UI.WordPress Plugin
Already running WooCommerce? The Keys24x7 Reseller plugin imports our catalog as WooCommerce products with your margin, keeps prices and stock in sync hourly, and fulfills paid orders automatically — credentials land in the buyer's completion email.
Install & set up
- 1Requirements: WordPress with WooCommerce active, PHP 7.4+.
- 2WP Admin → Plugins → Add New → Upload Plugin → choose
watshop-reseller.zip→ Install Now. - 3Click Activate.
- 4Open Settings → Keys24x7 Reseller and enter the API URL
https://keys24x7.com/api/v1and your API key. Set your price margin % and (optionally) enable hourly auto-sync. - 5Click Test connection — it calls
/api/v1/meand shows your account plus wallet balance. - 6Click Import now — the catalog is created as virtual WooCommerce products (SKU = product id, price = INR price × (1 + margin)), with categories and images mapped.
How selling works
- 1A customer pays for an imported product in your WooCommerce store.
- 2The plugin places the order on Keys24x7 with your buyer's email and stores the Keys24x7 order id on the WooCommerce order.
- 3A cron job polls the order until it is
Completed, then saves the credentials to the order, adds an order note and sends them to the buyer in the completion email. - 4If the order fails (for example your wallet hits
402), the WooCommerce order stays in processing with a note, you get an admin email, and a Retry button appears in the order's Keys24x7 Reseller meta box.
Keys24x7 Reseller API · Portal login · Create account