Документация · версия 3

Публичный API BuyDomain

Проверяйте домены и цены, запускайте покупку, отслеживайте задачи и управляйте NS через единый JSON API.

ProductionJSON · UTF-8https://buydomain.bot/api/v3
Live console · запросы из браузера

Попробовать API

Вставьте полный ключ v3, затем откройте нужный метод ниже. Запрос уйдёт только на этот сайт. Ключ не сохраняется, не добавляется в URL и стирается при закрытии страницы.

Если для ключа задан IP whitelist, разрешите IP текущего браузера. Не вводите ключ на чужом устройстве.
01 / Начало работы

Быстрый старт

1 Получите ключ

Откройте раздел API в Telegram-боте и создайте ключ v3. Полный ключ храните как пароль.

2 Настройте IP

При необходимости укажите исходящие IP ваших серверов в whitelist ключа: до 20 адресов IPv4/IPv6.

Первый запрос · curl
curl -sS https://buydomain.bot/api/v3/me \
  -H 'Authorization: Bearer YOUR_API_KEY'
Python · requests
import requests

BASE = "https://buydomain.bot/api/v3"
KEY = "YOUR_API_KEY"
response = requests.get(BASE + "/me",
    headers={"Authorization": "Bearer " + KEY}, timeout=30)
print(response.status_code, response.json())
PHP · cURL
$ch = curl_init("https://buydomain.bot/api/v3/me");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer YOUR_API_KEY"],
    CURLOPT_TIMEOUT => 30,
]);
echo curl_exec($ch);
02 / Доступ

Авторизация и безопасность

Bearer-ключ

Передавайте полный ключ в заголовке Authorization: Bearer bd3_…. Не помещайте его в URL, логи или публичный JavaScript.

Для POST и PUT нужен Content-Type: application/json. Тело — JSON-объект размером до 256 KiB.

IP whitelist

Пустой список разрешает доступ с любого IP. Если список заполнен, другие адреса получают HTTP 403 ip_not_allowed.

Принимаются точные IPv4/IPv6, но не CIDR и не имена хостов.

Важно при покупке: одна логическая покупка — один Idempotency-Key. Повтор с тем же телом вернёт сохранённый ответ, другой payload даст 409 idempotency_conflict. После timeout не создавайте новую покупку с другим ключом: результат у регистратора может быть неизвестен.
Ключи доступа

Отдельные проекты

До 10 ключей

В боте откройте Настройки → API → Добавить проект. У каждого проекта собственный ключ, статус, IP whitelist и адрес webhook. Все проекты используют один аккаунт, его баланс и скидки.

Безопасное управление

Ключ проекта можно отключить, перевыпустить или отозвать независимо от остальных. При перевыпуске сохраняйте прежний Idempotency-Key незавершённого заказа.

Для каждой логической покупки используйте уникальный Idempotency-Key в пределах всего аккаунта, даже если запросы отправляют разные проекты. Это защищает от двойной регистрации при переключении ключей.

Асинхронные результаты

Webhooks

Укажите для проекта публичный HTTPS URL на порту 443 в боте: Настройки → API → Проект → Webhook. После настройки сохраните отдельный секрет подписи. Доставка начнётся только когда администратор включит защищённый исходящий прокси; основной сервер не отправляет уведомления напрямую.

События

domain.purchase.completed, domain.purchase.failed, domain.nameservers.completed, domain.nameservers.failed. Промежуточный статус pending не рассылается. Тестовая кнопка отправляет webhook.test.

Доставка

HTTP POST с JSON. Ответ 2xx подтверждает доставку. Сетевые ошибки, 408, 429 и 5xx повторяются до 10 попыток; возможны дубли и иной порядок событий. Сохраняйте event_id и обрабатывайте его один раз. Повторяется только уведомление, не покупка.

Пример webhook · JSON
{
    "event_id": "9f6f1f61dc694af7a982fb7606337d0f",
    "type": "domain.purchase.completed",
    "occurred_at": "2026-10-26T10:00:00+00:00",
    "data": {
        "task_id": 12345,
        "sale_id": 67890,
        "domain": "example.com",
        "status": "success"
    }
}

Заголовки: X-BuyDomain-Event-ID, X-BuyDomain-Timestamp (Unix seconds), X-BuyDomain-Signature: sha256=…. Подпись — HMAC-SHA256 от строки timestamp + "." + raw_body, ключ — секрет webhook. Проверяйте подпись по сырым байтам тела и отклоняйте timestamp старше пяти минут.

Проверка подписи · PHP
$timestamp = $_SERVER["HTTP_X_BUYDOMAIN_TIMESTAMP"] ?? "";
$signature = $_SERVER["HTTP_X_BUYDOMAIN_SIGNATURE"] ?? "";
$raw = file_get_contents("php://input");
if (!ctype_digit($timestamp) || abs(time() - (int)$timestamp) > 300 ||
    !hash_equals("sha256=" . hash_hmac("sha256", $timestamp . "." . $raw, $secret), $signature)) {
    http_response_code(401); exit;
}
Проверка подписи · Python
import hmac, hashlib, time
timestamp = request.headers["X-BuyDomain-Timestamp"]
signature = request.headers["X-BuyDomain-Signature"]
raw = request.get_data()  # bytes, не пересобирайте JSON
expected = "sha256=" + hmac.new(secret.encode(), timestamp.encode() + b"." + raw, hashlib.sha256).hexdigest()
if abs(time.time() - int(timestamp)) > 300 or not hmac.compare_digest(signature, expected):
    abort(401)
Интеграция

Готовые PHP и Python SDK

Клиент и пример для каждого языка приведены здесь целиком. Скопируйте оба блока в файлы с указанными именами. После timeout не повторяйте покупку или смену NS автоматически: сначала проверьте задачу и сохраните тот же Idempotency-Key.

PHP 8.2+ · ext-curl

BuyDomainV3Client.php · PHP
<?php

/** PHP 8.2+, ext-curl. No automatic retries for purchases or NS mutations. */
final class BuyDomainV3Client
{
    public function __construct(private string $apiKey, private string $baseUrl = 'https://buydomain.bot/api/v3', private int $timeout = 30)
    {
        if (!preg_match('/^bd3_[A-Za-z0-9_-]{20,}$/', $apiKey)) throw new InvalidArgumentException('Invalid API v3 key');
        if ($baseUrl !== 'https://buydomain.bot/api/v3') throw new InvalidArgumentException('Unsupported API host');
        if ($timeout < 1 || $timeout > 120) throw new InvalidArgumentException('Invalid timeout');
    }

    public function me(): array { return $this->request('GET', '/me'); }
    /** Balance of this project key, plus billing_source: user or key. */
    public function balance(): array { return $this->request('GET', '/wallet/balance'); }
    /** Code-only lookup; returns no owner identity or account balance. */
    public static function balanceByTopupCode(string $code): array
    {
        if (!preg_match('/^bd3t_[a-f0-9]{32}$/', $code)) throw new InvalidArgumentException('Invalid top-up code');
        $ch = curl_init('https://buydomain.bot/api/v3/wallet/balance');
        if (!$ch) throw new RuntimeException('Could not initialize HTTP client');
        curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode(['topup_code' => $code], JSON_THROW_ON_ERROR),
            CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Accept: application/json'],
            CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_FOLLOWLOCATION => false]);
        $raw = curl_exec($ch);
        $status = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        if ($raw === false) throw new RuntimeException('API transport error: ' . $error);
        $response = json_decode($raw, true);
        if (!is_array($response)) throw new RuntimeException('Invalid API response; HTTP ' . $status);
        if ($status < 200 || $status >= 300) throw new BuyDomainV3Exception((string)($response['error_code'] ?? 'api_error'), (string)($response['message'] ?? 'API error'), $status, $response);
        return $response;
    }
    public function check(string $domain): array { return $this->request('POST', '/domains/check', ['domain' => $domain]); }
    public function checkBulk(array $domains): array { return $this->request('POST', '/domains/check/bulk', ['domains' => $domains]); }
    public function purchase(string $domain, string $idempotencyKey, ?float $confirmedPrice = null): array
    {
        if ($idempotencyKey === '' || strlen($idempotencyKey) > 191) throw new InvalidArgumentException('Idempotency-Key is required');
        $body = ['domain' => $domain];
        if ($confirmedPrice !== null) $body['confirmed_price'] = $confirmedPrice;
        return $this->request('POST', '/domains/purchase', $body, ['Idempotency-Key: ' . $idempotencyKey]);
    }
    public function task(int $id): array { return $this->request('GET', '/tasks/' . $id); }
    public function domains(int $page = 1, int $limit = 50, string $sort = 'desc'): array
    {
        if ($page < 1 || $limit < 1 || $limit > 200 || !in_array($sort, ['asc', 'desc'], true)) throw new InvalidArgumentException('Invalid pagination');
        return $this->request('GET', '/domains?' . http_build_query(compact('page', 'limit', 'sort')));
    }
    public function domain(string $domain): array { return $this->request('GET', '/domains/' . rawurlencode($domain)); }
    public function nameservers(string $domain): array { return $this->request('GET', '/domains/' . rawurlencode($domain) . '/nameservers'); }
    public function changeNameservers(string $domain, array $nameservers): array
    {
        return $this->request('PUT', '/domains/' . rawurlencode($domain) . '/nameservers', ['nameservers' => $nameservers]);
    }

    private function request(string $method, string $path, ?array $body = null, array $extraHeaders = []): array
    {
        $headers = array_merge(['Authorization: Bearer ' . $this->apiKey, 'Accept: application/json'], $extraHeaders);
        $options = [CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => $headers,
            CURLOPT_CONNECTTIMEOUT => min(10, $this->timeout), CURLOPT_TIMEOUT => $this->timeout,
            CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_FOLLOWLOCATION => false];
        if ($body !== null) {
            $headers[] = 'Content-Type: application/json';
            $options[CURLOPT_HTTPHEADER] = $headers;
            $options[CURLOPT_POSTFIELDS] = json_encode($body, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
        }
        $ch = curl_init($this->baseUrl . $path);
        if (!$ch) throw new RuntimeException('Could not initialize HTTP client');
        curl_setopt_array($ch, $options);
        $raw = curl_exec($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        if ($raw === false) throw new RuntimeException('API transport error: ' . $error . '. Do not change Idempotency-Key before checking the order.');
        $response = json_decode($raw, true);
        if (!is_array($response)) throw new RuntimeException('Invalid API response; HTTP ' . $code);
        if ($code < 200 || $code >= 300) throw new BuyDomainV3Exception((string)($response['error_code'] ?? 'api_error'), (string)($response['message'] ?? 'API error'), $code, $response);
        return $response;
    }
}

final class BuyDomainV3Exception extends RuntimeException
{
    public function __construct(public readonly string $errorCode, string $message, public readonly int $httpStatus, public readonly array $response)
    {
        parent::__construct($message, $httpStatus);
    }
}
example.php · PHP
<?php
require_once __DIR__ . '/BuyDomainV3Client.php';

$client = new BuyDomainV3Client(getenv('BUYDOMAIN_API_KEY') ?: '');
$check = $client->check('example.com');
print_r($check);

// Only after confirming the price and intending to buy, uncomment this:
// $orderId = 'my-order-123'; // Keep this exact ID if your connection times out.
// $purchase = $client->purchase('example.com', $orderId, (float)$check['data']['amount']);
// $task = $client->task((int)$purchase['data']['task_id']);

Python 3.10+ · стандартная библиотека

buydomain_v3.py · Python
"""BuyDomain API v3, Python 3.10+, standard library. Mutations are never retried."""
import json
from urllib import error, parse, request


class ApiError(Exception):
    def __init__(self, status, payload):
        self.status = status
        self.payload = payload
        super().__init__(payload.get("message", "API error"))


class BuyDomainV3Client:
    BASE = "https://buydomain.bot/api/v3"

    def __init__(self, api_key, timeout=30, opener=None):
        if not api_key.startswith("bd3_") or len(api_key) < 24:
            raise ValueError("Invalid API v3 key")
        if not 1 <= timeout <= 120:
            raise ValueError("Invalid timeout")
        self.api_key = api_key
        self.timeout = timeout
        self.opener = opener or request.urlopen

    def _call(self, method, path, body=None, headers=None):
        data = None if body is None else json.dumps(body, separators=(",", ":")).encode("utf-8")
        req = request.Request(self.BASE + path, data=data, method=method)
        req.add_header("Authorization", "Bearer " + self.api_key)
        req.add_header("Accept", "application/json")
        if data is not None:
            req.add_header("Content-Type", "application/json")
        for name, value in (headers or {}).items():
            req.add_header(name, value)
        try:
            with self.opener(req, timeout=self.timeout) as response:
                return json.load(response)
        except error.HTTPError as exc:
            try:
                payload = json.load(exc)
            except (ValueError, UnicodeDecodeError):
                payload = {"message": "Invalid API response"}
            raise ApiError(exc.code, payload) from exc
        # URLError/timeout are intentionally not retried: the mutation result may be unknown.

    def me(self): return self._call("GET", "/me")
    def balance(self): return self._call("GET", "/wallet/balance")

    @classmethod
    def balance_by_topup_code(cls, code, timeout=30, opener=None):
        import re
        if not re.fullmatch(r"bd3t_[a-f0-9]{32}", code):
            raise ValueError("Invalid top-up code")
        payload = json.dumps({"topup_code": code}).encode("utf-8")
        req = request.Request(cls.BASE + "/wallet/balance", data=payload, method="POST",
                              headers={"Content-Type": "application/json", "Accept": "application/json"})
        try:
            with (opener or request.urlopen)(req, timeout=timeout) as response:
                return json.load(response)
        except error.HTTPError as exc:
            try:
                response = json.load(exc)
            except (ValueError, UnicodeDecodeError):
                response = {"message": "Invalid API response"}
            raise ApiError(exc.code, response) from exc

    def check(self, domain): return self._call("POST", "/domains/check", {"domain": domain})
    def check_bulk(self, domains): return self._call("POST", "/domains/check/bulk", {"domains": domains})

    def purchase(self, domain, idempotency_key, confirmed_price=None):
        if not idempotency_key or len(idempotency_key) > 191:
            raise ValueError("Idempotency-Key is required")
        body = {"domain": domain}
        if confirmed_price is not None:
            body["confirmed_price"] = confirmed_price
        return self._call("POST", "/domains/purchase", body, {"Idempotency-Key": idempotency_key})

    def task(self, task_id): return self._call("GET", "/tasks/" + str(int(task_id)))
    def domains(self, page=1, limit=50, sort="desc"):
        if page < 1 or not 1 <= limit <= 200 or sort not in ("asc", "desc"):
            raise ValueError("Invalid pagination")
        return self._call("GET", "/domains?" + parse.urlencode({"page": page, "limit": limit, "sort": sort}))
    def domain(self, domain): return self._call("GET", "/domains/" + parse.quote(domain, safe=""))
    def nameservers(self, domain): return self._call("GET", "/domains/" + parse.quote(domain, safe="") + "/nameservers")
    def change_nameservers(self, domain, nameservers):
        return self._call("PUT", "/domains/" + parse.quote(domain, safe="") + "/nameservers", {"nameservers": nameservers})
example.py · Python
import os
from buydomain_v3 import BuyDomainV3Client

client = BuyDomainV3Client(os.environ["BUYDOMAIN_API_KEY"])
check = client.check("example.com")
print(check)

# Only after confirming the price and intending to buy, uncomment this:
# order_id = "my-order-123"  # Reuse this ID if your connection times out.
# purchase = client.purchase("example.com", order_id, check["data"]["amount"])
# print(client.task(purchase["data"]["task_id"]))
Финансы проектов

Баланс и списание

Источник списания

Для каждого ключа в боте можно выбрать billing_source=user (баланс владельца, по умолчанию) или billing_source=key (отдельный баланс ключа). Резервного списания со второго баланса при нехватке денег нет. Скидки определяются аккаунтом владельца.

Пополнение

Каждый ключ имеет код bd3t_…. В боте выберите «Пополнить баланс → Баланс API v3 ключа», введите код и оплатите с помощью доступного платёжного метода. Владелец может перевести деньги со своего баланса на ключ. Средства ключа доступны только для покупок и не выводятся. Остаток отозванного ключа переносит администратор на другой ключ того же владельца.

Оба метода GET /wallet/balance по Bearer-ключу и POST /wallet/balance по коду пополнения возвращают balance и billing_source. По коду не раскрываются данные владельца и его общий баланс.

Справочник методов

Аккаунт

GET/me

Текущий пользователь

Профиль и баланс владельца API-ключа.

HTTP 200
Запрос · curl
curl -sS https://buydomain.bot/api/v3/me \
  -H 'Authorization: Bearer YOUR_API_KEY'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "chat_id": "123456789",
        "name": "@username",
        "status": "active",
        "balance": 42.5,
        "withdrawable": 10,
        "withdrawal_only": 0,
        "restricted": 32.5,
        "reserved": 0,
        "currency": "USD"
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

Баланс и его составляющие возвращаются в USD.

▶ Попробовать запрос GET /meLIVE
Ответ появится ниже без перезагрузки страницы
GET/wallet/balance

Баланс API-ключа

Баланс этого ключа и выбранный источник списания.

HTTP 200
Запрос · curl
curl -sS https://buydomain.bot/api/v3/wallet/balance \
  -H 'Authorization: Bearer YOUR_API_KEY'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "balance": 25,
        "billing_source": "key",
        "currency": "USD"
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

billing_source: key — покупки списываются с ключа; user — с баланса владельца. Значение balance всегда показывает остаток на самом ключе.

▶ Попробовать запрос GET /wallet/balanceLIVE
Ответ появится ниже без перезагрузки страницы
POST/wallet/balance

Баланс по коду пополнения

Проверка баланса и источника списания по коду пополнения без Bearer-ключа.

HTTP 200
ПолеГде / типОписание
topup_codebody · stringКод bd3t_… из раздела API в боте.
Запрос · curl
curl -sS -X POST https://buydomain.bot/api/v3/wallet/balance \
  -H 'Content-Type: application/json' \
  -d '{"topup_code":"bd3t_0123456789abcdef0123456789abcdef"}'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "balance": 25,
        "billing_source": "key",
        "currency": "USD"
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

Код храните в секрете: любой, кто его знает, может видеть баланс ключа и пополнять его. Данные владельца не раскрываются.

▶ Попробовать запрос POST /wallet/balanceLIVE
request.json · редактор → подсветка
Ответ появится ниже без перезагрузки страницы
Справочник методов

Покупка

POST/domains/purchase

Купить домен

Создаёт production-задачу покупки и может списать средства.

HTTP 202
ПолеГде / типОписание
domainbody · stringОбязательное полное доменное имя.
confirmed_pricebody · numberНеобязательная подтверждённая цена, больше нуля.
Idempotency-Keyheader · stringОбязательный ключ одной логической покупки, до 191 символа.
Запрос · curl
curl -sS -X POST https://buydomain.bot/api/v3/domains/purchase \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-123' \
  -d '{"domain":"example.com","confirmed_price":13.37}'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "domain": "example.com",
        "task_id": 12345,
        "sale_id": 67890,
        "status": "pending"
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

HTTP 202 не означает, что домен уже куплен. Следите за GET /tasks/{task_id}. После timeout не отправляйте покупку с новым ключом.

▶ Попробовать запрос POST /domains/purchaseLIVE
request.json · редактор → подсветка

⚠️ Это реальная операция: возможны регистрация домена и списание денег. Автоматических повторов нет.

Ответ появится ниже без перезагрузки страницы
Справочник методов

Домены и задачи

GET/domains

Мои домены

Сохранённые домены владельца ключа, отсортированные по ID продажи.

HTTP 200
ПолеГде / типОписание
pagequery · integerСтраница от 1, по умолчанию 1.
limitquery · integerОт 1 до 200, по умолчанию 50.
sortquery · asc | descПо ID продажи, по умолчанию desc.
Запрос · curl
curl -sS https://buydomain.bot/api/v3/domains?page=1&limit=50&sort=desc \
  -H 'Authorization: Bearer YOUR_API_KEY'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "items": [
            {
                "sale_id": 67890,
                "domain": "example.com",
                "status": "active",
                "expired": "2027-10-25",
                "created": "2026-10-25 10:00:00"
            }
        ],
        "page": 1,
        "limit": 50,
        "sort": "desc",
        "total": 1,
        "has_more": false
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

Следующая страница: увеличьте page, пока has_more не станет false.

▶ Попробовать запрос GET /domainsLIVE
Ответ появится ниже без перезагрузки страницы
GET/domains/{domain}

Информация о домене

Сохранённая запись конкретного домена пользователя.

HTTP 200
ПолеГде / типОписание
domainpath · stringОбязательное полное доменное имя.
Запрос · curl
curl -sS https://buydomain.bot/api/v3/domains/example.com \
  -H 'Authorization: Bearer YOUR_API_KEY'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "sale_id": 67890,
        "domain": "example.com",
        "status": "active",
        "expired": "2027-10-25"
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

Чужие домены не раскрываются: ответ 404 domain_not_found.

▶ Попробовать запрос GET /domains/{domain}LIVE
Ответ появится ниже без перезагрузки страницы
GET/domains/{domain}/renewal-price

Цена продления моего домена

Ориентировочная цена продления на один год для активного домена владельца ключа, по котировке этого домена у привязанного аккаунта регистратора.

HTTP 200
ПолеГде / типОписание
domainpath · stringВаш активный купленный домен.
Запрос · curl
curl -sS https://buydomain.bot/api/v3/domains/example.com/renewal-price \
  -H 'Authorization: Bearer YOUR_API_KEY'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "domain": "example.com",
        "zone": "com",
        "amount": 19.97,
        "currency": "USD",
        "period": 1,
        "price_source": "domain_quote",
        "exact": false
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

Чужой или неактивный домен даёт 404. Наценка и скидка совпадают с расчётом при продлении в боте. exact=false: итог может измениться до фактического продления. Запрос не продлевает домен.

▶ Попробовать запрос GET /domains/{domain}/renewal-priceLIVE
Ответ появится ниже без перезагрузки страницы
GET/tasks/{task_id}

Статус задачи

Результат покупки или другой задачи владельца ключа.

HTTP 200
ПолеГде / типОписание
task_idpath · integerОбязательный числовой ID задачи.
Запрос · curl
curl -sS https://buydomain.bot/api/v3/tasks/12345 \
  -H 'Authorization: Bearer YOUR_API_KEY'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "task_id": 12345,
        "method": "buy_domain_asynx",
        "domain": "example.com",
        "sale_id": 67890,
        "status": "success",
        "error_type": null
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

Задача может оставаться pending или processing; проверяйте конечный статус.

▶ Попробовать запрос GET /tasks/{task_id}LIVE
Ответ появится ниже без перезагрузки страницы
Справочник методов

NS-серверы

GET/domains/{domain}/nameservers

Получить NS

NS, сохранённые для активного домена в системе.

HTTP 200
ПолеГде / типОписание
domainpath · stringВаш активный домен.
Запрос · curl
curl -sS https://buydomain.bot/api/v3/domains/example.com/nameservers \
  -H 'Authorization: Bearer YOUR_API_KEY'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "domain": "example.com",
        "nameservers": [
            "ns1.example.net",
            "ns2.example.net"
        ]
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

Каждый NS — отдельная строка массива, даже если в базе они сохранены в одной записи.

▶ Попробовать запрос GET /domains/{domain}/nameserversLIVE
Ответ появится ниже без перезагрузки страницы
PUT/domains/{domain}/nameservers

Изменить NS

Отправляет смену NS для активного домена владельца ключа.

HTTP 200 / 202
ПолеГде / типОписание
domainpath · stringВаш активный домен.
nameserversbody · string[]Обязательный список из 2–4 NS-серверов.
Запрос · curl
curl -sS -X PUT https://buydomain.bot/api/v3/domains/example.com/nameservers \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"nameservers":["ns1.example.net","ns2.example.net"]}'
Пример ответа · JSON
{
    "status": "ok",
    "data": {
        "domain": "example.com",
        "nameservers": [
            "ns1.example.net",
            "ns2.example.net"
        ],
        "pending": true
    },
    "request_id": "3f6d7c71cf784148becf86ef"
}

Операция меняет настройки у регистратора. После timeout не повторяйте изменение вслепую.

▶ Попробовать запрос PUT /domains/{domain}/nameserversLIVE
request.json · редактор → подсветка

⚠️ Это реальная операция: NS домена будут изменены. Автоматических повторов нет.

Ответ появится ниже без перезагрузки страницы
Справочник ответов

Формат ответа, ошибки и лимиты

Единая структура

Успех: status: ok, data, request_id. Ошибка: status: error, error_code, message, details, request_id.

Пример ошибки · JSON
{
    "status": "error",
    "error_code": "ip_not_allowed",
    "message": "This IP address is not allowed for the API key.",
    "details": {},
    "request_id": "3f6d7c71cf784148becf86ef"
}

HTTP-коды

  • 400 — неверный JSON или отсутствует Idempotency-Key.
  • 401 / 403 — ключ, IP или статус аккаунта не позволяют доступ.
  • 404 — маршрут или объект не найден.
  • 409 — конфликт или неопределённый результат исходной покупки.
  • 415 / 422 — неверный Content-Type или отклонённая операция.
  • 429 — лимит; смотрите Retry-After.
  • 500 / 503 — ошибка сервиса или временная недоступность.

Каждый ответ содержит request_id и заголовок X-Request-ID. Передавайте этот ID поддержке. Лимиты на ключ, IP и дорогие операции настраиваются администратором; при 429 дождитесь Retry-After.