If you run a reseller site, an agency dashboard or a small internal tool, sooner or later you will want to place and track social media orders from code instead of a browser. Most SMM panels expose a small HTTP API for this. This article walks through one such API end to end: raw HTTP first, then PHP, Python and Node.js clients, then error handling, status polling and a simple reseller sync job.
The examples use the WowSMM API v2, but the pattern is close to what many panels use, so most of it transfers.
How the API is shaped
There is one endpoint:
POST https://wowsmm.com/api/v2 Every request is a POST with form parameters. Two parameters are always present: key (your API key) and action. The response is JSON.
The actions are:
| action | extra parameters | purpose |
|---|---|---|
services | none | list services with IDs, rates and limits |
add | service, link, quantity | place an order |
status | order or orders (comma separated) | check one or many orders |
refill | order | request a refill on an eligible order |
cancel | orders | request cancellation |
balance | none | check wallet balance |
Full parameter details are in the WowSMM API documentation. Keep your key in an environment variable, never in a repository.
Raw requests with curl
Testing with curl first makes it obvious what the client libraries do under the hood.
export WOWSMM_KEY="your_api_key" curl -s -X POST https://wowsmm.com/api/v2 \ -d key="$WOWSMM_KEY" \ -d action=balance curl -s -X POST https://wowsmm.com/api/v2 \ -d key="$WOWSMM_KEY" \ -d action=services curl -s -X POST https://wowsmm.com/api/v2 \ -d key="$WOWSMM_KEY" \ -d action=add \ -d service=1 \ -d link="https://www.instagram.com/example_public_profile/" \ -d quantity=100 curl -s -X POST https://wowsmm.com/api/v2 \ -d key="$WOWSMM_KEY" \ -d action=status \ -d orders="1001,1002,1003" Replace the service ID with a real one from the services response. The link must be public; the API never needs an account password.
The raw HTTP request looks like this:
POST /api/v2 HTTP/1.1 Host: wowsmm.com Content-Type: application/x-www-form-urlencoded key=your_api_key&action=status&order=1001 Using the open-source clients
The wowsmm-api repository on GitHub contains small clients for PHP, Python and Node.js. Each takes your API key in the constructor and exposes services(), order(service, link, quantity), status(id), refill(id), cancel(ids) and balance(). PHP also has multiStatus(ids) and Python has multi_status(ids) for checking many orders in one request.
PHP
<?php require __DIR__ . '/wowsmm-api/php/WowSMM.php'; $api = new WowSMM(getenv('WOWSMM_KEY')); $balance = $api->balance(); echo "Balance: {$balance['balance']} {$balance['currency']}\n"; $order = $api->order(1, 'https://www.instagram.com/example_public_profile/', 100); if (isset($order['error'])) { fwrite(STDERR, "Order failed: {$order['error']}\n"); exit(1); } echo "Order ID: {$order['order']}\n"; $status = $api->status($order['order']); echo "Status: {$status['status']}\n"; Python
import os from wowsmm import WowSMM api = WowSMM(os.environ["WOWSMM_KEY"]) print(api.balance()) order = api.order(1, "https://www.instagram.com/example_public_profile/", 100) if "error" in order: raise SystemExit(f"Order failed: {order['error']}") order_id = order["order"] print("Order ID:", order_id) print(api.status(order_id)) Node.js
The Node client uses the built-in fetch, so it needs Node 18 or newer. Every method returns a promise.
const WowSMM = require("./node/wowsmm"); const api = new WowSMM(process.env.WOWSMM_KEY); async function main() { const balance = await api.balance(); console.log("Balance:", balance); const order = await api.order(1, "https://www.instagram.com/example_public_profile/", 100); if (order.error) { throw new Error(`Order failed: ${order.error}`); } console.log("Order ID:", order.order); console.log(await api.status(order.order)); } main().catch((err) => { console.error(err.message); process.exit(1); }); Error handling
When something is wrong, the API returns JSON with an error key instead of the normal fields. Typical causes are an invalid key, an unknown service ID, a quantity outside the service's minimum or maximum, a link that is not public, or a balance too low for the order.
Two layers are worth handling separately:
- Transport errors: timeouts, DNS failures, non-200 responses, invalid JSON. Retry these with backoff.
- API errors: a valid JSON body with
error. Do not blindly retry these. Log them and show the message to whoever placed the order.
A small wrapper in Python:
import time from wowsmm import WowSMM class ApiError(Exception): pass def call(fn, *args, retries=3, delay=2): for attempt in range(retries): try: result = fn(*args) except Exception: if attempt == retries - 1: raise time.sleep(delay * (attempt + 1)) continue if isinstance(result, dict) and "error" in result: raise ApiError(result["error"]) return result Never retry add automatically after a timeout without checking first. The request may have reached the server, and a retry can create a duplicate order. Store the request locally before sending it, and reconcile by checking your recent orders before trying again.
Polling order status
Orders move through states such as pending, in progress, partial, completed and canceled. Polling every few minutes is enough; polling every second only wastes requests.
const WowSMM = require("./node/wowsmm"); const api = new WowSMM(process.env.WOWSMM_KEY); const FINAL = new Set(["Completed", "Partial", "Canceled"]); const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); async function waitForOrder(id, intervalMs = 5 * 60 * 1000) { for (;;) { const s = await api.status(id); if (s.error) throw new Error(s.error); console.log(id, s.status, "remains:", s.remains); if (FINAL.has(s.status)) return s; await sleep(intervalMs); } } Check the exact status strings in the docs and compare case-insensitively if you want to be safe.
Checking many orders at once
If you track dozens or hundreds of orders, do not call status once per order. Use the multi-status call (multiStatus in PHP, multi_status in Python), which sends the orders parameter as a comma-separated list and returns a map keyed by order ID.
<?php require __DIR__ . '/wowsmm-api/php/WowSMM.php'; $api = new WowSMM(getenv('WOWSMM_KEY')); $ids = [1001, 1002, 1003]; $result = $api->multiStatus($ids); foreach ($result as $id => $info) { if (isset($info['error'])) { echo "$id: {$info['error']}\n"; continue; } echo "$id: {$info['status']} (remains {$info['remains']})\n"; } Batch in groups of a reasonable size rather than sending thousands of IDs in one request.
A simple reseller sync job
If you resell services on your own site, three scheduled jobs cover most of the work.
Service sync, daily. Call services(), compare against your local table, update rates, minimums and maximums, and disable services that disappeared. Apply your markup here so your storefront prices always follow your supplier.
Order sync, every few minutes. Select local orders that are not in a final state, group their supplier IDs into batches, call multiStatus, and update each local order. When an order ends as partial, use the remains value to refund your own customer for the undelivered part, since the supplier refunds that quantity to your wallet.
Balance check, hourly. Call balance() and alert yourself when it falls below a threshold.
A compact Python version of the order sync:
import os from wowsmm import WowSMM api = WowSMM(os.environ["WOWSMM_KEY"]) FINAL = {"completed", "partial", "canceled"} def sync_orders(db, batch_size=100): open_orders = db.fetch_open_orders() for i in range(0, len(open_orders), batch_size): batch = open_orders[i:i + batch_size] ids = [o["supplier_id"] for o in batch] result = api.multi_status(ids) if "error" in result: print("Batch failed:", result["error"]) continue for o in batch: info = result.get(str(o["supplier_id"])) if not info or "error" in info: continue db.update_order(o["id"], info["status"], info.get("remains")) if info["status"].lower() == "partial": db.refund_remains(o["id"], info.get("remains")) The db object stands in for your own data layer.
Wrapping up
The API surface is small: six actions, one endpoint, JSON responses. Most of the real work is in the parts around it: safe retries, batching status checks and keeping your own records in step with the supplier.
If you are building a storefront on top of this, the SMM reseller panel overview explains the reseller side, and the WowSMM homepage lists the platforms covered. Questions about the API can go to WowSMM support on Telegram at https://t.me/wow_smm_com.
