# NumberCodes API — Complete Technical Specification > NumberCodes API is a comprehensive, production-grade programmatic gateway for automated virtual phone numbers and SMS code verification. Synchronized with the Telegram bot [@NumberCodes_bot](https://t.me/NumberCodes_bot) on a unified USD balance. --- ## 1. System Architecture & Protocols NumberCodes supports two interface standards: 1. **Modern REST JSON API (`/api/v1/*`)**: Designed for custom services, microservices, and scripts. Uses standard HTTP methods, JSON request/response bodies, HTTP status codes, and optional Webhooks. 2. **SMS-Activate / SMSHub Compatible Protocol (`/stubs/handler_api.php`)**: Drop-in replacement for desktop software, automation tools, and legacy clients (TG-Expert, Telegram Prime, multi-threaded registrars). 3. **Webhooks (`callback_url`)**: High-throughput real-time HTTP POST push notifications when an SMS code is received or an order is cancelled, eliminating aggressive polling. --- ## 2. Authentication & Security All private endpoints require authentication using a personal API key. API keys start with the prefix `ak_live_` (e.g. `ak_live_3f8a02cb891e4a77...`). ### Obtaining an API Key 1. Open [@NumberCodes_bot](https://t.me/NumberCodes_bot) in Telegram. 2. Go to **"Profile"** or send `/api`. 3. Tap **"🔑 API разработчикам"**. 4. Copy your live secret token. ### Providing the API Key in Requests Supported authentication mechanisms: - **`X-API-Key` HTTP Header (Recommended for REST)**: `X-API-Key: ak_live_...` - **`Authorization: Bearer` HTTP Header**: `Authorization: Bearer ak_live_...` - **Query Parameter `api_key` (Required for SMS-Activate protocol)**: `?api_key=ak_live_...` ### Security Standards - API keys are scoped to your user account and never displayed in public logs or error messages. - Any attempt to access resources belonging to another user returns `404 Not Found`. - Keys can be instantly revoked and regenerated in [@NumberCodes_bot](https://t.me/NumberCodes_bot). --- ## 3. Rate Limits, Concurrency & Idempotency - **Rate Limiting**: Sliding window limit of **120 requests per minute** per API key. When exceeded, the server returns HTTP `429 Too Many Requests`. - **Concurrency Locks**: Purchase operations are serialized per user with `asyncio.Lock` to eliminate race conditions and double charges on your balance. - **Idempotency Protection**: Clients can pass an `Idempotency-Key: ` header (or `idempotency_key` JSON field) in `POST /api/v1/orders/buy` and `POST /api/v1/orders/buy-bulk`. - If a duplicate request arrives while the original is still processing, the server returns HTTP `409 Conflict`. - If the original request already succeeded within the last 5 minutes, the server returns the cached response without debiting the balance a second time. - **Balance Precision**: All user balances and charges are in USD, calculated with `round(amount, 2)` or `round(amount, 4)` and executed atomically inside database transactions. --- ## 4. Order Lifecycle & Automatic Refunds 1. **Creation (`waiting_sms`)**: A number is rented and enters the `waiting_sms` state for **20 minutes**. 2. **SMS Reception (`received`)**: Upstream carriers deliver the SMS. The code is saved to `sms_code`, pushed via Telegram notification (if enabled), and sent to `callback_url` via HTTP POST. 3. **Cancellation & Refund (`cancelled`)**: - The user can explicitly cancel the order via `POST /api/v1/orders/{id}/cancel` as long as no SMS code has been received. 100% of the purchase price is refunded to the balance immediately. - If 20 minutes expire and no SMS code has arrived, the system automatically cancels the upstream order, refunds 100% of the funds to the balance, and triggers a cancellation event. - Once an SMS code has been delivered, cancellation is locked (`400 Bad Request`). 4. **Completion (`completed`)**: The client marks the session as finished via `POST /api/v1/orders/{id}/finish`. --- ## 5. REST API v1 Reference Base URL: `http://127.0.0.1:8000` (or production host) ### 5.1 GET /api/v1/balance Returns current user balance in USD. **Headers**: - `X-API-Key: ak_live_...` **Response `200 OK`**: ```json { "status": "success", "balance": 25.50, "currency": "USD", "user_id": 888002 } ``` **cURL**: ```bash curl -X GET "https://api.numbercodes.bot/api/v1/balance" \ -H "X-API-Key: ak_live_your_key_here" ``` --- ### 5.2 GET /api/v1/services Returns catalog of active services with live prices in USD. **Headers**: - `X-API-Key: ak_live_...` **Response `200 OK`**: ```json { "status": "success", "total": 4, "services": [ { "code": "tg", "name": "Telegram", "price": 0.85, "currency": "USD" }, { "code": "wa", "name": "WhatsApp", "price": 0.45, "currency": "USD" }, { "code": "vk", "name": "VK / Mail.ru", "price": 0.35, "currency": "USD" }, { "code": "ya", "name": "Яндекс / Go", "price": 0.40, "currency": "USD" } ] } ``` --- ### 5.3 GET /api/v1/countries Returns list of supported countries, their country IDs, and popularity flags. **Headers**: - `X-API-Key: ak_live_...` **Response `200 OK`**: ```json { "status": "success", "total": 3, "countries": [ { "id": "0", "name_ru": "Россия", "name_en": "Russia", "is_popular": true }, { "id": "1", "name_ru": "Украина", "name_en": "Ukraine", "is_popular": true }, { "id": "2", "name_ru": "Казахстан", "name_en": "Kazakhstan", "is_popular": false } ] } ``` --- ### 5.4 GET /api/v1/availability Real-time availability and prices for specific services and countries. **Query Parameters**: - `service` (optional): Service code (e.g. `tg`, `wa`). - `country` (optional): Country ID (e.g. `0`). **Response `200 OK`**: ```json { "status": "success", "total": 1, "items": [ { "service": "tg", "country": "0", "count": 1420, "price": 0.85, "currency": "USD" } ] } ``` --- ### 5.5 POST /api/v1/orders/buy Purchase a single phone number for SMS activation. **Headers**: - `X-API-Key: ak_live_...` - `Content-Type: application/json` - `Idempotency-Key: ` (optional) **Body**: ```json { "service": "tg", "country_id": "0", "callback_url": "https://mysite.com/webhook", "idempotency_key": "uuid-v4-order-123" } ``` **Response `201 Created`**: ```json { "status": "success", "order": { "order_id": 54101, "phone": "+79991234567", "service": "tg", "service_name": "Telegram", "country_id": "0", "price": 0.85, "callback_url": "https://mysite.com/webhook", "expires_at": "2026-10-13T15:20:00Z" } } ``` **Errors**: - `400 Bad Request`: `{"status": "error", "code": 400, "detail": "Invalid service specified."}` - `402 Payment Required`: `{"status": "error", "code": 402, "detail": "Insufficient funds on your bot balance."}` - `409 Conflict`: `{"status": "error", "code": 409, "detail": "A purchase request with this Idempotency-Key is currently in progress."}` - `503 Service Unavailable`: `{"status": "error", "code": 503, "detail": "No phone numbers currently available for this service/country."}` **Python Example**: ```python import requests url = "https://api.numbercodes.bot/api/v1/orders/buy" headers = { "X-API-Key": "ak_live_your_key_here", "Idempotency-Key": "custom-uuid-12345" } payload = { "service": "tg", "country_id": "0", "callback_url": "https://mysite.com/webhook" } response = requests.post(url, json=payload, headers=headers) print(response.status_code, response.json()) ``` --- ### 5.6 POST /api/v1/orders/buy-bulk Batch purchase 1 to 20 phone numbers at once. **Headers**: - `X-API-Key: ak_live_...` - `Content-Type: application/json` **Body**: ```json { "service": "tg", "country_id": "0", "quantity": 3, "callback_url": "https://mysite.com/webhook" } ``` **Response `201 Created`**: ```json { "status": "success", "requested": 3, "total_purchased": 3, "orders": [ { "order_id": 54102, "phone": "+79991112233", "service": "tg", "price": 0.85 }, { "order_id": 54103, "phone": "+79992223344", "service": "tg", "price": 0.85 }, { "order_id": 54104, "phone": "+79993334455", "service": "tg", "price": 0.85 } ] } ``` --- ### 5.7 GET /api/v1/orders Retrieve paginated list of user orders. **Query Parameters**: - `status`: Filter by status (`all`, `waiting_sms`, `received`, `cancelled`). Default `all`. - `page`: Page number (integer >= 1, default 1). - `limit`: Number of orders per page (1..100, default 20). **Response `200 OK`**: ```json { "status": "success", "page": 1, "limit": 20, "total": 48, "orders": [ { "id": 54101, "phone": "+79991234567", "service": "tg", "price": 0.85, "status": "received", "sms_code": "49201", "callback_url": "https://mysite.com/webhook", "created_at": "2026-10-13 14:30:00", "expires_at": "2026-10-13 14:50:00" } ] } ``` --- ### 5.8 GET /api/v1/orders/{order_id} Check order status and read received SMS code. **Path Parameters**: - `order_id`: Integer ID of the order. **Response `200 OK`**: ```json { "status": "success", "order": { "id": 54101, "phone": "+79991234567", "service": "tg", "service_name": "Telegram", "country_id": "0", "price": 0.85, "status": "received", "sms_code": "49201", "callback_url": "https://mysite.com/webhook", "created_at": "2026-10-13 14:30:00", "expires_at": "2026-10-13 14:50:00" } } ``` --- ### 5.9 POST /api/v1/orders/{order_id}/cancel Cancel number and instantly refund purchase price to balance. **Response `200 OK`**: ```json { "status": "success", "message": "Order #54101 cancelled successfully. $0.85 refunded to your bot balance.", "order_id": 54101, "refunded_amount": 0.85 } ``` **Errors**: - `400 Bad Request`: Cannot cancel after SMS code is already received. - `404 Not Found`: Order does not exist or does not belong to your account. --- ### 5.10 POST /api/v1/orders/{order_id}/finish Mark order as completed after successful registration. **Response `200 OK`**: ```json { "status": "success", "message": "Order #54101 completed successfully." } ``` --- ### 5.11 POST /api/v1/orders/{order_id}/retry Request additional/second SMS code on the same number. **Response `200 OK`**: ```json { "status": "success", "message": "Ready to receive additional SMS code.", "order_id": 54101 } ``` --- ### 5.12 POST /api/v1/orders/{order_id}/outbound-call Initiate flash-call verification from the rented number to a target phone. **Body**: ```json { "target_phone": "+79991234567" } ``` **Response `200 OK`**: ```json { "status": "success", "order_id": 54101, "response": { "call_id": "call_991823", "status": "initiated" } } ``` --- ### 5.13 PUT /api/v1/orders/{order_id}/webhook Attach or update the Webhook callback URL for an order. **Body**: ```json { "callback_url": "https://mysite.com/webhook" } ``` **Response `200 OK`**: ```json { "status": "success", "order_id": 54101, "callback_url": "https://mysite.com/webhook" } ``` --- ### 5.14 GET /api/v1/transactions Retrieve user financial history (purchases and refunds). **Query Parameters**: - `page`: Page number (default 1). - `limit`: Number of items per page (default 20). **Response `200 OK`**: ```json { "status": "success", "page": 1, "limit": 20, "total": 15, "transactions": [ { "id": 102, "amount": -0.85, "type": "purchase", "comment": "API покупка: Telegram (Россия)", "created_at": "2026-10-13 14:30:00" }, { "id": 103, "amount": 0.85, "type": "refund", "comment": "Возврат средств за отмену заказа #54101", "created_at": "2026-10-13 14:35:00" } ] } ``` --- ## 6. Webhooks Specification When `callback_url` is provided during order creation or updated via `PUT /api/v1/orders/{id}/webhook`, the NumberCodes engine pushes HTTP POST requests immediately upon state changes. ### 6.1 Event: `sms_received` Triggered when an incoming SMS code is verified: ```json { "event": "sms_received", "timestamp": "2026-10-13T14:31:05Z", "data": { "order_id": 54101, "phone": "+79991234567", "service": "Telegram", "sms_code": "49201", "status": "received" } } ``` ### 6.2 Event: `order_cancelled` Triggered when an order times out (20 minutes) or is cancelled by provider: ```json { "event": "order_cancelled", "timestamp": "2026-10-13T14:50:00Z", "data": { "order_id": 54101, "phone": "+79991234567", "service": "Telegram", "refunded_amount": 0.85, "reason": "Истёк срок ожидания (20 минут)" } } ``` ### Requirements for Webhook Handlers - The receiving endpoint must return HTTP `200 OK` within **6 seconds**. - Content-Type of delivery is `application/json; charset=utf-8`. --- ## 7. SMS-Activate / SMSHub Compatible Protocol Specification Endpoint: `/stubs/handler_api.php` Supported HTTP Methods: `GET` and `POST` Rate Limiting: 240 requests/minute per API key. Returns HTTP 429 `ERROR_RATE_LIMIT` upon violation. --- ### 7.1. Method Comparison & Transport Differences | Characteristic | Method GET | Method POST | | :--- | :--- | :--- | | **Parameter Transport** | URL Query String: `?api_key=...&action=...` | Request Body: `application/x-www-form-urlencoded` or `multipart/form-data` | | **Header Support** | Standard HTTP headers | Supports `X-API-Key: ak_live_...` in header instead of passing `api_key` in body | | **Security & Privacy** | Key visible in URLs, browser history, and proxy logs | Key hidden in encrypted HTTP body (recommended for production scripts) | | **Recommended Usage** | Fast status polling (`getStatus`), balance queries (`getBalance`), catalogs (`getPrices`, `getCountries`) | Ordering numbers (`getNumber`), updating statuses (`setStatus`), automated registrars | --- ### 7.2. Standard GET Method Specification Parameters must be appended to the URL as query parameters: `GET /stubs/handler_api.php?api_key=&action=&=` #### Supported Actions (GET): | Action | Query Parameters | Response Format | Description & Example Request | | :--- | :--- | :--- | :--- | | `getBalance` | `api_key` | Plain Text: `ACCESS_BALANCE:` | Check account USD balance.
`/stubs/handler_api.php?api_key=KEY&action=getBalance` | | `getPrices` | `api_key`, `country` (opt, default 0), `service` (opt) | JSON: `{ "0": { "": { "": } } }` | Service pricing and stock catalog.
`/stubs/handler_api.php?api_key=KEY&action=getPrices&country=0&service=imo` | | `getNumbersStatus` | `api_key`, `country` (opt) | JSON: `{ "": , "_": }` | Real-time available count per service.
`/stubs/handler_api.php?api_key=KEY&action=getNumbersStatus&country=0` | | `getCountries` | `api_key` | JSON: `{ "0": { "id": 0, "rus": "Россия", ... } }` | Full list of 195 supported countries.
`/stubs/handler_api.php?api_key=KEY&action=getCountries` | | `getOperators` | `api_key`, `country` (opt, default 0) | JSON: `{ "status": "success", "operators": { ... } }` | Mobile carriers catalog by country.
`/stubs/handler_api.php?api_key=KEY&action=getOperators&country=0` | | `getCurrentActivations` | `api_key` | JSON: `{ "status": "success", "activeActivations": [ ... ] }` | List of currently active orders waiting for SMS.
`/stubs/handler_api.php?api_key=KEY&action=getCurrentActivations` | | `getNumber` | `api_key`, `service`, `country` (opt, default 0) | Plain Text: `ACCESS_NUMBER::` | Buy a virtual number for a service.
`/stubs/handler_api.php?api_key=KEY&action=getNumber&service=im&country=0` | | `getNumberV2` | `api_key`, `service`, `country` (opt, default 0) | JSON: `{ "activationId": ..., "phoneNumber": ... }` | Buy number with structured JSON response.
`/stubs/handler_api.php?api_key=KEY&action=getNumberV2&service=im&country=0` | | `getStatus` | `api_key`, `id` | Plain Text: `STATUS_WAIT_CODE` \| `STATUS_OK:` \| `STATUS_CANCEL` | Poll SMS verification code status.
`/stubs/handler_api.php?api_key=KEY&action=getStatus&id=10421` | | `setStatus` | `api_key`, `id`, `status` (`1`, `3`, `6`, `8`) | Plain Text: `ACCESS_READY` \| `ACCESS_RETRY_GET` \| `ACCESS_ACTIVATION` \| `ACCESS_CANCEL` | Change order status (6=finish, 8=cancel).
`/stubs/handler_api.php?api_key=KEY&action=setStatus&id=10421&status=6` | --- ### 7.3. Standard POST Method Specification Parameters must be submitted in the request body as `application/x-www-form-urlencoded` or `multipart/form-data`: `POST /stubs/handler_api.php` `Body: api_key=&action=&=` #### Supported Actions (POST): | Action | Form Body Parameters | Response Format | Use Case & Code Example | | :--- | :--- | :--- | :--- | | `getBalance` | `api_key`, `action=getBalance` | `ACCESS_BALANCE:` | Check account balance securely | | `getPrices` | `api_key`, `action=getPrices`, `country=0`, `service=tg` | JSON | Fetch current prices map | | `getNumbersStatus` | `api_key`, `action=getNumbersStatus`, `country=0` | JSON | Check inventory quantity | | `getCountries` | `api_key`, `action=getCountries` | JSON | Load countries dictionary | | `getOperators` | `api_key`, `action=getOperators`, `country=0` | JSON | Retrieve available mobile operators | | `getCurrentActivations`| `api_key`, `action=getCurrentActivations` | JSON | Resume active activation sessions | | `getNumber` | `api_key`, `action=getNumber`, `service=tg`, `country=0` | `ACCESS_NUMBER::` | Secure order placement | | `getNumberV2` | `api_key`, `action=getNumberV2`, `service=tg`, `country=0` | JSON | JSON-formatted order placement | | `getStatus` | `api_key`, `action=getStatus`, `id=10421` | `STATUS_WAIT_CODE` \| `STATUS_OK:` | Poll SMS code reception | | `setStatus` | `api_key`, `action=setStatus`, `id=10421`, `status=6` (or `8`) | `ACCESS_ACTIVATION` \| `ACCESS_CANCEL` | Complete or refund order | #### Example: Python `requests` POST Integration ```python import time import requests API_URL = "https://numbercodes.shop/stubs/handler_api.php" API_KEY = "ak_live_xxx" # 1. Purchase number for Telegram (Russia) res = requests.post(API_URL, data={ "api_key": API_KEY, "action": "getNumber", "service": "tg", "country": "0" }) # Expected output: ACCESS_NUMBER:10421:79325048419 if not res.text.startswith("ACCESS_NUMBER"): raise SystemExit(f"Order failed: {res.text}") _, order_id, phone = res.text.split(":") print(f"Allocated phone number: +{phone} (Activation ID: {order_id})") # 2. Poll for incoming SMS code (every 3 seconds) for attempt in range(40): time.sleep(3) status_resp = requests.post(API_URL, data={ "api_key": API_KEY, "action": "getStatus", "id": order_id }) txt = status_resp.text if txt.startswith("STATUS_OK:"): code = txt.split(":")[1] print(f"SMS code received: {code}") # Complete activation requests.post(API_URL, data={"api_key": API_KEY, "action": "setStatus", "id": order_id, "status": "6"}) break elif txt == "STATUS_WAIT_CODE": print(f"Waiting for SMS... (attempt {attempt+1}/40)") else: print(f"Unexpected status: {txt}") break ``` --- ### 7.4. Error Codes & Response Statuses Reference | Response String | HTTP Status | Type | Meaning & Action Required | | :--- | :--- | :--- | :--- | | `ACCESS_BALANCE:` | 200 OK | Success | Current balance in USD (e.g. `ACCESS_BALANCE:7.00`). | | `ACCESS_NUMBER::`| 200 OK | Success | Number acquired. Extract `` and clean `` without `+`. | | `STATUS_WAIT_CODE` | 200 OK | Pending | SMS code not yet received. Continue polling every 2–4 seconds. | | `STATUS_OK:` | 200 OK | Success | SMS code received (e.g. `STATUS_OK:18492`). Proceed with login. | | `STATUS_CANCEL` | 200 OK | Info | Activation expired or was canceled. Funds returned to balance. | | `ACCESS_READY` | 200 OK | Success | Gateway confirmed readiness to receive SMS (`status=1`). | | `ACCESS_RETRY_GET` | 200 OK | Success | Gateway prepared to receive additional SMS code (`status=3`). | | `ACCESS_ACTIVATION` | 200 OK | Success | Activation completed successfully (`status=6`). | | `ACCESS_CANCEL` | 200 OK | Success | Activation cancelled and refunded (`status=8`). | | `BAD_KEY` | 200 OK | Error | Invalid or inactive API key. Check key in Telegram bot `/api`. | | `BAD_SERVICE` | 200 OK | Error | Invalid service code. Check aliases (`im`/`imo`, `lf`/`tiktok`, `sg`/`ozon`, `uu`/`wb`). | | `NO_BALANCE` | 200 OK | Error | Insufficient bot balance. Top up USD wallet in [@NumberCodes_bot](https://t.me/NumberCodes_bot). | | `NO_NUMBERS` | 200 OK | Error | Temporary carrier stock exhaustion for this country/service. Retry shortly. | | `NO_ACTIVATION` | 200 OK | Error | Specified activation `id` was not found or belongs to another user account. | | `BAD_STATUS` | 200 OK | Error | Invalid status transition (e.g. attempting to cancel `status=8` after receiving SMS). | | `BAD_ACTION` | 200 OK | Error | Unsupported `action` parameter. | | `ERROR_RATE_LIMIT` | 429 Too Many | Error | Rate limit exceeded (>240 requests/min). Introduce 2-3s delay in polling loops. | --- ## 8. Troubleshooting & Common Integration Issues | Symptom / Code | Root Cause | Recommended Fix | | --- | --- | --- | | `401 Unauthorized` | Invalid, missing, or revoked API key | Pass `X-API-Key: ak_live_...` header; verify key in bot under `/api`. | | `402 Payment Required` | Bot balance is lower than service price | Top up your USD balance in [@NumberCodes_bot](https://t.me/NumberCodes_bot). | | `404 Not Found` | Order ID does not exist or belongs to another user | Verify order ID from creation response; ensure same API key is used. | | `409 Conflict` | Parallel request with duplicate `Idempotency-Key` | Await response from first request before re-submitting. | | `429 Too Many Requests` | Exceeded 120 req/minute rate limit | Back off and implement retry with exponential jitter. | | `503 Service Unavailable` | Carrier runs out of numbers for country/service | Retry after a few seconds or select an alternative country. | | `EARLY_CANCEL_DENIED` | Attempted to cancel after receiving SMS | Orders with received SMS codes are final and cannot be refunded. | --- ## 9. Official Resources - Web Documentation: `/docs` - Interactive Sandbox: `/docs#sandbox` - OpenAPI Schema: `/openapi.json` - LLM Index: `/llms.txt` - LLM Full Docs: `/llms-full.txt` - Telegram Bot: [@NumberCodes_bot](https://t.me/NumberCodes_bot)