Moving off the HeroSMS API: migrate your integration to Hush SMS

Moving off the HeroSMS API: migrate your integration to Hush SMS

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:

What the DNS error looks like in different languages
WhereError in the log
curlcurl: (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.jsgetaddrinfo ENOTFOUND hero-sms.com
PHP, GuzzlecURL error 6: Could not resolve host: hero-sms.com
Godial 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.php address with an action parameter and text responses;
  • the key goes in the Authorization: Bearer header, not in the api_key parameter;
  • a purchase needs an Idempotency-Key header: 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 API methods and their Hush SMS equivalents
HeroSMS, handler_api.phpHush SMS API
getBalanceGET /v1/me: profile and balance
getNumber, getNumberV2POST /v1/numbers with a {"service", "country"} body and an Idempotency-Key header
getStatus, getStatusV2GET /v1/numbers/{id}: status and codes in the sms_codes field; every SMS of the order: GET /v1/orders/{id}/sms
setStatus, status=8POST /v1/numbers/{id}/cancel: cancel the order
setStatus, status=6POST /v1/orders/{id}/finish: finish the order
setStatus, status=3No separate method: request another code in the bot or in the web account
getActiveActivationsGET /v1/numbers: active orders
getPrices, getServicesList, getCountriesGET /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 API responses and what Hush SMS returns
HeroSMS responseWhat Hush SMS returns
ACCESS_NUMBER:id:number201 and an order object with id, phone_number, status and price
NO_NUMBERS404 with the not_available code
NO_BALANCE402 with the insufficient_funds code
BAD_KEY401: the key is missing or invalid
STATUS_WAIT_CODEstatus: pending: the number is issued, waiting for the SMS
STATUS_OK:codestatus: sms_received, the code is in sms_codes
STATUS_CANCELstatus: canceled, then refunded: the money is back on the balance
EARLY_CANCEL_DENIED409 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: true and the previous price;
  • on 503 and 504 responses, 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

  1. Get an API key in the bot @HushNumBot or in the web account settings and top up the balance.
  2. Drop in the adapter in place of the module that called handler_api.php.
  3. Test the full cycle on one service: purchase, code, finish and cancel.
  4. Connect webhooks and keep status polling as a fallback.
  5. 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.