Developers · Documentation

API documentation

Base URL https://rediprofiles.com/api/v1. Version 1. Changes are only ever additions, so what you build keeps working.

Quick start

Three calls: see what is for sale, buy one, read the login details. Every route lives under https://rediprofiles.com/api/v1 and ends with a slash.

# 1. what is for sale (prices in naira)
curl "https://rediprofiles.com/api/v1/services/?currency=ngn" -H "X-API-Key: $REDI_KEY"

# 2. buy one
curl -X POST "https://rediprofiles.com/api/v1/orders/" -H "X-API-Key: $REDI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"service_id": 42, "quantity": 1, "external_customer_ref": "shop-order-1001"}'

# 3. the reply already carries the account details; read it again any time
curl "https://rediprofiles.com/api/v1/orders/ODR1234567/" -H "X-API-Key: $REDI_KEY"

Authentication

Create a key on the API keys page of your dashboard and send it on every request:

X-API-Key: rp_live_...

Keys are tied to your account, so orders are paid from your wallet and get your prices, including any custom pricing on your account. Revoke a key at any time from the same page. Passwords are never used with the API.

Limits: 120 requests a minute per account and 30 order placements a minute. Over that you get 429 with a Retry-After header.

Money and currency

Every amount is returned twice, in dollars and in naira, at the same rate the website uses:

"price": "3.20",  "price_usd": "3.20",  "price_ngn": "4920.00",  "currency": "USD"

The plain field (price, charged, balance) follows the currency you choose with ?currency=ngn on any request, or a header X-Currency: NGN. Without either it is dollars. Your wallet itself is kept in dollars; the naira figure is what that is worth today.

Services

GET/services/ lists what you can buy right now (active and in stock). GET/services/{id}/ is one service. Pass the id as service_id when ordering.

{
  "id": 42, "name": "Instagram · USA · aged 1 year", "category_name": "Instagram",
  "price": "4920.00", "price_usd": "3.20", "price_ngn": "4920.00", "currency": "NGN",
  "quantity_in_stock": 17, "in_stock": true, "is_active": true,
  "final_price": "3.200000"            // older field, dollars, kept for existing integrations
}

Orders

CallWhat it does
POST/orders/Buys from your wallet. The reply carries the login details when stock is there.
GET/orders/Your orders, newest first, from every channel (dashboard and API).
GET/orders/{order_id}/One order, with its account details.
GET/api-orders/Only the orders placed through the API.

Placing an order

POST https://rediprofiles.com/api/v1/orders/
{"service_id": 42, "quantity": 1, "external_customer_ref": "shop-order-1001"}

external_customer_ref is optional, up to 128 characters: your own order or customer id. It is stored on the order and you can filter by it later with ?external_customer_ref=shop-order-1001.

What comes back

{
  "order_id": "ODR1234567", "service": 42, "service_name": "Instagram · USA · aged 1 year",
  "quantity": 1, "status": "completed", "purchase_date": "2026-10-22T14:42:10Z",
  "charged": "4920.00", "charged_usd": "3.20", "charged_ngn": "4920.00", "currency": "NGN",
  "account_data": [ { "sqid": "ACC9F2K1", "account_details": "username: ... password: ..." } ],
  "source": "api", "external_customer_ref": "shop-order-1001",
  "charged_price": "3.200000"          // older field, dollars
}

Order status

statusMeaning
completedDelivered. account_data holds the login details.
pending, processingPaid, being fulfilled. Read the order again in a few seconds.
pre_orderedPaid while out of stock; delivered automatically when stock arrives. Keep reading the order, or watch your email.
failedCould not be fulfilled. Money is returned to the wallet.
refundedRefunded after a support review.
Place each order once. If your request times out, read /orders/?external_customer_ref=... before retrying, so a slow network cannot buy twice.

Pre-orders

Out-of-stock services are hidden from /services/ unless you ask: /services/?include=all lists them with "pre_order": true. To buy one, send "allow_preorder": true with the order. You pay now, the order sits at pre_ordered, and it is delivered automatically when stock arrives (you also get an email). Without that flag a short stock answers 400 with the number available.

Other list filters: ?category=<id> (a platform or sub-category and everything under it), ?search=instagram, and on orders ?status=completed, ?since=2026-10-01.

Safe retries

Send a unique Idempotency-Key header with any POST that spends money (orders, SMS numbers, proxies). If your request times out and you send it again with the same key, you get the original order back (200) instead of a second purchase. Keys are per account; any unique string up to 128 characters works, your own order id is a good choice.

curl -X POST "https://rediprofiles.com/api/v1/orders/" -H "X-API-Key: $REDI_KEY" \
  -H "Idempotency-Key: shop-order-1001" -H "Content-Type: application/json" \
  -d '{"service_id": 42, "quantity": 1, "external_customer_ref": "shop-order-1001"}'

Categories

GET/categories/ is the catalogue's shape: every platform with its sub-categories and how many services each has in stock. Use the ids with /services/?category=.

[ { "id": 4, "name": "Instagram", "services_in_stock": 12, "services_total": 15,
    "subcategories": [ { "id": 9, "name": "Aged accounts", "services_in_stock": 7, "services_total": 8 }, ... ] }, ... ]

SMS verification

Rent a number, receive the code, done. Numbers are paid from the wallet and refunded automatically if no code arrives before the timer ends.

CallWhat it does
GET/sms/servers/The two servers with their timer options, and the country list.
GET/sms/services/?server=route1&country=UKServices on offer for that server and country, with price and availability.
POST/sms/orders/Rents a number. Body: {"service": "whatsapp", "country": "UK", "server": "route1", "timer_minutes": 15, "external_customer_ref": "..."}
GET/sms/orders/{id}/Checks with the provider and returns the code once it has arrived. Poll every 5 to 10 seconds.
POST/sms/orders/{id}/cancel/Cancels a waiting number and refunds it.
GET/sms/orders/Your SMS orders (?status=WAITING, ?external_customer_ref=).
{ "id": 8812, "order_id": "ODR7781234", "service": "WhatsApp", "country": "UK", "server": "route1",
  "number": "+447700900123", "status": "WAITING", "sms_code": null, "timer_minutes": 15,
  "expires_at": "2026-10-22T15:12:00Z", "seconds_remaining": 842,
  "charged": "1200.00", "charged_usd": "0.78", "charged_ngn": "1200.00", "currency": "NGN" }

Status values: WAITING (number live, no code yet), OK (sms_code is filled), CANCELLED and EXPIRED (refunded). Server 2 supports rentals of 3 to 25 days through timer_minutes; server 1 is a 15-minute window.

Proxies

Datacenter proxies are fixed IPs for a period; residential and mobile are data packages that rotate through real IPs in a country you choose.

CallWhat it does
GET/proxies/catalogue/Types, categories (with group, target website, countries), data packages, periods, max per order.
GET/proxies/quote/?category=12&country=us&period=30&count=2Price and availability for datacenter proxies. For a data package: ?package=5&country=gb.
POST/proxies/orders/Buys. Datacenter: {"category": 12, "country": "us", "period": 30, "count": 2, "protocol": "http"}. Package: {"package": 5, "country": "gb"}. Add external_customer_ref freely.
GET/proxies/orders/{id}/The order with its lines (ip:port:user:pass), expiry and days left.
GET / POST/proxies/orders/{id}/renew/GET quotes a renewal (?period=30), POST renews with {"period": 30}. Same IPs, more days.
POST/proxies/orders/{id}/location/Moves a data package's pool: {"country": "de"}. Returns the new lines.
POST/proxies/orders/{id}/protocol/Switches datacenter proxies between http and socks.
GET/proxies/orders/Your proxy orders without the lines.
{ "id": 31, "order_id": "ODR7791001", "type": "server", "category": "Instagram", "group": "Social media",
  "country": "us", "period_days": 30, "count": 2, "protocol": "http", "status": "active",
  "expires_at": "2026-11-21T10:02:00Z", "days_left": 29,
  "charged": "9840.00", "charged_usd": "6.40", "charged_ngn": "9840.00", "currency": "NGN",
  "lines": ["203.0.113.10:30001:user1:pass1", "203.0.113.11:30002:user2:pass2"] }

Webhooks

Instead of polling, give us an HTTPS URL and we POST to it when something happens. Up to 5 webhooks per account.

CallWhat it does
GET/webhooks/Your webhooks and the list of events.
POST/webhooks/{"url": "https://yourshop.com/redi-hook", "events": ["order.completed", "sms.code_received"]}. Leave events out for everything. The reply contains the secret once.
PATCH/webhooks/{id}/Change url, events, is_active; {"rotate_secret": true} issues a new secret.
POST/webhooks/{id}/test/Sends a ping event.
GET/webhooks/{id}/deliveries/The last 50 deliveries with attempts and status codes.
DELETE/webhooks/{id}/Removes it.

Events

  • order.completed delivered account order (payload is the order, as in /orders/{id}/, login details included)
  • order.pre_order_filled a pre-order got its stock, order.failed, order.refunded
  • sms.code_received the code arrived (payload as in /sms/orders/{id}/, sms_code filled), sms.expired, sms.cancelled
  • proxy.expiring three days before a proxy order expires
  • ping your test

What we send

POST https://yourshop.com/redi-hook
Content-Type: application/json
X-Redi-Event: order.completed
X-Redi-Delivery: 4812
X-Redi-Signature: sha256=3f1c…

{"event": "order.completed", "created_at": "2026-10-22T14:42:11Z", "data": { ...the order... }}

Verify the signature: HMAC-SHA256 of the raw request body with your webhook secret, hex, prefixed sha256=. Answer with any 2xx within 10 seconds. Anything else is retried after 1, 5, 30 minutes, then 2 and 6 hours; a URL that keeps failing is switched off and shows in /webhooks/. The same delivery id can arrive twice, so treat it as idempotent.

# Python
import hmac, hashlib
expected = "sha256=" + hmac.new(SECRET.encode(), raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, request.headers["X-Redi-Signature"])

Account and balance

GET/profiles/me/ gives your username, email and wallet. GET/profiles/balance/ is the quick version:

{"username": "yourshop", "balance": "48200.00", "balance_usd": "31.35", "balance_ngn": "48200.00", "currency": "NGN"}

Keys, scopes and IP allow-list

Keys are made on the dashboard's API keys page, or with a full-access key: POST/keys/ {"name": "my-shop", "scope": "orders", "allowed_ips": "102.89.1.5"}. The reply shows the key once.

scopeMay do
readEvery GET: catalogue, orders, balance, SMS and proxy status.
ordersRead, plus buying: POST /orders/, /sms/orders/, /proxies/orders/ and their renew, cancel, location and protocol calls.
fullEverything, including keys and webhooks. Keys made before scopes existed are full.

allowed_ips is optional: IPs or CIDR ranges, one per line. A request from anywhere else gets 403. Other calls: GET/keys/, GET/keys/{id}/usage/ (requests per day for a week), POST/keys/{id}/rotate/ (new secret, same key id, old value stops at once), PATCH/keys/{id}/ with {"is_active": false}, DELETE/keys/{id}/.

Paging and filters

Lists come back as a plain array. Add ?page=1 (and page_size, up to 200, default 50) to get pages instead:

{"count": 1320, "next": "...?page=2", "previous": null, "results": [ ... ]}

Errors

CodeWhy
401No X-API-Key header, or the key was revoked.
400Bad request: the body explains. Common: Insufficient stock, Insufficient wallet balance, missing service_id.
404No such service or order, or a route without its trailing slash.
429Rate limit. Wait the seconds given in Retry-After.

OpenAPI

Machine-readable schema at https://rediprofiles.com/api/v1/schema/, browsable at Swagger UI and ReDoc. A ready-made Postman collection covers every call above. What changed and when: changelog.

Log inCreate account