SMM Panel API Integration: Code Examples for 2026

Mountain GoatTác giả thuộc ban biên tập 9SMM.COMPublished 8 min read

SMM panel API integration is a smaller job than most people expect: one endpoint, one key, seven actions and no OAuth dance. This guide takes you from an empty file to a working integration — service sync, order placement, batched status polling, refills and error handling — with runnable examples in PHP, Python and Node.js against the 9SMM API v2.

What the API gives you

Everything the dashboard does, minus the clicking:

ActionWhat it does
servicesReturns the live catalogue with IDs, rates, min/max and capability flags
addPlaces an order and deducts the charge immediately
statusReturns charge, start count, status and remains — one order or up to 100
refillRequests a refill on eligible orders
refill_statusChecks a refill request
cancelRequests cancellation on up to 100 orders
balanceReturns your USD balance

That is the whole surface. If you have integrated any panel before, the shape will be familiar — this interface is close to an industry convention, which is also why switching suppliers is mostly a matter of changing a base URL and a key.

Before you write code

  1. Get a key. It lives in your panel account and starts with smm_. Each account has one active key; regenerating it invalidates the old one instantly.
  2. Fund the balance. add fails with Not enough funds on balance rather than queueing. See payment methods.
  3. Know the limit. 1,200 requests per rolling minute per key. Over that you get HTTP 429 with {"error": "Rate limit exceeded"} — the standard 429 semantics apply, so back off rather than retry in a tight loop.
  4. Keep the key server-side. Never in browser JavaScript, never in a mobile binary. A leaked key spends your balance.

Endpoint and authentication

POST https://9smm.com/api/v2
Content-Type: application/x-www-form-urlencoded

Authentication is a key parameter in the body — no header, no token exchange. Every request carries key and action. Rates, charges and balances are USD throughout.

One thing to internalise before anything else: most errors return HTTP 200 with an error field in the body. A 200 is not success. Check for error on every single response.

Step 1: Pull the service list

Start here, because every later call needs a service ID.

import requests

API = "https://9smm.com/api/v2"
KEY = "smm_your_key_here"

def call(payload):
    r = requests.post(API, data={"key": KEY, **payload}, timeout=30)
    r.raise_for_status()
    data = r.json()
    if isinstance(data, dict) and "error" in data:
        raise RuntimeError(data["error"])
    return data

services = call({"action": "services"})
for s in services[:3]:
    print(s["service"], s["name"], s["rate"], s["min"], s["max"], s["refill"])

Each entry looks like this:

{
  "service": 1,
  "name": "Instagram Followers",
  "type": "Default",
  "category": "Instagram — Followers",
  "rate": "0.90",
  "min": 10,
  "max": 100000,
  "refill": true,
  "cancel": true,
  "dripfeed": true
}

Cache it and refresh on a schedule — hourly is plenty. Do not call services before every order; that is how people hit the rate limit on day one. Note that rate is a string: parse it as a decimal rather than a float if you bill customers from it.

The charge for any order is rate × quantity ÷ 1,000, so you can compute and display the cost before submitting.

Step 2: Place an order

PHP, with cURL:

<?php
function smm(array $params) {
    $params['key'] = 'smm_your_key_here';
    $ch = curl_init('https://9smm.com/api/v2');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => http_build_query($params),
        CURLOPT_TIMEOUT        => 30,
    ]);
    $raw = curl_exec($ch);
    curl_close($ch);
    $data = json_decode($raw, true);
    if (isset($data['error'])) {
        throw new RuntimeException($data['error']);
    }
    return $data;
}

$order = smm([
    'action'   => 'add',
    'service'  => 1,
    'link'     => 'https://www.instagram.com/your_profile/',
    'quantity' => 1000,
]);

echo $order['order']; // 23501

Node.js, the same call:

const API = "https://9smm.com/api/v2";
const KEY = "smm_your_key_here";

async function smm(params) {
  const res = await fetch(API, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({ key: KEY, ...params }),
  });
  const data = await res.json();
  if (data && data.error) throw new Error(data.error);
  return data;
}

const { order } = await smm({
  action: "add",
  service: "1",
  link: "https://www.instagram.com/your_profile/",
  quantity: "1000",
});

The response is {"order": 23501}. Store that ID against your own customer reference immediately — the panel knows orders, not your customers.

Parameters for add:

ParameterRequiredNotes
serviceyesService ID
linkyesPublic profile, post or video URL
quantityyesWithin min/max. Omit for Custom Comments
commentsnoCustom Comments only, one per line, under 500 characters each
runsnoDrip-feed, 2 to 100
intervalnoDrip-feed, 0 to 1,440 minutes

Step 3: Drip-feed

Only on services where dripfeed is true. quantity is the total across all runs, and each individual run (quantity ÷ runs) must still fall within the service's min and max — that constraint causes most Incorrect quantity errors.

action=add&service=1&link=https://www.instagram.com/your_profile/
&quantity=5000&runs=5&interval=60

Five runs of 1,000, an hour apart. Why bother is covered in drip-feed and refill explained.

Step 4: Poll status in batches

The mistake here is one request per order. Use orders with up to 100 comma-separated IDs:

ids = ",".join(str(i) for i in order_ids[:100])
result = requests.post(API, data={
    "key": KEY, "action": "status", "orders": ids
}, timeout=30).json()

for oid, info in result.items():
    if "error" in info:
        log.warning("order %s: %s", oid, info["error"])
        continue
    print(oid, info["status"], info["remains"], info["charge"])

A multi-status response is keyed by order ID, and a bad ID returns its own error entry without breaking the rest:

{
  "23501": {"charge": "0.90", "start_count": 3572, "status": "Completed", "remains": 0, "currency": "USD"},
  "99999": {"error": "Incorrect order ID"}
}

Statuses are Pending, In progress, Processing, Completed, Partial and Canceled. The first three are non-terminal; stop polling an order once it reaches one of the last three. Before delivery starts, remains equals the ordered quantity and start_count is 0 — that is normal, not a stalled order. SMM order status explains each state in plain language.

Poll on a sane schedule: every few minutes for active orders, never in a loop with no sleep.

Step 5: Refill and cancel

action=refill&orders=23501,23510

Returns an array, one entry per order:

[{"order": 23501, "refill": 23501},
 {"order": 23510, "error": "Refill not available"}]

The refill ID equals the order ID. Check progress with action=refill_status&refill=23501, which returns Pending, In progress, Completed or Rejected.

Refill requires refill: true on the service, a Completed or Partial order, no previous refill request, and the order to be inside the refill window counted from the order date. Track that deadline in your own system — the API will not remind you.

cancel takes orders (up to 100), works on services with cancel: true while the order is Pending, In progress or Processing, and returns {"order": 23501, "cancel": 1} on acceptance. cancel: 1 means recorded, not finished — poll status for the outcome.

Step 6: Handle errors like you mean it

Match on the exact strings. All return HTTP 200 except the rate limit:

ErrorUsual cause
Invalid API keyKey wrong, regenerated, or account inactive
Rate limit exceededOver 1,200 per minute (HTTP 429)
Incorrect service IDUnavailable service, or drip-feed on a service without it
Incorrect linkEmpty link, or a duplicate active order on that service and link
Incorrect quantityOutside min/max, bad run size, or interval outside 0 to 1,440
Not enough funds on balanceTop up
Incorrect order IDNot yours, or not numeric
Too many orders (max 100)Chunk your batch
Refill not available / Cancel not availableConditions not met
Internal errorRetry with backoff, then contact support

The one that bites hardest in production: if add times out, do not resend blindly. Check status or the Orders page first. A resend can create a duplicate paid order.

Production checklist

Before you call an SMM panel API integration finished:

  • Key in a secret store, never in the repository.
  • Service catalogue cached and refreshed on a schedule.
  • Every response checked for error before use.
  • Order IDs persisted against your own references the moment add returns.
  • Status polling batched, scheduled, and stopped at terminal states.
  • Refill deadlines tracked locally.
  • Balance monitored with an alert threshold, because add fails rather than queues.
  • Exponential backoff on 429 and Internal error.

FAQ

Is the API the same across SMM panels? Broadly yes — most follow the same action names and parameters, which is why swapping providers is usually a config change. Verify the error strings and limits, which do vary. See SMM API provider.

Do API orders cost more than dashboard orders? No. Same rates, same balance.

Can I call the API from the browser? No. The key authenticates full account access, including spending. Server-side only.

How many orders can I check in one call? Up to 100 IDs via orders, on status, refill and cancel.

Do I need an API key to start? Create a free account, fund the balance, and the key is in your dashboard. Full reference at /smm-api.

Ready to grow your social media?

Create a free 9SMM.COM account, add funds and place your first order from the service list.

Create free account

Keep reading

SMM Panel API Integration: Code Examples for 2026