Bearer keys
Send Authorization: Bearer smp_live_… or X-Api-Key. The full key is shown once and only its hash is stored. Up to 20 active keys per account, optional expiry date and one-click rotation.
How an integration works
The API mirrors what the dashboard does, in the same order. Nothing is charged until you create an order, and every write is idempotent.
In your dashboard, pick the scopes the key needs and, optionally, the IPs allowed to use it. The full key is shown once.
GET /v1/catalog/services returns every published service with the price already in your account currency and tier.
POST /v1/orders/preview validates the link and quantity and returns the exact charge without creating anything.
POST /v1/orders with an Idempotency-Key. The wallet is charged and the order is routed to the supplier with the best measured quality.
Register an https endpoint and receive signed events for status changes, partial delivery, completion, refunds and refills.
curl -X POST https://YOUR-DOMAIN/v1/orders \
-H "Authorization: Bearer smp_live_xxxxxxxxxx_..." \
-H "Idempotency-Key: 2b7c1d9e-order-1001" \
-H "Content-Type: application/json" \
-d '{"service":"<service_id>","link":"https://instagram.com/p/abc","quantity":1000}'Replace YOUR-DOMAIN with this storefront’s host, the key with your own and <service_id> with an id from the catalog.
Authentication and limits
A key is a server-side credential. It never goes into a browser, it carries only the permissions you grant, and you can revoke or rotate it at any time without touching your other keys.
Send Authorization: Bearer smp_live_… or X-Api-Key. The full key is shown once and only its hash is stored. Up to 20 active keys per account, optional expiry date and one-click rotation.
Optionally pin a key to up to 20 IP addresses. Requests from any other address are refused with 403 FORBIDDEN.
Each key inherits the requests-per-minute limit of your customer tier. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; over the limit you get 429 with Retry-After.
Limit in force for the Reseller tier1,200 requests per minute
Each key carries only the scopes you grant. A call outside its scopes is refused with 403 FORBIDDEN.
| Scope | What it does |
|---|---|
catalog:read | List services and prices |
orders:read | Read orders |
orders:write | Create orders, refill and cancel |
wallet:read | Read balances |
assets:read | List your listings and offers |
assets:write | Create and edit listings, verification and availability |
transactions:write | Record handoff steps on deals |
Idempotency
POST /v1/orders and POST /v1/orders/batch require an Idempotency-Key header of 8 to 120 characters. Repeating a call with the same key returns the same order without charging again. Reusing a key with a different body is refused with 409 IDEMPOTENCY_KEY_REUSE. Inside a batch, each item is keyed by the batch key plus its index.
HTTP/1.1 409 Conflict
X-Request-Id: req_<hex>
{
"error": {
"code": "IDEMPOTENCY_KEY_REUSE",
"message": "..."
},
"request_id": "req_<hex>"
}Endpoints
All paths live under /v1 on this storefront’s host. Money is always a decimal string with an ISO 4217 currency, never a float. Full request and response schemas are in the OpenAPI file.
| Method | Path | Scope | What it does |
|---|---|---|---|
| GET | /v1/catalog/platforms | catalog:read | Platforms that currently have published services |
| GET | /v1/catalog/services | catalog:read | Services with the price in your currency and tier; filter by platform |
| GET | /v1/catalog/services/{id} | catalog:read | One service with its field schema |
| Method | Path | Scope | What it does |
|---|---|---|---|
| POST | /v1/orders/preview | orders:write | Validate and quote without creating or charging |
| POST | /v1/ordersIdempotency-Key required | orders:write | Create an order: charges the wallet and routes to the best supplier |
| GET | /v1/orders | orders:read | List orders, newest first, with cursor pagination |
| POST | /v1/orders/batchIdempotency-Key required | orders:write | Up to 100 orders in one call, each with its own result |
| GET | /v1/orders/{id} | orders:read | Fetch one order by id |
| POST | /v1/orders/{id}/refill | orders:write | Request a refill within the guarantee window |
| POST | /v1/orders/{id}/cancel | orders:write | Request cancellation; refunded once the supplier confirms |
| Method | Path | Scope | What it does |
|---|---|---|---|
| GET | /v1/wallet/balance | wallet:read | Balances per currency: available, cash, bonus and reserved |
| Method | Path | Scope | What it does |
|---|---|---|---|
| POST | /v1/assets | assets:write | Create a listing draft; it goes live after verification and moderation |
| GET | /v1/assets | assets:read | Your listings |
| PATCH | /v1/assets/{id} | assets:write | Update a listing (full body; If-Match for optimistic locking) |
| POST | /v1/assets/batch | assets:write | Import up to 1,000 listings, matched by external_ref |
| POST | /v1/assets/{id}/verify | assets:write | Start (method) or check (verification_id) an ownership verification |
| POST | /v1/assets/{id}/availability | assets:write | Hide or show a listing (not while it is under negotiation) |
| Method | Path | Scope | What it does |
|---|---|---|---|
| GET | /v1/offers | assets:read | Offers received on your listings |
| Method | Path | Scope | What it does |
|---|---|---|---|
| POST | /v1/transactions/{id}/handoff | transactions:write | Mark a playbook step, add a secure item or complete the handoff |
HTTP/1.1 201 Created
X-Request-Id: req_<hex>
X-Api-Version: 2026-09-23
RateLimit-Limit: <limit> RateLimit-Remaining: <n> RateLimit-Reset: <seconds>
{
"id": "SMP-7K2Q9XHT",
"status": "routing",
"service": "<service_id>",
"link": "https://instagram.com/p/abc",
"quantity": 1000,
"charge": "<decimal>",
"refunded": "0",
"currency": "USD",
"start_count": null,
"remains": null,
"delivered": null,
"refill_until": null,
"created_at": "2026-09-23T12:00:00.000Z",
"updated_at": "2026-09-23T12:00:00.000Z"
}Webhooks
Register an https endpoint on a public host and choose the events you want. Every delivery is signed with your endpoint secret, which is shown once when the endpoint is created.
POST /webhooks/orders HTTP/1.1
Content-Type: application/json
Webhook-Id: <event id>
Webhook-Timestamp: 1790000000
Webhook-Signature: t=1790000000,v1=<hex HMAC-SHA256(secret, "1790000000.<body>")>
{ "id": "<event id>", "type": "order.partial", "created_at": "2026-09-23T12:05:00.000Z",
"data": { "orderId": "SMP-7K2Q9XHT", ... } }import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWebhook(secret, headers, rawBody) {
const t = headers['webhook-timestamp'];
const v1 = (headers['webhook-signature'] ?? '')
.split(',').find((p) => p.startsWith('v1='))?.slice(3) ?? '';
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300; // 5 minutes
return fresh && expected.length === v1.length
&& timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}Events are written to an outbox in the same database transaction as the change that caused them, so nothing is lost when a delivery fails. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h before being marked dead.
order.createdorder.status_changedorder.partialorder.completedorder.refundedorder.refill_updateddeposit.confirmedoffer.createdoffer.updatedreservation.createdtransaction.updatedpayout.updatedlisting.updateddispute.updatedCompatibility
POST /api/v2 implements the common “SMM API v2” contract: send key and action as form fields or JSON. Same keys, scopes and rate limits as /v1; only the format changes, and errors come back as { "error": "message" }.
Panels do not send an idempotency key, so one is derived from the order content and the current minute; the duplicate-order window covers the rest.
curl -X POST https://YOUR-DOMAIN/api/v2 \
-d key=smp_live_xxxxxxxxxx_... \
-d action=add \
-d service=<service_id> \
-d link=https://instagram.com/p/abc \
-d quantity=1000
{ "order": "SMP-7K2Q9XHT" }Errors
Every non-2xx response has the same body. Integrate on the code, not on the message: messages may change, codes will not. The request_id is also sent as the X-Request-Id header.
HTTP/1.1 400 Bad Request
X-Request-Id: req_<hex>
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request.",
"details": {
"fields": [{ "path": "quantity", "message": "..." }]
}
},
"request_id": "req_<hex>"
}| Code | HTTP | Meaning |
|---|---|---|
VALIDATION_ERROR | 400 | Invalid body or headers; details.fields lists each path and problem |
UNAUTHORIZED | 401 | Missing, invalid, revoked or expired key |
INSUFFICIENT_FUNDS | 402 | Wallet balance too low for the quote |
FORBIDDEN | 403 | Key lacks the scope, IP not allowed, sub-user without permission or account suspended |
NOT_FOUND | 404 | Order, service or listing does not exist for this account |
CONFLICT | 409 | State conflict, such as the active-keys limit |
INVALID_TRANSITION | 409 | The order or deal cannot move to that state |
DUPLICATE_ORDER | 409 | Same service, link and quantity inside the duplicate window |
IDEMPOTENCY_KEY_REUSE | 409 | Idempotency-Key already used with a different body |
ALREADY_RESERVED | 409 | The asset is reserved by another buyer |
POLICY_BLOCKED | 422 | Service or country blocked by platform policy |
MARGIN_GUARD | 422 | No supplier can serve this order at the published price |
UNPROCESSABLE | 422 | The request is valid but cannot be fulfilled |
RATE_LIMITED | 429 | Per-key limit reached; wait the seconds in Retry-After |
INTERNAL | 500 | Unexpected error; contact support with the request_id |
PROVIDER_ERROR | 502 | Upstream supplier failure; the order is retried or refunded |
NO_ROUTE | 503 | No supplier route available right now; retry later |
NOT_CONFIGURED | 503 | Feature not configured on this storefront |
Who can use it
API keys and webhook endpoints are created by the account owner on the Reseller plan. Orders placed with a key are charged to the account wallet and appear in the same dashboard as manual orders. Sub-users cannot create keys.
Whether you run a panel that needs a reliable supplier, an agency tool that places orders for clients, or a seller inventory that must stay in sync with the marketplace, the integration follows the same pattern: a scoped key, idempotent writes, and events you verify before you trust them.
Every endpoint lives under /v1 on the host of the storefront you are integrating with. Responses are JSON, money is a decimal string paired with an ISO 4217 currency, and timestamps are ISO 8601. Each response carries an X-Api-Version header with the date of the contract you are talking to, and an X-Request-Id you can quote to support. Errors always have the same body: an error object with a stable code, a human message and optional details, plus the request_id.
The OpenAPI 3.1 file describes every path, schema and error code. Import it into your HTTP client or generator and you get typed requests for the catalog, orders, wallet, listings, offers and deal handoffs.
A key is created by the account owner on the Reseller plan and is shown once; only a hash is stored. When you create it, you choose its scopes, an optional IP allowlist of up to 20 addresses and an optional expiry. Rotation creates a fresh key with the same scopes and revokes the old one, so you can roll credentials without downtime. Orders placed with a key are charged to the account wallet and appear in the same dashboard as manual orders, with their source marked as API.
Each key inherits the requests-per-minute limit of the account’s customer tier. The RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers tell you where you stand; when you exceed the limit the API returns 429 with a Retry-After header, and the right response is to wait exactly that long.
Order creation is the only call that moves money, so it is protected twice. First, the Idempotency-Key header is mandatory: repeating a request with the same key returns the original order without a second charge, and reusing the key with a different body is refused with 409 IDEMPOTENCY_KEY_REUSE. Second, a duplicate-order window catches the same service, link and quantity sent again inside a short interval without a key match, returning 409 DUPLICATE_ORDER.
Batches accept up to 100 orders per call. Each item is validated and charged on its own, keyed by the batch key plus its index, and the response lists a result per item so one bad line never blocks the rest. Use POST /v1/orders/preview first when you want the exact charge and validation result without creating anything.
Every state change is written to an outbox in the same database transaction as the change itself, and a dispatcher delivers it to your https endpoint with three headers: Webhook-Id, Webhook-Timestamp and Webhook-Signature. The signature is an HMAC-SHA256 of the timestamp, a dot and the raw body, computed with the endpoint secret shown once at creation. Verify it in constant time, reject timestamps older than five minutes and deduplicate by Webhook-Id, because a retried event carries the same id.
Deliveries that do not receive a 2xx are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 24 hours before being marked dead. Respond quickly and process asynchronously; a slow handler is the most common reason for duplicate processing.
If your panel already speaks the widespread “SMM API v2” format, you can add this storefront as a supplier by pasting the /api/v2 URL and your key. The actions services, add, status, balance, refill, refill_status and cancel are supported, prices come back in your account currency and tier, and errors use the flat { "error": "message" } shape those panels expect. Idempotency is derived from the order content and the current minute, so accidental double submissions inside the same minute are collapsed.
The Reseller plan. API keys and webhook endpoints are created by the account owner; sub-users cannot create them. Keys are server-side credentials and should never be embedded in a browser or mobile app.
Send the key as Authorization: Bearer smp_live_… or in an X-Api-Key header. The key must carry the scope the endpoint requires, for example orders:write to create an order or catalog:read to list services.
Resend it with the same Idempotency-Key. You receive the original order and nothing is charged twice. If you send a different body under the same key, the API answers 409 IDEMPOTENCY_KEY_REUSE.
Compute HMAC-SHA256 with your endpoint secret over the string formed by Webhook-Timestamp, a dot and the raw body, then compare it in constant time with the v1 value in Webhook-Signature. Reject timestamps older than 5 minutes and deduplicate by Webhook-Id.
Each key inherits the requests-per-minute limit of your customer tier, shown on this page. The RateLimit-* headers report the current window and a 429 response includes Retry-After in seconds.
Yes. GET /v1/catalog/services returns the price already converted to your account currency with your tier discount, and POST /v1/orders/preview returns the exact charge, including volume discounts and coupons, before you commit.
Yes. With the assets:write scope you can create and update listings, import up to 1,000 at once, start ownership verification and toggle availability; assets:read lists your listings and offers, and transactions:write records handoff steps on a deal.
On the Reseller plan, create a key, download the OpenAPI file and send your first order from your own system.
We never sell reviews or ratings. Some platforms restrict engagement services or account transfers in their terms; you are responsible for the rules of each platform you use.