
In short
Since October 28, 2026, requests to hero-sms.com fail: the registrar froze the domain, so its address no longer resolves in DNS. Your code is fine, but automated purchases have stopped. To get them running again, move the integration to the Hush SMS REST API: getNumber → POST /v1/numbers, getStatus → GET /v1/numbers/{id}, setStatus 8 and 6 → cancel and finish the order. Below are the mapping table and an adapter that returns the familiar responses such as ACCESS_NUMBER, so the rest of your code stays untouched.
This guide is for anyone whose bot, script or service buys numbers through the HeroSMS API. If you only need one code, buy a number in the bot @HushNumBot or on the website, and read what happened to the domain in HeroSMS not working.
How to tell the problem is hero-sms.com
If your integration stopped buying numbers on the evening of October 28 and your logs show one of these errors, the problem is not your server or your key:
| Where | Error in the log |
|---|---|
| curl | curl: (6) Could not resolve host: hero-sms.com |
| Python, requests (Linux) | Failed to resolve 'hero-sms.com' ([Errno -2] Name or service not known) |
| Node.js | getaddrinfo ENOTFOUND hero-sms.com |
| PHP, Guzzle | cURL error 6: Could not resolve host: hero-sms.com |
| Go | dial tcp: lookup hero-sms.com: no such host |
If your requests still go through, your server remembers the old IP from its DNS cache. Once the record expires, they will fail the same way, so connect a backup channel now rather than at the moment of the outage.
What changes when you move
- REST and JSON instead of one
handler_api.phpaddress with anactionparameter and text responses; - the key goes in the
Authorization: Bearerheader, not in theapi_keyparameter; - a purchase needs an
Idempotency-Keyheader: repeating it with the same key returns the same order instead of buying a second number; - prices and the balance in API responses are in rubles;
- codes can arrive through webhooks, not only by polling the status.
API base URL: https://api.hush-sms.com. The full reference with examples: docs.hush-sms.com.
What replaces the HeroSMS methods
| HeroSMS, handler_api.php | Hush SMS API |
|---|---|
getBalance | GET /v1/me: profile and balance |
getNumber, getNumberV2 | POST /v1/numbers with a {"service", "country"} body and an Idempotency-Key header |
getStatus, getStatusV2 | GET /v1/numbers/{id}: status and codes in the sms_codes field; every SMS of the order: GET /v1/orders/{id}/sms |
setStatus, status=8 | POST /v1/numbers/{id}/cancel: cancel the order |
setStatus, status=6 | POST /v1/orders/{id}/finish: finish the order |
setStatus, status=3 | No separate method: request another code in the bot or in the web account |
getActiveActivations | GET /v1/numbers: active orders |
getPrices, getServicesList, getCountries | GET /v1/services, GET /v1/services/{code}/countries, GET /v1/services/{code}/offers |
Do not copy service codes and country IDs from the old integration blindly: take them from GET /v1/services and GET /v1/services/{code}/countries. The country ID in a purchase request is numeric, as before.
Responses and statuses
| HeroSMS response | What Hush SMS returns |
|---|---|
ACCESS_NUMBER:id:number | 201 and an order object with id, phone_number, status and price |
NO_NUMBERS | 404 with the not_available code |
NO_BALANCE | 402 with the insufficient_funds code |
BAD_KEY | 401: the key is missing or invalid |
STATUS_WAIT_CODE | status: pending: the number is issued, waiting for the SMS |
STATUS_OK:code | status: sms_received, the code is in sms_codes |
STATUS_CANCEL | status: canceled, then refunded: the money is back on the balance |
EARLY_CANCEL_DENIED | 409 with the too_early code; details.seconds_left shows the seconds left |
An adapter: old responses, new API
If HeroSMS calls are spread across your code, the simplest fix is to replace one module. This Python adapter answers with the same strings as handler_api.php, so the rest of your code can stay as it is. It only needs the requests library.
import uuid
import requests
API = "https://api.hush-sms.com/v1"
class HushAdapter:
"""Responses like handler_api.php, the Hush SMS REST API inside."""
def __init__(self, api_key):
self.http = requests.Session()
self.http.headers["Authorization"] = f"Bearer {api_key}"
def _call(self, method, path, **kwargs):
response = self.http.request(method, API + path, timeout=30, **kwargs)
data = response.json() if response.content else {}
failed = response.status_code >= 400
error = data.get("error", {}).get("code") if failed else None
return error, data
def get_number(self, service, country, idempotency_key=None):
# Keep the key until you get an answer: retrying with it never buys twice.
key = idempotency_key or str(uuid.uuid4())
body = {"service": service, "country": int(country)}
headers = {"Idempotency-Key": key}
error, data = self._call("POST", "/numbers", json=body, headers=headers)
if error == "not_available":
return "NO_NUMBERS"
if error == "insufficient_funds":
return "NO_BALANCE"
if error:
return f"ERROR:{error}"
order = data["order"]
return f"ACCESS_NUMBER:{order['id']}:{order['phone_number'].lstrip('+')}"
def get_status(self, order_id):
error, data = self._call("GET", f"/numbers/{order_id}")
if error:
return f"ERROR:{error}"
order = data["order"]
if order.get("sms_codes"):
return f"STATUS_OK:{order['sms_codes'][-1]}"
if order["status"] in ("canceled", "refunded"):
return "STATUS_CANCEL"
return "STATUS_WAIT_CODE"
def set_status(self, order_id, status):
if int(status) == 8:
error, _ = self._call("POST", f"/numbers/{order_id}/cancel")
if error == "too_early":
return "EARLY_CANCEL_DENIED"
return f"ERROR:{error}" if error else "ACCESS_CANCEL"
if int(status) == 6:
error, _ = self._call("POST", f"/orders/{order_id}/finish")
return f"ERROR:{error}" if error else "ACCESS_ACTIVATION"
return "BAD_STATUS"
Two places worth adapting to your setup. First, on 503 and 504 responses to a purchase, retry with the same Idempotency-Key, not a new one, or you may buy two numbers. Second, poll the status no more than once every few seconds, or better, use webhooks.
Rules that differ
- you can cancel an order while it has no SMS, and no earlier than 2 minutes after the purchase;
- if no SMS arrives within 15 minutes, the order closes by itself and the money goes back to your balance;
- if the supplier price changed during the purchase, the response includes
price_changed: trueand the previous price; - on
503and504responses, check the order status first and only then repeat the action.
Webhooks instead of polling
Set the webhook address in the API section of the Telegram bot. When an SMS arrives, Hush SMS sends a POST request with the code to that address, and status polling can stay as a fallback. The request format is described in the Webhooks section of the docs.
A one-evening migration plan
- Get an API key in the bot @HushNumBot or in the web account settings and top up the balance.
- Drop in the adapter in place of the module that called
handler_api.php. - Test the full cycle on one service: purchase, code, finish and cancel.
- Connect webhooks and keep status polling as a fallback.
- Move your order traffic and keep a second supplier in your code for the next outage.
Frequently asked questions
Does Hush SMS have an endpoint compatible with handler_api.php?
No, Hush SMS has its own REST API. The fastest way to move is the adapter above: it answers with the same strings as the old API.
Where do I get an API key?
In the Telegram bot @HushNumBot, in the API section, or in the web account settings. The key goes in the Authorization: Bearer header.
Will my HeroSMS balance move over?
No, these are different services. At Hush SMS you top up separately: in the bot with SBP bank transfer, cryptocurrency or Telegram Stars, on the website with cryptocurrency.
What if hero-sms.com comes back?
Keep Hush SMS as a second supplier in your code and switch to it when the first one fails. Then the next outage of a single service will not stop your purchases.
Moving an integration? Get an API key and start with one test order.