If you run a reseller website, a Telegram bot or a small agency tool, sooner or later you want orders to go to your SMM panel automatically instead of placing them by hand. Most SMM panels use the same simple "API v2" format, so once you understand it, you can connect to almost any panel in an afternoon.
In this post I will show the full flow with working PHP and Python code: list services, check balance, place an order and track its status.
Disclosure: I run SafeSMM, so the examples use the SafeSMM endpoint. The request format is the common v2 style, so the same code usually works with other panels after you change the URL and the key.
How an SMM panel API v2 works
Everything goes through one URL with a POST request. You always send two fields:
-
key: your API key (from your account page) -
action: what you want to do
The response is JSON. The common actions are:
| Action | What it does | Extra parameters |
|---|---|---|
services |
List all services | none |
balance |
Show your balance | none |
add |
Place an order |
service, link, quantity
|
status |
Order status |
order (or orders for many) |
refill |
Ask for a refill | order |
cancel |
Ask to cancel | order |
Step 1: test with cURL
Before writing any code, test your key from the terminal:
curl -X POST https://safesmm.net/api/v2 \
-d "key=YOUR_API_KEY" \
-d "action=balance"
A working key returns something like:
{ "status": "success", "balance": "100.84292", "currency": "USD" }
A wrong key returns:
{ "error": "Invalid API key." }
Step 2: a small Python client
This uses only the Python standard library, so there is nothing to install:
import json
import urllib.parse
import urllib.request
API_URL = "https://safesmm.net/api/v2"
API_KEY = "YOUR_API_KEY"
def call(action, **params):
data = {"key": API_KEY, "action": action, **params}
body = urllib.parse.urlencode(data).encode()
req = urllib.request.Request(API_URL, data=body, method="POST")
with urllib.request.urlopen(req, timeout=30) as resp:
result = json.loads(resp.read().decode())
if isinstance(result, dict) and "error" in result:
raise RuntimeError(result["error"])
return result
print(call("balance"))
services = call("services")
for s in services[:5]:
print(s["service"], s["name"], s["rate"], s["min"], s["max"])
order = call("add", service=1, link="https://instagram.com/yourpage", quantity=100)
print(call("status", order=order["order"]))
Step 3: the same thing in PHP
<?php
function smm_call($action, $params = []) {
$data = array_merge(['key' => 'YOUR_API_KEY', 'action' => $action], $params);
$ch = curl_init('https://safesmm.net/api/v2');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
if (is_array($result) && isset($result['error'])) {
throw new Exception($result['error']);
}
return $result;
}
print_r(smm_call('balance'));
$order = smm_call('add', ['service' => 1, 'link' => 'https://instagram.com/yourpage', 'quantity' => 100]);
print_r(smm_call('status', ['order' => $order['order']]));
Order status values
When you call status, you get fields like status, start_count, remains and charge. The usual status values are:
- Pending: the order is in the queue
- In progress: delivery has started
- Completed: fully delivered
- Partial: only part was delivered, and the rest is refunded to your balance
- Canceled: not delivered, and the amount is refunded
I wrote a longer guide on what each status means here: https://safesmm.net/blog/smm-panel-order-status-meanings
Drip-feed and custom comments
Two special order types use the same add action:
-
Drip-feed: send
quantity(per run),runsandinterval(minutes between runs) to spread delivery over time. -
Custom comments: send
commentswith one comment per line instead ofquantity.
Tips from running a panel
-
Cache the services list. Call
servicesonce and store it. Service IDs, rates, min and max all come from there, and you do not need to fetch it on every order. -
Check status every few minutes, not every second. Use
orderswith comma-separated IDs to check many orders in one request. - Validate the link before you send it. A private profile or a wrong link is the most common reason for a stuck or canceled order.
- Keep the key on the server. Never put your API key in front-end JavaScript or a public repo.
-
Handle the
errorfield everywhere. Both examples above turn it into an exception so one bad order does not break your whole script.
Full code
The complete PHP and Python clients (with refill, cancel, drip-feed, comments and multi-status) are on GitHub:
https://github.com/safesmmnet/safesmm-api-examples
The full parameter list is in the API docs: https://safesmm.net/api/docs
If you have questions about connecting a panel API to your own site or bot, ask in the comments and I will help.
Top comments (0)