Overview
Endpoint
https://9smm.com/api/v2Method
application/x-www-form-urlencoded or JSONAuthentication
key parameter in the request bodyResponse
{"error": "..."}Rate limit
Currency
Every request goes to the same URL with an action parameter that says what to do. Parameter names, response fields and status strings follow the v2 format used across SMM panels, so an existing reseller script normally works after you change the API URL and key.
Quick start
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=balanceGet an API key
- Create an account and add funds to your balance.
- Open Profile and click Generate API key in the API access card.
- Copy the key straight away. It starts with
smm_and is shown in full only once.
Each account has one active key. Regenerate key issues a new one and the old key stops working immediately, which is what to do if a key leaks. Orders placed through the API appear on your Orders page like any other order.
Code samples: PHP, Node.js, Python
Complete scripts that check your balance, count services, place an order and read its status. They use only each language's standard library. Set SMM_API_KEY in your environment (or paste the key into the file), and change the service ID and link before running, because the add step places a real order.
smm_api.php
<?php
// SMM panel API v2 client — PHP 7.4+ with the cURL extension, no dependencies.
const API_URL = 'https://9smm.com/api/v2';
$apiKey = getenv('SMM_API_KEY') ?: 'YOUR_API_KEY';
function smm_api(string $apiKey, array $params)
{
$ch = curl_init(API_URL);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query(['key' => $apiKey] + $params),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
if ($body === false) {
$err = curl_error($ch);
curl_close($ch);
throw new RuntimeException('Network error: ' . $err);
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($body, true);
if (!is_array($data)) {
throw new RuntimeException("Unexpected response (HTTP $httpCode)");
}
// Errors arrive as HTTP 200 with {"error": "..."}; only the rate limit uses HTTP 429.
if (isset($data['error'])) {
throw new RuntimeException($data['error']);
}
return $data;
}
try {
$balance = smm_api($apiKey, ['action' => 'balance']);
echo "Balance: {$balance['balance']} {$balance['currency']}\n";
$services = smm_api($apiKey, ['action' => 'services']);
echo 'Services available: ' . count($services) . "\n";
// Replace the service ID and link with your own: this places a real order
// and charges your balance.
$order = smm_api($apiKey, [
'action' => 'add',
'service' => 1,
'link' => 'https://www.instagram.com/your_profile/',
'quantity' => 1000,
]);
echo "Order placed: {$order['order']}\n";
$status = smm_api($apiKey, ['action' => 'status', 'order' => $order['order']]);
echo "Status: {$status['status']}, remains: {$status['remains']}, charge: {$status['charge']}\n";
} catch (RuntimeException $e) {
echo 'API error: ' . $e->getMessage() . "\n";
exit(1);
}
Run: SMM_API_KEY=smm_... php smm_api.php
smm-api.mjs
// SMM panel API v2 client — Node.js 18+ (built-in fetch), no dependencies.
const API_URL = "https://9smm.com/api/v2";
const API_KEY = process.env.SMM_API_KEY || "YOUR_API_KEY";
async function smmApi(params) {
const res = await fetch(API_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ key: API_KEY, ...params }),
});
const data = await res.json();
// Errors arrive as HTTP 200 with {"error": "..."}; only the rate limit uses HTTP 429.
if (data && !Array.isArray(data) && data.error) {
throw new Error(res.status === 429 ? `${data.error} — slow down and retry` : data.error);
}
return data;
}
async function main() {
const balance = await smmApi({ action: "balance" });
console.log(`Balance: ${balance.balance} ${balance.currency}`);
const services = await smmApi({ action: "services" });
console.log(`Services available: ${services.length}`);
// Replace the service ID and link with your own: this places a real order
// and charges your balance.
const { order } = await smmApi({
action: "add",
service: "1",
link: "https://www.instagram.com/your_profile/",
quantity: "1000",
});
console.log(`Order placed: ${order}`);
const status = await smmApi({ action: "status", order: String(order) });
console.log(`Status: ${status.status}, remains: ${status.remains}, charge: ${status.charge}`);
}
main().catch((err) => {
console.error(`API error: ${err.message}`);
process.exit(1);
});
Run: SMM_API_KEY=smm_... node smm-api.mjs
smm_api.py
"""SMM panel API v2 client — Python 3.8+, standard library only."""
import json
import os
import sys
import urllib.error
import urllib.parse
import urllib.request
API_URL = "https://9smm.com/api/v2"
API_KEY = os.environ.get("SMM_API_KEY", "YOUR_API_KEY")
class SmmApiError(Exception):
pass
def smm_api(action, **params):
body = urllib.parse.urlencode({"key": API_KEY, "action": action, **params}).encode()
req = urllib.request.Request(API_URL, data=body, headers={"User-Agent": "smm-api-client/1.0"})
try:
with urllib.request.urlopen(req, timeout=30) as res:
data = json.load(res)
except urllib.error.HTTPError as e:
# Only the rate limit uses a real HTTP error code (429).
if e.code == 429:
raise SmmApiError("Rate limit exceeded - slow down and retry") from e
raise
# Every other error arrives as HTTP 200 with {"error": "..."}.
if isinstance(data, dict) and "error" in data:
raise SmmApiError(data["error"])
return data
def main():
balance = smm_api("balance")
print(f"Balance: {balance['balance']} {balance['currency']}")
services = smm_api("services")
print(f"Services available: {len(services)}")
# Replace the service ID and link with your own: this places a real order
# and charges your balance.
order = smm_api("add", service=1, link="https://www.instagram.com/your_profile/", quantity=1000)
print(f"Order placed: {order['order']}")
status = smm_api("status", order=order["order"])
print(f"Status: {status['status']}, remains: {status['remains']}, charge: {status['charge']}")
if __name__ == "__main__":
try:
main()
except SmmApiError as e:
print(f"API error: {e}")
sys.exit(1)
Run: SMM_API_KEY=smm_... python smm_api.py
Actions
services
List every active service with its current rate and limits.
| Parameter | Required | Description |
|---|---|---|
| key | Yes | Your API key (starts with smm_). |
| action | Yes | services |
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=servicesResponse
[
{
"service": 1,
"name": "Instagram Followers",
"type": "Default",
"category": "Instagram — Followers",
"rate": "0.90",
"min": 10,
"max": 100000,
"refill": true,
"cancel": true,
"dripfeed": true
}
]- rate is the price in USD per 1,000 units. The order charge is rate × quantity ÷ 1,000.
- category combines platform and category ("Instagram — Followers") so identical category names on different platforms stay distinguishable.
- type is one of Default, Custom Comments, Subscriptions or Drip-feed.
- refill, cancel and dripfeed tell you which follow-up actions the service accepts.
add
Place a new order. The charge is taken from your balance immediately.
| Parameter | Required | Description |
|---|---|---|
| key | Yes | Your API key (starts with smm_). |
| action | Yes | add |
| service | Yes | Service ID from the services list. |
| link | Yes | Link to the profile, post or video. |
| quantity | Yes | Amount to deliver, within the service's min and max. Not needed for Custom Comments. |
| comments | No | Custom Comments services only: one comment per line (separated by \n). Quantity is the number of lines; each comment must be under 500 characters. |
| runs | No | Drip-feed: split the order into 2–100 runs. Quantity is the total across all runs. |
| interval | No | Drip-feed: minutes between runs, 0–1440. |
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=add \
-d service=1 \
--data-urlencode "link=https://www.instagram.com/your_profile/" \
-d quantity=1000Response
{ "order": 23501 }Drip-feed: 5,000 in 5 runs, one hour apart
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=add \
-d service=1 \
--data-urlencode "link=https://www.instagram.com/your_profile/" \
-d quantity=5000 \
-d runs=5 \
-d interval=60Response
{ "order": 23502 }- The returned order ID is the same number you see on the Orders page, and it is what status, refill and cancel expect.
- Drip-feed only works on services with dripfeed: true, and each run (quantity ÷ runs) must itself fall within the service's min and max.
- You can't have two active orders for the same link on the same service. The second one is rejected with Incorrect link until the first finishes.
status
Check one order, or up to 100 orders in a single request.
| Parameter | Required | Description |
|---|---|---|
| key | Yes | Your API key (starts with smm_). |
| action | Yes | status |
| order | No | One order ID. |
| orders | No | Comma-separated order IDs, max 100. Use instead of order. |
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=status \
-d order=23501Response
{
"charge": "0.90",
"start_count": 3572,
"status": "In progress",
"remains": 157,
"currency": "USD"
}Several orders at once
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=status \
-d orders=23501,23502,99999Response
{
"23501": { "charge": "0.90", "start_count": 3572, "status": "Completed", "remains": 0, "currency": "USD" },
"23502": { "charge": "4.50", "start_count": 0, "status": "Pending", "remains": 5000, "currency": "USD" },
"99999": { "error": "Incorrect order ID" }
}- status is exactly one of: Pending, In progress, Processing, Completed, Partial, Canceled.
- Until delivery starts, remains equals the ordered quantity and start_count is 0.
- In a multi-order request an unknown ID gets its own error entry; the other orders are still returned.
refill
Request a refill for a completed or partial order.
| Parameter | Required | Description |
|---|---|---|
| key | Yes | Your API key (starts with smm_). |
| action | Yes | refill |
| order | No | One order ID. |
| orders | No | Comma-separated order IDs, max 100. Use instead of order. |
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=refill \
-d order=23501Response
{ "refill": "23501" }Several orders at once
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=refill \
-d orders=23501,23510Response
[
{ "order": 23501, "refill": 23501 },
{ "order": 23510, "error": "Refill not available" }
]- A refill is accepted when the service has refill: true, the order is Completed or Partial, it is still inside the service's refill period (counted from the order date), and no refill has been requested for it before.
- The refill ID is the order ID. Pass it to refill_status.
refill_status
Check the progress of a refill you requested.
| Parameter | Required | Description |
|---|---|---|
| key | Yes | Your API key (starts with smm_). |
| action | Yes | refill_status |
| refill | Yes | Refill ID returned by refill (same as the order ID). |
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=refill_status \
-d refill=23501Response
{ "status": "In progress" }- status is one of: Pending, In progress, Completed, Rejected.
cancel
Request cancellation of up to 100 orders.
| Parameter | Required | Description |
|---|---|---|
| key | Yes | Your API key (starts with smm_). |
| action | Yes | cancel |
| orders | Yes | Comma-separated order IDs, max 100. A single order= is accepted too. |
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=cancel \
-d orders=23501,23502Response
[
{ "order": 23501, "cancel": 1 },
{ "order": 23502, "cancel": { "error": "Cancel not available" } }
]- Cancellation is accepted when the service has cancel: true, the order is still Pending, In progress or Processing, and it hasn't been requested before.
- cancel: 1 means the request was recorded. Keep polling status to see the final result.
balance
Get your current account balance.
| Parameter | Required | Description |
|---|---|---|
| key | Yes | Your API key (starts with smm_). |
| action | Yes | balance |
Request
curl -X POST https://9smm.com/api/v2 \
-d key=YOUR_API_KEY \
-d action=balanceResponse
{ "balance": "100.84", "currency": "USD" }- Balance and charges are always in USD, whatever currency you view the website in.
Errors
Errors are returned with HTTP 200 and a body such as {"error": "Incorrect order ID"}. Check for the error field on every response. The messages below are exact, so you can match on them.
| error | When it happens |
|---|---|
| Invalid API key | The key is missing or wrong, was replaced by a newer key, or the account is not active. |
| Rate limit exceeded | Too many requests for this key in the last minute. Returned with HTTP 429. |
| No action specified | The action parameter is empty. |
| Invalid action | The action is not one of the seven listed on this page. |
| Incorrect service ID | The service ID is not a number, the service isn't available, or you asked for drip-feed on a service without it. |
| Incorrect link | The link is empty, or you already have an active order for this link on this service. |
| Incorrect quantity | Quantity is missing or outside the service's min–max, a drip-feed run is outside those limits, or interval is outside 0–1440. |
| Incorrect comments | comments was sent but every line is empty. |
| Not enough funds on balance | Your balance doesn't cover the order charge. |
| Order failed | Any other rejection, for example more than 100 runs or a comment over 500 characters. |
| Incorrect order ID | The order ID doesn't exist on your account or isn't a number. |
| Too many orders (max 100) | More than 100 IDs in orders. |
| Refill not available | The order doesn't meet the refill conditions described under refill. |
| Cancel not available | The order doesn't meet the cancel conditions described under cancel. |
| Incorrect refill ID | No refill was requested for that order, or the ID isn't yours. |
| Internal error | Something failed on our side. Retry later; contact support if it persists. |
Rate limits and good practice
- Each key can make 1,200 requests in any rolling minute. Above that the API answers HTTP
429with{"error": "Rate limit exceeded"}; wait a few seconds and retry. - Poll order progress with
orders=(up to 100 IDs per request) instead of one request per order. - Cache the services list and refresh it periodically rather than on every order.
- If an
addrequest times out, checkstatusor your Orders page before sending it again. - Keep the key on your server. Never put it in browser JavaScript or a mobile app.
API FAQ
Is this API compatible with other SMM panels?
Yes. It uses the standard v2 format that most SMM panels and reseller scripts expect: the same action names, parameter names, response fields and status strings. Existing software usually only needs the new API URL and your key.
How do I get an API key?
Create a free 9SMM.COM account, open Profile in your dashboard and click Generate API key in the API access card. The full key is shown only once, so store it safely. Regenerating creates a new key and stops the old one immediately.
Can I send JSON instead of form data?
Yes. Send application/x-www-form-urlencoded (the standard) or a JSON body with the same parameter names. GET requests with query parameters are also accepted, but POST keeps your key out of server logs.
Why do errors come back with HTTP 200?
That is the SMM panel convention: many existing clients only read the response body and would crash on a 4xx status before seeing the message. Always check for an error field. The one exception is the rate limit, which returns HTTP 429.
What currency are balance and prices in?
USD. Rates in the services list are per 1,000 units, and balance and order charges are reported in USD even if you view the website in another currency.
What happens if my add request times out?
Don't resend it blindly. Call status or check the Orders page first. A second active order for the same link on the same service is rejected with Incorrect link, which protects you from most accidental duplicates.