Reseller API
Programmatic access for approved resellers: browse your product catalog, place balance-paid orders, retrieve proxy credentials, and monitor traffic usage — all over a simple REST API.
API at a glance
- Base URL:
https://hellworld.io/openapi/v1 - Auth:
Authorization: Bearer <key_id>.<key_secret> - Format: JSON over HTTPS
- Billing: prepaid account balance (USD)
- Rate limit: per reseller — 300 requests/min (reads), 60/min (writes / order creation). Up to 5 active keys per reseller.
Overview
The Reseller API is available to approved reseller accounts. Your account manager enables specific products for your account and configures your reseller pricing. Only products enabled for your account appear in the API.
The typical integration loop:
GET /products— see your enabled products and unit pricesPOST /orders— place an order, paid from your prepaid balanceGET /orders/{order_ref}— confirm order stateGET /proxies— retrieve proxy credentials and live traffic usage- Build proxy strings using the per-product gateway formats below
Currently available products (first batch):
| product_code | Product | Category | Billing |
|---|---|---|---|
MOBILE-ET | ET Mobile | mobile | per GB |
RES-GEOFAST | Geofast | residential | per GB |
More products can be enabled for your account on request.
Authentication
Create an API key in your Reseller Portal (Dashboard → Reseller Portal → API Keys). A key has two parts:
- Key ID — starts with
dk_live_, safe to log - Key Secret — shown only once at creation; store it securely
Send both on every request, joined by a dot:
Authorization: Bearer dk_live_AbCdEf1234567890.YOUR_KEY_SECRETcurl -s https://hellworld.io/openapi/v1/products \
-H "Authorization: Bearer dk_live_AbCdEf1234567890.YOUR_KEY_SECRET"Common pitfalls
- The header value is
Bearer+ one space +key_id.key_secret— the first dot separates ID from secret. - Write requests also require
Content-Type: application/jsonand anIdempotency-Keyheader (see Idempotency). - Rate limits are per reseller (shared across all your keys): 300 requests/min for reads, 60/min for writes (order creation). On
429 RATE_LIMITED, honor theRetry-Afterresponse header. You may hold up to 5 active API keys. - Keys can be revoked and re-created in the portal at any time. Revocation is immediate.
Authentication errors:
| HTTP | code | Meaning |
|---|---|---|
| 401 | AUTH_MISSING_KEY | No Authorization header |
| 401 | AUTH_INVALID_KEY | Malformed key, unknown key ID, or secret mismatch |
| 401 | AUTH_KEY_REVOKED | Key was revoked in the portal |
| 401 | AUTH_KEY_EXPIRED | Key past its expiry date |
| 403 | DEALER_SUSPENDED | Reseller account disabled — contact your account manager |
| 429 | RATE_LIMITED | Over the per-reseller rate limit (300/min read, 60/min write); see Retry-After |
All errors share one envelope:
{ "error": { "code": "INSUFFICIENT_BALANCE", "message": "Account balance is insufficient for this order" } }Products
GET /products
Returns the products enabled for your account, with your unit prices.
curl -s https://hellworld.io/openapi/v1/products \
-H "Authorization: Bearer $KEY"{
"data": [
{
"product_code": "MOBILE-ET",
"display_name": "ET Mobile",
"category": "mobile",
"billing_unit": "gb",
"unit_price": 2.50,
"currency": "USD"
},
{
"product_code": "RES-GEOFAST",
"display_name": "Geofast",
"category": "residential",
"billing_unit": "gb",
"unit_price": 0.30,
"currency": "USD"
}
]
}unit_price is your negotiated reseller price per billing_unit. If a product shows no unit_price, the platform default price applies at order time.
Balance
GET /balance
curl -s https://hellworld.io/openapi/v1/balance \
-H "Authorization: Bearer $KEY"{ "balance": 152.40, "currency": "USD" }Topping up: log into your account on hellworld.io and use the normal balance recharge (PayPal / crypto / other supported channels), or arrange an offline payment with your account manager — it is credited to the same balance the API spends from.
Orders
POST /orders
Places an order paid from your prepaid balance. Requires an Idempotency-Key header — 1–64 ASCII characters; allowed characters are A–Z, a–z, 0–9, ., _, : and - (spaces are not allowed; a UUID fits). Anything else is rejected with 422 IDEMPOTENCY_KEY_INVALID.
curl -s -X POST https://hellworld.io/openapi/v1/orders \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7c9e6679-7425-40de-963d-1a4a6e4c1b2f" \
-d '{ "product_code": "RES-GEOFAST", "quantity": 10 }'| Field | Type | Required | Meaning |
|---|---|---|---|
product_code | string | yes | From GET /products |
quantity | number | yes | Amount in billing_unit (GB), must be > 0 |
Success — 200:
{
"order_ref": "a1b2c3d4e5f6...",
"state": "active",
"total_amount": 3.00,
"currency": "USD"
}Traffic is added to your account's proxy credential for that product (see Proxies & Usage). Repeat purchases of the same product top up the same credential.
Accepted — 202 (manual review):
{
"order_ref": "a1b2c3d4e5f6...",
"state": "manual_review",
"message": "Order was recorded but pending manual verification; do not retry this request"
}202 means you were already charged — do NOT retry
A 202 manual_review response means the balance was deducted and the order was recorded, but automatic fulfillment needs manual confirmation. Retrying with a new Idempotency-Key would place (and charge) a second order. Poll GET /orders/{order_ref} until the state becomes completed.
Errors:
| HTTP | code | Meaning |
|---|---|---|
| 400 | IDEMPOTENCY_KEY_REQUIRED | Missing Idempotency-Key header |
| 422 | IDEMPOTENCY_KEY_INVALID | Key not 1–64 chars of A-Za-z0-9._:- |
| 402 | INSUFFICIENT_BALANCE | Top up your balance first |
| 404 | PRODUCT_NOT_FOUND | No such product available for your account (unknown code, or not enabled for you) |
| 422 | NO_DEALER_PRICE | Whitelisted but no wholesale price set for this product — contact the operator |
| 409 / 422 | IDEMPOTENCY_KEY_CONFLICT | See Idempotency |
| 422 | VALIDATION_ERROR | Missing/invalid product_code or quantity |
| 422 | ORDER_REJECTED | Order could not be created |
| 500 | ORDER_STATE_UNKNOWN | Outcome unverified — do NOT retry with a new key; poll GET /orders or contact support |
| 403 | SANDBOX_KEY_DISABLED | Sandbox keys are not usable yet — create a live key |
Idempotency
Every POST /orders must carry an Idempotency-Key. Semantics:
| Situation | Result |
|---|---|
| Same key, same body, finished before | The original response is replayed (no duplicate charge) |
| Same key, same body, still processing | 409 IDEMPOTENCY_KEY_CONFLICT — wait, then repeat the same request to fetch the stored result |
| Same key, different body | 422 IDEMPOTENCY_KEY_CONFLICT — a key can only ever be used for one request body |
Request failed with a validation-type error (VALIDATION_ERROR, PRODUCT_NOT_FOUND, INSUFFICIENT_BALANCE, …) | The error is replayed for that key; these are proven side-effect-free — retry with a new key |
Request ended in ORDER_STATE_UNKNOWN | Outcome could not be verified. Never retry with a new key (possible double charge) — poll GET /orders, or contact support quoting the key |
Generate a fresh UUID per order attempt and persist it with your local order record. Keys must match [A-Za-z0-9._:-]{1,64} — max 64 characters, no spaces.
GET /orders
Paginated order history (newest first).
curl -s "https://hellworld.io/openapi/v1/orders?pageNum=1&pageSize=20" \
-H "Authorization: Bearer $KEY"{
"data": [
{
"order_ref": "a1b2c3d4e5f6...",
"order_no": "CZ20260825...",
"state": "completed",
"total_amount": 3.00,
"currency": "USD",
"created_at": "2026-08-25 10:12:03",
"products": [
{ "product_code": "RES-GEOFAST", "display_name": "Geofast", "quantity": 10 }
]
}
],
"pagination": { "page": 1, "page_size": 20, "total": 42 }
}pageSize is capped at 100.
GET /orders/
Single order lookup — use this to poll the outcome of a 202 manual_review order.
curl -s https://hellworld.io/openapi/v1/orders/a1b2c3d4e5f6 \
-H "Authorization: Bearer $KEY"Returns the same object shape as the list endpoint. 404 ORDER_NOT_FOUND if the ref does not exist or does not belong to your account.
Order states:
| state | Meaning |
|---|---|
pending | Created, not yet paid |
completed | Paid; fulfillment done (traffic credited) |
manual_review | Paid; awaiting manual fulfillment confirmation — poll until completed |
failed | Payment failed; nothing charged |
disputed | Under payment dispute |
refunded | Refunded |
unknown | Any other internal state |
Proxies & Usage
GET /proxies
Your proxy credentials with live traffic usage — one entry per enabled product you have purchased.
curl -s https://hellworld.io/openapi/v1/proxies \
-H "Authorization: Bearer $KEY"{
"data": [
{
"product_code": "RES-GEOFAST",
"display_name": "Geofast",
"billing_unit": "gb",
"username": "hw_ab12cd34",
"password": "s3cr3tPass",
"traffic": { "limit_gb": 50.0, "used_gb": 12.345, "remaining_gb": 37.655 },
"status": "active",
"expires_at": null
}
],
"usage_refresh_seconds": 60
}| Field | Meaning |
|---|---|
username / password | Credentials for the product's proxy gateway (formats below) |
traffic.limit_gb | Total GB ever purchased (topped up cumulatively) |
traffic.used_gb | GB consumed |
traffic.remaining_gb | limit - used, floored at 0 |
status | active, or suspended (e.g. traffic exhausted) |
expires_at | null for never-expiring GB balances |
Usage freshness
Usage is refreshed at most once per 60 seconds per account; within that window the last-known values are returned. Polling GET /proxies more often than once a minute yields no fresher data — a 1-minute poll loop is the recommended maximum.
Buying more of the same product (POST /orders) increases limit_gb on the same credential — you do not get a new username per order.
Proxy Formats
Build proxy strings with the username / password from GET /proxies. Country targeting and sticky sessions are encoded in the username.
ET Mobile (MOBILE-ET)
| Gateway | etmobile.hellworld.io |
| Port | 5000 |
| Protocols | HTTP / HTTPS |
Rotating (new IP per request):
etmobile.hellworld.io:5000:USERNAME-country-us:PASSWORDSticky session (same IP for the TTL):
etmobile.hellworld.io:5000:USERNAME-country-us-sid-a1b2c3d4-ttl-10m:PASSWORD-country-<cc>— optional 2-letter country code, lowercase (omit for global random)-sid-<id>— any 8-char alphanumeric session ID; samesid= same IP-ttl-<N>m— session lifetime in minutes
curl -x etmobile.hellworld.io:5000 \
-U "USERNAME-country-us-sid-a1b2c3d4-ttl-10m:PASSWORD" \
https://ipinfo.ioGeofast (RES-GEOFAST)
| Gateway | geofast.hellworld.io |
| Port (HTTP/HTTPS) | 6969 |
| Port (SOCKS5) | 9696 |
Rotating:
geofast.hellworld.io:6969:USERNAME-country-US:PASSWORDSticky session (~10 min, same session = same IP):
geofast.hellworld.io:6969:USERNAME-country-US-session-a1b2c3d4e5:PASSWORD-country-<CC>— optional 2-letter country code, uppercase (omit for global random)-session-<id>— any 10-char alphanumeric session ID
curl -x geofast.hellworld.io:6969 \
-U "USERNAME-country-US-session-a1b2c3d4e5:PASSWORD" \
https://ipinfo.ioFor SOCKS5 use port 9696 with the same username syntax.
Custom Gateway Domain (White-label)
By default your customers connect to the shared gateway hostnames above (etmobile.hellworld.io, geofast.hellworld.io). If you want them to connect through your own branded domain, add a CNAME record pointing at our gateway — no code changes, and no impact on speed.
Setup
Add one CNAME per gateway you use, pointing at our gateway hostname:
| Your record | Type | Target |
|---|---|---|
mproxy.yourbrand.com | CNAME | etmobile.hellworld.io |
proxy.yourbrand.com | CNAME | geofast.hellworld.io |
Customers then connect exactly as before, swapping only the hostname — ports and username syntax are unchanged:
proxy.yourbrand.com:6969:USERNAME-country-US-session-a1b2c3d4e5:PASSWORDRules
- Always CNAME to our
*.hellworld.iogateway hostname (e.g.geofast.hellworld.io) — never to a raw IP address. We may update the gateway's underlying IPs at any time; because your record points at our hostname, those changes are picked up automatically with no action on your side. A record pinned to a fixed IP will break when the IP changes. - DNS-only — do NOT put a CDN/proxy layer in front (e.g. Cloudflare's orange-cloud). Proxy ports are raw TCP; routing them through a CDN breaks the connection. Keep the record grey-cloud / DNS-only.
- Ports stay the same (ET Mobile
5000, Geofast HTTP6969/ SOCKS59696). - Credentials are still issued by this API — a custom domain only rebrands the hostname; the
username/passwordalways come fromGET /proxies.
Does it affect speed?
No. A custom domain is a pure DNS alias — proxy traffic still goes directly to the gateway IP and never passes through our servers or the extra hostname. The added CNAME hop only affects the first DNS lookup (a few milliseconds, then cached); actual proxy latency and throughput are identical to using the shared hostname.
Webhooks
Get notified when an API order is created, instead of polling. Configure the target URL in the Reseller Portal (Webhook section) — you receive a signing secret there.
Event: order.created — sent after an API order is recorded (active or manual_review):
{
"event_type": "order.created",
"event_id": "…",
"dealer_uuid": "…",
"created_at": "2026-08-25T10:12:03Z",
"data": {
"order_ref": "a1b2c3d4e5f6...",
"state": "active",
"total_amount": 3.00,
"currency": "USD"
}
}Signature — every delivery carries:
X-Dealer-Signature: sha256=<hex>where <hex> is HMAC-SHA256(webhook_secret, raw_request_body). Verify before trusting:
const crypto = require('crypto');
function verify(rawBody, header, secret) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret)
.update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}Delivery & retries: your endpoint must return 2xx within a few seconds. Failed deliveries are retried with exponential backoff (30s, 60s, 120s, … capped at 1h) up to 12 attempts, then dropped. Use the portal's Test webhook button to verify your endpoint (webhook.test event).
TIP
Webhooks are best-effort push. For a guaranteed view of order outcomes (especially manual_review transitions), poll GET /orders/{order_ref}.
Error Reference
| HTTP | code | Where | Meaning |
|---|---|---|---|
| 400 | IDEMPOTENCY_KEY_REQUIRED | POST /orders | Missing Idempotency-Key header |
| 422 | IDEMPOTENCY_KEY_INVALID | POST /orders | Key not 1–64 chars of A-Za-z0-9._:- |
| 401 | AUTH_MISSING_KEY | all | No Authorization header |
| 401 | AUTH_INVALID_KEY | all | Malformed/unknown key or bad secret |
| 401 | AUTH_KEY_REVOKED | all | Key revoked |
| 401 | AUTH_KEY_EXPIRED | all | Key expired |
| 402 | INSUFFICIENT_BALANCE | POST /orders | Prepaid balance too low |
| 403 | DEALER_SUSPENDED | all | Reseller account disabled |
| 404 | PRODUCT_NOT_FOUND | POST /orders | No such product available for your account |
| 422 | NO_DEALER_PRICE | POST /orders | Whitelisted but no wholesale price set — contact the operator |
| 404 | ORDER_NOT_FOUND | GET /orders/ | No such order on your account |
| 409 | IDEMPOTENCY_KEY_CONFLICT | POST /orders | Same key still processing — repeat later |
| 422 | IDEMPOTENCY_KEY_CONFLICT | POST /orders | Same key, different body |
| 422 | VALIDATION_ERROR | POST /orders | Bad product_code / quantity |
| 422 | ORDER_REJECTED | POST /orders | Order refused |
| 429 | RATE_LIMITED | all | Over per-reseller limit (300/min read, 60/min write); see Retry-After |
| 500 | INTERNAL_ERROR | any | Unexpected server error |
| 500 | ORDER_STATE_UNKNOWN | POST /orders | Outcome unverified — never retry with a new key; poll GET /orders |
| 403 | SANDBOX_KEY_DISABLED | all | Sandbox keys are disabled until the sandbox launches |
FAQ
How do I become a reseller / get products enabled?
Reseller accounts are set up by our team: your account is registered as a reseller, products are whitelisted, and your pricing is configured. Contact support or your account manager.
How do I pay?
The API spends your prepaid account balance. Top up online (log into hellworld.io → balance recharge: PayPal, crypto, and other supported channels) or arrange an offline payment with your account manager.
I placed an order but GET /proxies shows nothing.
GET /proxies only lists products that are (a) enabled for your account and (b) already purchased at least once. Right after your first order it can take a moment for the credential to be provisioned — check GET /orders/{order_ref} is completed, then retry.
Does every order create a new proxy account?
No. Purchases of the same product accumulate on one credential per product — limit_gb grows, the username stays the same.
How fresh is used_gb?
Usage is refreshed at most once per 60 seconds per account (usage_refresh_seconds in the response). Poll at most once a minute.
I got 202 manual_review — was I charged?
Yes. The order is recorded and your balance was deducted; fulfillment just needs a manual confirmation on our side. Do not retry with a new Idempotency-Key (that would be a second, separate order). Poll GET /orders/{order_ref}.
Is there a sandbox?
Not yet — sandbox keys are currently rejected on all API calls (403 SANDBOX_KEY_DISABLED). A mock sandbox is planned.
Can I get more products over the API?
Yes — any catalogued product can be enabled for your account. Ask your account manager.