API v3

API docs

API reference for SMM Africa. Check balances, fetch services, place orders, and track status from one endpoint.

Base URLhttps://smm.africa/api/v3
FormatPOST · JSON or form fields · JSON response
Your API keyIssued per account. Pull it from Account once you sign in.
Sign in to get your key

Start with a balance check

A balance request is the fastest way to confirm your key, request format, and that the integration round-trips before you start placing orders.

POST /api/v3 · balance
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "balance"
}'
HTTP 200 · balance
{
  "balance": "100.84",
  "currency": "USD"
}

Authentication

Send your account API key either in Authorization: Bearer YOUR_API_KEY or as the key request field. Choose one method; the key field works with common reseller panels. Keep keys in server-side environment variables and rotate them from Account.

All actions use POST https://smm.africa/api/v3. Supported bodies: application/json, application/x-www-form-urlencoded, and multipart/form-data with text fields only. Responses are JSON. The action field selects the operation.

curl · form authentication
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  --data-urlencode "key=YOUR_API_KEY" \
  --data-urlencode "action=balance"

All balances, charges, and order amounts in v3 are returned in USD for compatibility.

From API connection to your first order

Follow these steps at your own pace. This guide never sends an order or charges your wallet.

1 / 6 · Connect your account

Open your account and obtain your API key. Keep it on your server. Use the balance example to check authentication without creating a paid order.

Open connection example

Buying questions and guides

Your first reseller order

Why are my customer payments separate from my SMM.Africa wallet?

Your customer pays your business under your own arrangement. Your SMM.Africa wallet pays for the orders you submit here. Collecting a customer payment does not automatically fund that wallet, and a wallet refund does not automatically reimburse your client. Reconcile the two records separately, including fees and any approved customer refund.

Open this answer
How should I place my first reseller API order?

Start with the API authentication and balance examples, then review the service, target format, limits, and price. Use a target you control and a quantity within the stated limits. The add action spends wallet balance. Save the returned order ID and check its status. Read the current API documentation before choosing a retry strategy.

Open this answer
Should I send another API order if the first request times out?

First check whether the original request created an order. A timeout can leave the outcome uncertain even when processing succeeded. Preserve the request reference and any order ID, and use the documented idempotency mechanism where supported. Do not generate a new paid request merely because the first response was missing; contact support if the outcome remains unclear.

Open this answer

Read the full guide

Reference

Full endpoint reference

Review the request parameters, sample responses, and the exact behavior for each supported action.

balance

Check the wallet balance tied to the authenticated reseller API key.

POST
POST /api/v3 · balance
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "balance"
}'

Request parameters

ParameterTypeRequiredDescription & constraints
keystringConditionalYour API key, used to authenticate the request. Required only when Authorization: Bearer is absent.
actionstringYesbalance Use the exact action value shown.

Example response

HTTP 200 · balance
{
  "balance": "100.84",
  "currency": "USD"
}

All balances, charges, and order amounts in v3 are returned in USD for compatibility.

Example error response

HTTP 400 · balance
{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY",
  "request_id": "request-123",
  "retryable": false
}

services

Fetch the live SMM Africa service catalog with service IDs, pricing, limits, and refill or cancel flags.

POST
POST /api/v3 · services
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "services"
}'

Request parameters

ParameterTypeRequiredDescription & constraints
keystringConditionalYour API key, used to authenticate the request. Required only when Authorization: Bearer is absent.
actionstringYesservices Use the exact action value shown.

Example response

HTTP 200 · services
[
  {
    "service": 1,
    "name": "Service Name",
    "type": "Default",
    "rate": "0.90",
    "min": "50",
    "max": "10000",
    "category": "First Category",
    "description": "Start: 2 mins | Speed: 1000/day",
    "refill": true,
    "cancel": true,
    "drop_rate": "Almost No",
    "start_time": "2 mins",
    "speed": "1000/day",
    "reliability": "Poor"
  }
]

Fetch the live catalog before ordering because service IDs, rates, limits, and availability can change.

Example error response

HTTP 400 · services
{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY",
  "request_id": "request-123",
  "retryable": false
}

add

Place a new social media growth order with service ID, target link, and quantity.

POST
POST /api/v3 · add
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "add",
  "service": 1234,
  "link": "https://instagram.com/your-profile",
  "quantity": 500,
  "max_charge": 5,
  "idempotency_key": "order-unique-001"
}'

Request parameters

ParameterTypeRequiredDescription & constraints
keystringConditionalYour API key, used to authenticate the request. Required only when Authorization: Bearer is absent.
actionstringYesadd Use the exact action value shown.
serviceintegerYesThe ID of the service you want to order. Use an ID from the current services response; its type determines the fields below.
linkstringConditionalThe target profile, post, or content URL. Required for the selected service type; supply the target URL.
quantityintegerConditionalThe quantity you want to order. Must satisfy the selected service's min and max. Omit when its type does not accept quantity.
max_chargenumber / decimal stringNoOptional maximum USD charge, including discounts, with up to six decimal places. PRICE_LIMIT_EXCEEDED (409) creates no order or debit. Changing a ceiling changes request intent. Maximum permitted USD charge, with at most six decimal places.
idempotency_keystringRecommendedUse one unique value per logical order. If the request times out without a response, retry with the same value so the order cannot be charged twice. Persist a unique value per logical order; reuse only with an identical retry payload.
Fields by service type

Select the type returned by services; do not send a separate type field. List values are newline-separated text, including in JSON. The API derives billable quantity for comments, custom lists and packages, then checks service limits.

Service typeRequired fieldsOptional fields / constraints
Defaultlink, quantityStandard target URL and requested quantity.
PackagelinkNo quantity.
SEOlink, quantity, keywordsPut one keyword on each line.
Custom Commentslink, commentsOne comment per line; the comment count is the order quantity.
Custom Comments Packagelink, commentsOne comment per line; no quantity.
Comment Replieslink, username, commentsusername is the comment owner; put one reply per line.
Comment Likeslink, usernameusername is the comment owner; no quantity.
Mentionslink, quantity, usernamesPut one username per line.
Mentions with Hashtagslink, quantity, usernames, hashtagsPut usernames and hashtags on separate lines.
Mentions Custom Listlink, usernamesPut one username per line; no quantity.
Mentions Hashtaglink, quantity, hashtagsPut one hashtag per line.
Mentions User Followerslink, quantity, usernameusername is the source profile URL to scrape.
Mentions Media Likerslink, quantity, mediamedia is the source media URL to scrape.
Polllink, quantity, answeranswer is the exact poll-option text required for the service.
Invites from Groupslink, quantity, groupsPut one group on each line.
Web Trafficlink, quantity, country, device, type_of_trafficdevice is mobile or desktop. type_of_traffic is 1 (requires google_keyword), 2 (requires referring_url), or 3.
Optional attribution fields
ParameterTypeRequiredDescription & constraints
goal_keystringNoOptional goal key for guided-growth attribution. Optional attribution metadata.
recommendation_tierstringNoOptional recommendation tier. Optional attribution metadata.
user_intent_labelstringNoOptional label describing the user’s intent. Optional attribution metadata.
source_flowstringNoOptional identifier for the flow that created the order. Optional attribution metadata.

Example response

HTTP 200 · add
{
  "order": 1000000,
  "charged": 5.85,
  "queued": true
}

Store the returned order ID, and check status before retrying an add request.

Example error response

HTTP 400 · add
{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY",
  "request_id": "request-123",
  "retryable": false
}

status

Poll an order by ID to read delivery state, start count, remains, and charge details.

POST
POST /api/v3 · status
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "status",
  "orders": [
    "1000000",
    "1000001"
  ]
}'

Request parameters

ParameterTypeRequiredDescription & constraints
keystringConditionalYour API key, used to authenticate the request. Required only when Authorization: Bearer is absent.
actionstringYesstatus Use the exact action value shown.
orderinteger / stringYesThe SMM Africa order ID returned by add. Use the order ID returned by add for follow-up requests.
ordersarray / stringConditionalFor bulk status, send 1–20 IDs as a JSON array or comma-separated form string instead of order. Inspect each item's http_status and retry_after. Use order or orders, never both. Each bulk item has its own http_status.

Example response

HTTP 200 · status
{
  "orders": [
    {
      "order": 1000000,
      "status": "processing",
      "start_count": 3572,
      "remains": 157,
      "charge": 0.27819,
      "updated_at": "2026-10-15T12:00:00Z",
      "freshness": "fresh",
      "reason_code": null,
      "retryable": false,
      "automatic_retry": false,
      "can_cancel": false,
      "funds_state": "captured",
      "next_retry_at": null,
      "refund_deadline_at": null,
      "http_status": 200
    },
    {
      "order": "1000001",
      "http_status": 400,
      "error": "Order not found",
      "code": "ORDER_NOT_FOUND",
      "request_id": "request-123",
      "retryable": false
    }
  ]
}

Poll one order per status request. Stagger requests and honor Retry-After when rate limited.

Example error response

HTTP 400 · status
{
  "error": "Order not found",
  "code": "ORDER_NOT_FOUND",
  "request_id": "request-123",
  "retryable": false
}

refill

Request a refill for an eligible order inside the published refill window.

POST
POST /api/v3 · refill
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "refill",
  "order": 1000000,
  "idempotency_key": "refill-unique-001"
}'

Request parameters

ParameterTypeRequiredDescription & constraints
keystringConditionalYour API key, used to authenticate the request. Required only when Authorization: Bearer is absent.
actionstringYesrefill Use the exact action value shown.
orderinteger / stringYesThe SMM Africa order ID returned by add. Use the order ID returned by add for follow-up requests.

Example response

HTTP 200 · refill
{
  "order": 1000000,
  "success": "Refill request accepted."
}

Refill is available only for eligible orders within the service’s published refill window.

Example error response

HTTP 400 · refill
{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY",
  "request_id": "request-123",
  "retryable": false
}

cancel

Request cancellation for an order where the selected service supports cancellation.

POST
POST /api/v3 · cancel
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "cancel",
  "order": 1000000,
  "idempotency_key": "cancel-unique-001"
}'

Request parameters

ParameterTypeRequiredDescription & constraints
keystringConditionalYour API key, used to authenticate the request. Required only when Authorization: Bearer is absent.
actionstringYescancel Use the exact action value shown.
orderinteger / stringYesThe SMM Africa order ID returned by add. Use the order ID returned by add for follow-up requests.

Example response

HTTP 200 · cancel
{
  "order": 1000000,
  "success": "Cancellation request accepted."
}

Cancellation is available only when the selected service supports it.

Example error response

HTTP 400 · cancel
{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY",
  "request_id": "request-123",
  "retryable": false
}

Build and test your integration

Check balance, fetch services, select the service type, persist an operation ID, submit the order, save its order ID, then poll status. Prices are USD; rates are per 1,000 units or per package according to service type. Treat IDs as opaque strings. Decimal strings and numeric money fields retain their existing types; charges use six-decimal USD precision.

The Node.js and PHP 3.3 clients persist operation keys and successful responses, honor Retry-After, and stop unresolved mutations before the 24-hour replay window expires. Keep state private, persistent, and separate for each account. Test first with the local sandbox, which has no connection to live wallets or suppliers.

Node.js 3.3 · PHP 3.3 · Sandbox · Integration guide · Webhook signature verifier

Local sandbox · Node 22+
node sandbox.mjs
# Endpoint: http://127.0.0.1:4013/api/v3
# Key: sandbox_test_key (never use a live key)

subscription_create

Create a capped subscription without debiting funds. Use a post-URL service, a positive integer quantity, USD, spending_cap with up to six decimals, max_posts (1–10000), and a persisted idempotency key.

POST /api/v3 · subscription_create
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "subscription_create",
  "service": 1234,
  "quantity": 500,
  "currency": "USD",
  "spending_cap": 25,
  "max_posts": 10,
  "idempotency_key": "campaign-unique-001"
}'
HTTP 200 · subscription_create
{
  "subscription": {
    "id": "YOUR_SUBSCRIPTION_ID",
    "service_id": "1234",
    "quantity": 500,
    "currency": "USD",
    "spending_cap": "25.000000",
    "spent": "0.000000",
    "max_posts": 10,
    "post_count": 0,
    "state": "active",
    "pause_reason": null
  }
}

subscription_post

Submit a discovered post URL. The service and quantity come from the subscription. Optional max_charge caps this post in USD. A repeated canonical URL returns its original order, including after cancellation.

POST /api/v3 · subscription_post
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "subscription_post",
  "subscription": "YOUR_SUBSCRIPTION_ID",
  "link": "https://example.com/post/123",
  "max_charge": 5,
  "idempotency_key": "post-unique-001"
}'
HTTP 200 · subscription_post
{
  "order": "1000000",
  "charged": 0.5,
  "queued": true
}

subscription_status

Read settings and up to 20 post receipts. Pass after=next_cursor for subsequent pages. Poll receipt order IDs using bulk status.

POST /api/v3 · subscription_status
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "subscription_status",
  "subscription": "YOUR_SUBSCRIPTION_ID"
}'
HTTP 200 · subscription_status
{
  "subscription": {
    "id": "YOUR_SUBSCRIPTION_ID",
    "service_id": "1234",
    "quantity": 500,
    "currency": "USD",
    "spending_cap": "25.000000",
    "spent": "0.000000",
    "max_posts": 10,
    "post_count": 0,
    "state": "active",
    "pause_reason": null
  },
  "posts": [
    {
      "post_key": "opaque-cursor",
      "order_id": "1000000",
      "charged": "0.500000",
      "created_at": "2026-10-15T12:00:00Z"
    }
  ],
  "next_cursor": null
}

subscription_pause

Pause future post submissions. Existing orders continue.

POST /api/v3 · subscription_pause
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "subscription_pause",
  "subscription": "YOUR_SUBSCRIPTION_ID"
}'
HTTP 200 · subscription_pause
{
  "subscription": {
    "id": "YOUR_SUBSCRIPTION_ID",
    "service_id": "1234",
    "quantity": 500,
    "currency": "USD",
    "spending_cap": "25.000000",
    "spent": "0.000000",
    "max_posts": 10,
    "post_count": 0,
    "state": "paused",
    "pause_reason": null
  }
}

subscription_resume

Resume a paused subscription after funding. Completed or canceled subscriptions cannot resume.

POST /api/v3 · subscription_resume
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "subscription_resume",
  "subscription": "YOUR_SUBSCRIPTION_ID"
}'
HTTP 200 · subscription_resume
{
  "subscription": {
    "id": "YOUR_SUBSCRIPTION_ID",
    "service_id": "1234",
    "quantity": 500,
    "currency": "USD",
    "spending_cap": "25.000000",
    "spent": "0.000000",
    "max_posts": 10,
    "post_count": 0,
    "state": "active",
    "pause_reason": null
  }
}

subscription_cancel

Stop future posts permanently. Existing orders must be canceled individually while eligible. Refunds do not replenish the subscription spending cap.

POST /api/v3 · subscription_cancel
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "subscription_cancel",
  "subscription": "YOUR_SUBSCRIPTION_ID"
}'
HTTP 200 · subscription_cancel
{
  "subscription": {
    "id": "YOUR_SUBSCRIPTION_ID",
    "service_id": "1234",
    "quantity": 500,
    "currency": "USD",
    "spending_cap": "25.000000",
    "spent": "0.000000",
    "max_posts": 10,
    "post_count": 0,
    "state": "canceled",
    "pause_reason": null
  }
}

webhook_set

Configure one verified HTTPS receiver per account. Requires deployment enablement. Generate a random 32–128 character base64url secret. Verify X-SMM-Signature, then echo the webhook.verify challenge as JSON. Reconfiguration invalidates old pending events.

POST /api/v3 · webhook_set
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "webhook_set",
  "url": "https://example.com/smm-webhook",
  "secret": "REPLACE_WITH_RANDOM_BASE64URL_SECRET"
}'
HTTP 200 · webhook_set
{
  "webhook": {
    "url": "https://example.com/smm-webhook",
    "enabled": true
  }
}

webhook_status

Read the current URL, enabled state, and update time. The signing secret is never returned.

POST /api/v3 · webhook_status
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "webhook_status"
}'
HTTP 200 · webhook_status
{
  "webhook": {
    "url": "https://example.com/smm-webhook",
    "enabled": true,
    "updated_at": "2026-10-15T12:00:00Z"
  }
}

webhook_disable

Disable future delivery. An already in-flight HTTP request may finish.

POST /api/v3 · webhook_disable
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "webhook_disable"
}'
HTTP 200 · webhook_disable
{
  "webhook": {
    "enabled": false
  }
}

webhook_deliveries

Read up to 20 deliveries, their payload, attempts, last HTTP status, and next attempt time. Pass after=next_cursor for subsequent pages. History is retained for 30 days.

POST /api/v3 · webhook_deliveries
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "webhook_deliveries"
}'
HTTP 200 · webhook_deliveries
{
  "deliveries": [],
  "next_cursor": null
}

webhook_replay

Queue a delivered or failed event for the same endpoint version. The event ID is preserved. Consumers must deduplicate; replay never repeats an order or wallet mutation.

POST /api/v3 · webhook_replay
curl --fail-with-body --silent --show-error --max-time 60 \
  -X POST https://smm.africa/api/v3 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SMM_AFRICA_KEY" \
  -d '{
  "action": "webhook_replay",
  "event": "evt_123"
}'
HTTP 200 · webhook_replay
{
  "event": "evt_123",
  "queued": true
}

Verify and reconcile order events

X-SMM-Signature contains t=UNIX_SECONDS,v1=HEX_HMAC_SHA256. Sign timestamp + "." + the exact raw request body with your secret. Use constant-time comparison and reject timestamps more than five minutes from your clock. Persist each event ID before returning 2xx. Delivery is at least once and may arrive out of order. Events cover account order activity, including website orders. Fetch status for current delivery and refund details; polling remains the reconciliation fallback. Failed delivery retries up to eight times with backoff; redirects are rejected.

order.updated
{
  "id": "evt_123",
  "type": "order.updated",
  "api_version": "3.3",
  "created_at": "2026-10-15T12:00:00Z",
  "data": {
    "order": "1000000",
    "status": "completed"
  }
}

Errors & retries

Check the HTTP status and the JSON error field. Authentication errors use HTTP 400; API callers are not redirected to login. Log technical errors privately and show customers a neutral message.

HTTP 400 · application/json
{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY",
  "request_id": "request-123",
  "retryable": false
}

Add responses replay for 24 hours. After expiry, the same key can create another order: reconcile first. REQUEST_IN_PROGRESS permits the same request/key after Retry-After. IDEMPOTENCY_CONFLICT requires restoring the original payload. ACTION_OUTCOME_UNCONFIRMED requires support review: never change keys to bypass it. PRICE_LIMIT_EXCEEDED means no debit or order was committed. Log request_id (also X-Request-Id) for support.

HTTPError / conditionWhat to do
400Invalid API keyCheck the key and required fields. Correct the request before retrying.
402Insufficient balanceFund the account wallet before submitting the order again.
409A matching API order request is already being processedWait before retrying the identical request with its original idempotency key.
409This idempotency key was already used for a different API order requestRestore the original payload for a retry. Use a new key only for a new logical order.
415Unsupported content typeSend JSON, URL-encoded form fields, or multipart text fields.
423Account suspended (message varies)Resolve the account restriction with support; do not retry repeatedly.
429Too many requests. Please slow down and try again shortly.Wait for the Retry-After interval, then retry with backoff.
500 / 503Server failure or temporary unavailability (message varies)Back off. For order submissions, keep the same payload and idempotency key.

Retry an order without charging twice

  1. Create and persist a unique idempotency_key for each logical order before sending add. Replace the example key with your own unique value.
  2. Allow up to 60 seconds for the server request. After 20 seconds, show a pending message to the customer while the backend request continues.
  3. If no response arrives, retry the exact same payload with the same key after a delay. Do not generate a new key. A completed retry replays the response with X-Idempotent-Replay: true.
  4. Once you receive an order ID, save it and use status to track that order. A queued response means accepted for fulfillment, not completed delivery.

Idempotency-Key and X-Idempotency-Key headers are also supported. Without a client key, an order can still be submitted, but retrying after a timeout cannot safely identify the original attempt.

Rate limits

Limits are action-specific and deployment-configurable. On HTTP 429, honor Retry-After (seconds). Without that header, use exponential backoff with jitter, for example 2, 4, 8, then 16 seconds plus a small random delay. Bound retries and surface persistent failures to your operator.

Cache the catalog fetched with POST /api/v3 and action=services. Refresh it periodically and verify current service IDs, rates, and min/max constraints before ordering.

Order lifecycle

Send action=status with order for one ID, or orders for 1–20 IDs. Bulk responses include http_status per item; back off per item using retry_after. As a starting client policy, poll every 30 seconds, then increase the interval for long-running orders; honor rate limits. Stagger requests across orders. Signed order webhooks are available when enabled for the deployment. Keep bulk polling for reconciliation. A stale response never authorizes repeating add.

queued → pending → processing
Active states: continue polling. Orders may skip intermediate states.
completed · partial · refunded · canceled
Terminal states: stop routine delivery polling. Partial means delivery was incomplete; inspect charge and remains. Refill and cancellation requests are subject to service and order eligibility.
unknown
An unrecognized state; do not treat it as success or place a replacement order automatically. Retry status with backoff and contact support if it persists.

Integration notes for production use

Treat the API like infrastructure. These notes cover the things that matter once you are running automated orders, a client panel, or a high-volume storefront.

Shared wallet balance

Direct orders, storefront orders, and API orders all draw from the same wallet balance. Fund it from any channel, use it from any channel.

Secure your key

Never expose the key, API hostname, configured provider name, or raw transport errors in client-side code or storefront messages. Proxy calls through your backend, log technical details privately, and after 20 seconds show customers neutral wording such as: Order submission is taking longer than expected. Check your orders before trying again.

Quick answers

How do I get an API key?

Log in to SMM Africa, open Account, copy your API key, and rotate it from the same place at any time.

Is the API free?

There is no extra API fee. You only pay for services you order. The same wallet funds direct orders, storefront orders, and API orders.

What are the rate limits?

Limits are action-specific and can change. Space bulk calls, batch status polls, and when the API returns 429 wait for the Retry-After interval before retrying.

How should I handle retries?

Send one unique Idempotency-Key header or idempotency_key field for each logical add order. If the request times out, retry the identical payload with the same key so a completed attempt is replayed without another charge.

Is there a webhook?

Status polling is the supported path today. Hit the status action for orders you are watching and cache the result in your own system.

Which countries are supported?

The API is available worldwide, with strong support for African markets and M-Pesa-friendly wallet funding.

API vocabulary

Terms resellers should understand

Reseller API
A server-to-server interface that lets another panel or workflow place and track SMM Africa orders from its own application.
Service catalog
The live list of service IDs, prices, limits, refill support, and cancellation support used before placing an order.
Action
The JSON field that chooses the API operation, such as balance, services, add, status, refill, or cancel.
Wallet-backed ordering
An ordering model where direct, storefront, and API orders all draw from the same funded SMM Africa wallet balance.