Reseller API · v2
One URL, seven actions
The catalogue, orders, refills, cancels and your balance, all from a script. POST form fields or JSON, read JSON back. The shapes below are the ones every panel client already expects, so a script written against another panel usually needs the URL and the key changed and nothing else.
- Method
- POST only. GET answers 405.
- Body
- Form fields or a JSON object.
- Failures
- HTTP 200 with an error key.
https://xsmmpanel.uk/api/v2Sign in and your real key drops into every example on this page. Sign in or open an account.
The key spends your balance without a second check. Treat it like the password. If it ends up somewhere public, regenerate it on your API page and the old one stops working immediately.
Getting started
Two minutes to the first call
Copy your key from the panel above, then run the balance call. It moves no money and touches no order, so it is the safest way to prove the key and the endpoint are right before you point a real script at them.
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=balance"
{
"balance": "24.3500",
"currency": "USD"
}- An answer of
{ "error": "Invalid API key" }means the key is wrong or the account is suspended. It still arrives with HTTP 200. - An answer of
{ "error": "Confirm your email address before using the API" }means the key is right and the address has not been confirmed. Open the link in the signup email and run it again. - Anything else and you are through. Head to the service list for the ids you will order against.
Request format
The rules that hold everywhere
| Property | Value |
|---|---|
| Endpoint | https://xsmmpanel.uk/api/v2 |
| Method | POST. A GET returns HTTP 405 and { "error": "Use POST. See the documentation at /api" }. |
| Content type | application/x-www-form-urlencoded · application/json |
| Auth | key in the body of every request. No headers, no signatures, no session. |
| Response | JSON. Money and counts come back as strings; money carries four decimals. |
| Ids per call | 100 for orders, refills and the ids you pass to cancel. Anything past that is dropped without a word. |
curl -X POST https://xsmmpanel.uk/api/v2 \
-H "Content-Type: application/json" \
-d '{"key":"YOUR_API_KEY","action":"status","order":23501}'- JSON values are turned into strings before they are read, so
"quantity": 1000and"quantity": "1000"behave identically. Arrays do not survive that trip, which is whycommentsis one string with newlines rather than a list. keyandactionare trimmed, andactionis lower-cased. Fields you invent are ignored rather than rejected.- Send the form encoding if you have a choice. It is what the curl examples on this page use and what most existing panel libraries emit.
Service list
action=servicesEvery service you can order right now, ordered by id. Anything the network has delisted, or an admin has switched off, is left out of the list rather than returned with a flag.
| Field | Type | Required | Meaning |
|---|---|---|---|
| key | string | Yes | Your API key. |
| action | string | Yes | services |
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=services"
[
{
"service": 1001,
"name": "Instagram Followers | Max 50K | Instant | No refill",
"type": "Default",
"category": "Instagram Followers",
"rate": "0.4200",
"min": "50",
"max": "50000",
"refill": false,
"cancel": false,
"dripfeed": false
}
]rateis the price per 1,000 units, as a string with four decimals.minandmaxare strings too.refill,cancelanddripfeedare real JSON booleans, not0and1.typeis one ofDefault,Custom Comments,Mentions,PackageorSubscriptions. OnlyCustom Commentschanges what you send toadd.- Rates move when the network’s cost moves, so cache the list for minutes, not days. The charge is calculated from the rate at the moment the order lands, not the rate you cached.
Add order
action=addTakes the money and hands the order to the network network inside the same request. The charge is rate × quantity ÷ 1000 rounded to four decimals, and it has already left your balance by the time the response arrives.
| Field | Type | Required | Meaning |
|---|---|---|---|
| key | string | Yes | Your API key. |
| action | string | Yes | add |
| service | integer | Yes | Service id from the service list. |
| link | string | Yes | The public page, post or profile URL. It has to parse as http or https; a username on its own is rejected before your balance is touched. |
| quantity | integer | Yes | Units to deliver. Must sit between the service’s min and max. |
| runs | integer | No | Drip-feed only. How many runs to split the delivery into, 1 to 100. |
| interval | integer | No | Drip-feed only. Minutes between runs, 1 to 1440. |
| comments | string | No | Custom Comments services only, and required for them. One comment per line, separated by newlines. Blank lines are dropped. |
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=add" \ -d "service=1001" \ -d "link=https://instagram.com/yourprofile" \ -d "quantity=1000"
{
"order": 23501
}curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=add" \ -d "service=1088" \ -d "link=https://instagram.com/p/Cx0abcd/" \ -d "quantity=250" \ -d "runs=4" \ -d "interval=60"
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=add" \ -d "service=1204" \ -d "link=https://instagram.com/p/Cx0abcd/" \ -d "quantity=3" \ --data-urlencode "comments=Shot of the week Where was this taken? Saved for later"
- With
runs,quantityis the amount per run. The drip-feed example above bills 4 × 250 = 1,000 units, and the order reads 1,000 everywhere afterwards. - Sending
runsorintervalto a service withdripfeed: falsereturnsDrip-feed is not available for this service. The order is not created. - For
Custom Comments,quantityis what you are charged for, not the number of lines you sent. Send as many lines as the quantity you are paying for. - In a JSON body,
commentsis still a single string with\nbetween lines. Arrays are stringified, not read as a list. ordercomes back as a JSON number. The single-orderrefillbelow returns its id as a string. Parse both rather than assuming a type.- If the network is unreachable the order is still created and still charged. It sits at
Pendingand the sync job forwards it on a retry. If the network rejects it outright, the charge goes straight back to your balance. Either way you get an order id, so treat it as a receipt and pollstatusrather than as proof of delivery.
Order status
action=statusOne order, by id. Orders belonging to another account answer the same way a nonexistent id does, so the endpoint never confirms that someone else's order number is real.
| Field | Type | Required | Meaning |
|---|---|---|---|
| key | string | Yes | Your API key. |
| action | string | Yes | status |
| order | integer | Yes | Order id. |
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=status" \ -d "order=23501"
{
"charge": "1.7500",
"start_count": "4821",
"status": "In progress",
"remains": "1200",
"currency": "USD"
}statusis one ofPending,In progress,Processing,Completed,PartialorCanceled. The last three are terminal.chargeis what the order cost after settlement, so it drops when an order finishes as aPartialand the undelivered share is refunded. Read it again once the status goes terminal if you are reconciling spend.- These figures come from the panel’s copy of the order, refreshed by the sync job rather than fetched from the network on every call. Polling faster than that loop returns the same numbers.
Status for many orders
action=statusSame action, but pass orders instead of order and the response becomes an object keyed by id. Use it instead of a loop; one call replaces a hundred.
| Field | Type | Required | Meaning |
|---|---|---|---|
| key | string | Yes | Your API key. |
| action | string | Yes | status |
| orders | string | Yes | Order ids separated by commas. Everything past the 100th is dropped. |
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=status" \ -d "orders=23501,23502,23503"
{
"23501": {
"charge": "1.7500",
"start_count": "4821",
"status": "Completed",
"remains": "0",
"currency": "USD"
},
"23502": {
"error": "Incorrect order ID"
},
"23503": {
"charge": "0.8400",
"start_count": "0",
"status": "Pending",
"remains": "2000",
"currency": "USD"
}
}- A bad id fails inside its own key and the rest of the batch still returns. There is no all-or-nothing behaviour here.
- Values that are not positive integers are dropped before the query runs, so they get no key in the response at all. Match on the ids you sent, not on the ones you got back.
- If none of the values parse, the whole response collapses to
{ "error": "Incorrect order ID" }. - Send both
orderandordersandorderswins.
Create refill
action=refillAsks the network to top the count back up on a completed order. Refills cost nothing; your balance is not touched.
| Field | Type | Required | Meaning |
|---|---|---|---|
| key | string | Yes | Your API key. |
| action | string | Yes | refill |
| order | integer | No | One order id. Use this or orders. |
| orders | string | No | Order ids separated by commas, up to 100. Changes the response to an array. |
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=refill" \ -d "order=23501"
{
"refill": "1"
}curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=refill" \ -d "orders=23501,23502"
[
{ "order": 23501, "refill": 4 },
{ "order": 23502, "refill": { "error": "Refill is not available for this service" } }
]- The order has to be
Completedand the service has to carryrefill: true. Anything else is refused with the reason in the error string. - One refill at a time per order. While one sits at
PendingorIn progress, a second request returnsA refill is already in progress for this order. - The single form returns the refill id as a string; the multi form returns it as a number on each row. Keep it either way, since
refill_statusneeds it.
Refill status
action=refill_statusWhere a refill got to. Takes one id or a list, and the shape changes with it in the same way status does.
| Field | Type | Required | Meaning |
|---|---|---|---|
| key | string | Yes | Your API key. |
| action | string | Yes | refill_status |
| refill | integer | No | One refill id. Use this or refills. |
| refills | string | No | Refill ids separated by commas, up to 100. Changes the response to an array. |
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=refill_status" \ -d "refill=4"
{
"status": "Completed"
}curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=refill_status" \ -d "refills=4,9"
[
{ "refill": 4, "status": "In progress" },
{ "refill": 9, "status": { "error": "Incorrect refill ID" } }
]- A refill is
Pending,In progress,CompletedorRejected. - In the multi form the error replaces the status value, so
statusis either a string or an object. Check its type before comparing it. - Refills on orders that are not yours read as
Incorrect refill ID.
Cancel orders
action=cancelStops an order that has not started delivering and returns the full charge to your balance. The response is an array whether you send one id or a hundred.
| Field | Type | Required | Meaning |
|---|---|---|---|
| key | string | Yes | Your API key. |
| action | string | Yes | cancel |
| orders | string | Yes | Order ids separated by commas, up to 100. A single order field works too, and the response shape does not change. |
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=cancel" \ -d "orders=23501,23502"
[
{ "order": 23501, "cancel": 1 },
{ "order": 23502, "cancel": { "error": "Order can no longer be cancelled" } }
]- A success is the number
1, not a message. A failure is an object with anerrorkey on that row. - Only services with
cancel: truecan be cancelled, and only while the count has not moved. Once the network reports a start count and part of the quantity is delivered, the window is shut. - Orders already sitting with the network are cancelled there first. If they cannot be reached, the row comes back with
Could not reach the network to cancel. Try again in a minute.and nothing is refunded, so a retry cannot double-refund you.
Account balance
action=balanceYour spendable credit. Cheap enough to call before a batch of orders so you can stop before the first Not enough funds.
| Field | Type | Required | Meaning |
|---|---|---|---|
| key | string | Yes | Your API key. |
| action | string | Yes | balance |
curl -X POST https://xsmmpanel.uk/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=balance"
{
"balance": "24.3500",
"currency": "USD"
}- Four decimals, always
USD. The display currency on your account page converts for reading only; charges, rates and this figure stay in dollars. - Credit is bought in the panel, not through the API. There is no top-up action.
Errors
A failure is still a 200
Every failure except a GET comes back with HTTP 200 and a body of { "error": "..." }. A client that only checks the status code will read a rejected order as a placed one. Check for the error key on every response before you touch anything else in it.
const res = await fetch("https://xsmmpanel.uk/api/v2", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key: API_KEY, action: "add", service: 1001,
link: "https://instagram.com/yourprofile", quantity: 1000 }),
});
const data = await res.json(); // res.ok is true even on a rejection
if (data.error) throw new Error(data.error);
console.log("order", data.order);| Message | Cause |
|---|---|
| Invalid API key | The key is missing, does not match an account, or the account is suspended. |
| Confirm your email address before using the API | The key is real but the address on the account has never been confirmed. This blocks every action, including balance and services. Open the link in the signup email and retry. |
| Invalid action | action is missing or is not one of the seven above. The value is trimmed and lower-cased first, so ADD is fine. |
| Incorrect service ID | No active service carries that id. |
| Incorrect link | link did not parse as an http or https URL. |
| Incorrect quantity | Quantity is missing, zero, negative or not a number. |
| Quantity is below the minimum of N | N is that service’s own min, so the message tells you the number to use. |
| Quantity exceeds the maximum of N | Same, against max. Split the job across several orders. |
| Not enough funds | The charge is larger than your balance. Nothing is deducted and no order is created. |
| Drip-feed is not available for this service | runs or interval was sent to a service with dripfeed: false. |
| Runs must be between 1 and 100 | Drip-feed run count is out of range. |
| Interval must be between 1 and 1440 minutes | Drip-feed gap is out of range. 1440 minutes is a day. |
| This service needs a comment on each line | A Custom Comments service was ordered with comments empty or blank. |
| Incorrect order ID | No order with that id on your account. Other people's orders answer the same way. |
| Refill is not available for this service | The service carries refill: false. |
| Refill is only available for completed orders | The order has not reached Completed yet. |
| A refill is already in progress for this order | One open refill per order. Poll refill_status and wait for it to settle. |
| Incorrect refill ID | No refill with that id against one of your orders. |
| Cancel is not available for this service | The service carries cancel: false. |
| Order can no longer be cancelled | The order is already closed, or delivery has started. |
| Could not reach the network to cancel. Try again in a minute. | The cancel could not be confirmed upstream. Nothing was refunded, so retrying is safe. |
- Messages are plain English and stable. Match on them if you have to, but prefer branching on which action you called plus the presence of
error. Not enough fundsdeducts nothing and creates nothing. Retrying after a top-up is safe.- A response that does not match this page is a bug on our side. Open a ticket with the request body and the response and it gets fixed.