API

Отправка ссылок на индексацию и статус каждой из них — из вашего кода. Обычный HTTP и JSON, ключ создаётся в кабинете за минуту, отдельного согласования не требуется.

База https://dobermanindexztoken.com/api/v1Версия v1Ключ в заголовкеОтвет — JSON

С чего начать#

Три шага: создать ключ в кабинете, отправить список ссылок, забрать статус каждой. Ниже — весь путь одной командой; всё остальное на этой странице подробности к нему.

bash
# 1. Отправить ссылки
curl -X POST https://dobermanindexztoken.com/api/v1/tasks \
  -H "X-Api-Key: ik_ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://site.com/a", "https://site.com/b"]}'

# 2. Забрать статус каждой (id из ответа выше)
curl https://dobermanindexztoken.com/api/v1/tasks/965/urls \
  -H "X-Api-Key: ik_ВАШ_КЛЮЧ"

Ключ создаётся в кабинете, на странице «API и ключи». Он показывается один раз — в базе остаётся только хеш, и восстановить ключ невозможно. Потеряли — заведите новый, старый отзовите.

Ключ и авторизация#

Ключ передаётся заголовком. Два способа намеренно: Authorization привычен тем, кто ходил в другие API, а X-Api-Key — тем, у кого заголовок авторизации уже занят прокси или шлюзом.

http
X-Api-Key: ik_ВАШ_КЛЮЧ
# или
Authorization: Bearer ik_ВАШ_КЛЮЧ

Ключей на аккаунт можно завести до десяти — по одному на сервис, чтобы отозвать один, не трогая остальные. Отзыв действует сразу.

Ключ — это доступ к балансу. Он позволяет тратить токены, поэтому место ему в переменных окружения на сервере, а не в коде страницы и не в репозитории.

Формат ответа#

В каждом ответе есть поле success — в успешном и в ошибочном. Разбирать по HTTP-коду тоже можно, но клиентские библиотеки чаще смотрят в тело, и одно поле дешевле объяснить, чем таблицу кодов.

json
{ "success": true,  "task": { ... } }

{ "success": false, "error": { "code": "insufficient_funds",
                               "message": "Недостаточно токенов",
                               "details": {} } }

Поля внутри версии v1 не исчезают и не меняют смысл. Новые появляться могут — разбирайте ответ так, чтобы незнакомое поле не ломало код.

Отправка ссылок#

POST/api/v1/tasks
ПолеОбязательноЧто это
urlsдаМассив адресов или одна строка с переводами строк
productнетКод услуги, по умолчанию mass_index
titleнетНазвание — по нему задание проще найти в кабинете
idempotency_keyнетСвоя строка на попытку. Повтор с тем же ключом вернёт то же задание и не спишет токены дважды
bash
curl -X POST https://dobermanindexztoken.com/api/v1/tasks \
  -H "X-Api-Key: ik_ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{
    "product": "mass_index",
    "urls": ["https://site.com/a", "https://site.com/b", "https://site.com/a"],
    "title": "Августовский блог",
    "idempotency_key": "blog-2026-08-23"
  }'
json
{
  "success": true,
  "charged_tokens": 6,
  "balance_after": 4994,
  "task": {
    "id": 965,
    "status": "queued",
    "urls": { "total": 2, "crawled": 0, "indexed": 0, "errors": 0,
              "rejected": 0, "duplicates": 1, "excluded_already_indexed": 0 },
    "tokens": { "held": 6, "charged": 0, "refunded": 0 }
  }
}

Списывается не всё, что отправили. Повторы, битые адреса и уже проиндексированные ссылки отсеиваются до списания: в примере из трёх адресов приняты два, за них и заморожено 6 токенов. Сколько отсеяно и почему — в полях rejected, duplicates и excluded_already_indexed.

charged_tokens и balance_after есть в ответе намеренно: скрипт, отправляющий пачками, должен уметь остановиться сам, а не узнавать об исчерпанном балансе из отказа.

GET/api/v1/tasks

Ваши задания. Параметры: status, product, date_from, date_to, page, per_page (до 100).

GET/api/v1/tasks/{id}

Одно задание со счётчиками и суммами.

POST/api/v1/tasks/{id}/cancel

Отмена — пока задание не ушло в работу. У каждого задания есть окно на отмену: срок лежит в поле starts_at, признак — can_cancel. Внутри окна токены возвращаются на баланс целиком: за то, что не сделано, мы не берём.

После начала работы отмена невозможна, и возврата не будет — ответ 409. Ссылки к этому моменту уже поданы, обход идёт, и вернуть за него деньги значило бы отдать сделанное даром.

Статус каждой ссылки#

GET/api/v1/tasks/{id}/urls

Главный вызов: что стало с каждым адресом. Параметры: status, q (поиск по адресу), page, per_page.

bash
curl "https://dobermanindexztoken.com/api/v1/tasks/965/urls?status=crawled&per_page=200" \
  -H "X-Api-Key: ik_ВАШ_КЛЮЧ"
json
{
  "success": true,
  "task_id": 965,
  "total": 2,
  "urls": [
    {
      "url": "https://site.com/a",
      "status": "crawled",
      "crawled_at": "2026-08-23T10:23:16+00:00",
      "first_crawled_at": "2026-08-23T10:23:16+00:00",
      "crawl_count": 1,
      "checked_at": null,
      "billable": true,
      "rejected_because": null
    }
  ]
}

Два поля — два разных факта, и путать их нельзя. crawled_at — по ссылке прошёл подтверждённый поисковый бот; это мы фиксируем сами, и время точное. checked_at — когда мы смотрели поисковую выдачу. Пустой checked_at означает «проверки не было», а не «страницы нет в индексе».

statusЧто это
pendingВ очереди, бот ещё не заходил
crawledБот прошёл по ссылке
indexedПроверка нашла страницу в выдаче
not_indexedПроверка страницу не нашла
errorОтправить не удалось

Коды состояний не переводятся и не переименовываются — интеграции ветвятся по ним. Подписи на языке пользователя живут в кабинете, а не здесь.

Аккаунт и услуги#

GET/api/v1/account

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

json
{
  "success": true,
  "account": {
    "id": 4320,
    "email": "you@example.com",
    "lang": "ru",
    "balance": { "total": 4980, "purchased": 4980, "bonus": 0, "usd": "4.98" }
  }
}
GET/api/v1/products

Услуги и цены. Нужен, чтобы не зашивать коды и стоимость в свой код: цена меняется у нас, а ломается — у вас.

GET/api/v1/balance/history

Движения токенов: пополнения, заморозки, списания, возвраты. Параметр direction отделяет приход от расхода. Нужна тем, кто ведёт свой учёт расходов: считая их по своим же вызовам, вы разойдётесь с нами на первом же возврате, о котором не знали.

Вебхуки#

Опрашивать статус в цикле не нужно. Подпишитесь на события — мы сами постучимся на ваш адрес, когда задание завершится.

POST/api/v1/webhooks
bash
curl -X POST https://dobermanindexztoken.com/api/v1/webhooks \
  -H "X-Api-Key: ik_ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://ваш-сервис.ру/hook", "events": ["task.completed"]}'

В ответе придёт secret — единственный раз. Им подписывается каждая доставка.

Как проверить, что стучимся мы

В каждой доставке есть заголовок X-Indexer-Signature — HMAC-SHA256 от тела запроса вашим секретом. Сравнивайте его постоянным по времени сравнением.

python
import hmac, hashlib

def is_ours(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Не ответили 2xx — попробуем ещё: через минуту, две, пять, пятнадцать, полчаса и дальше раз в час. Журнал доставок с ответами вашего сервера виден через GET /webhooks/{id}/deliveries — он отвечает на вопрос «приходило ли вообще», не спрашивая нас.

ВызовЧто делает
GET /webhooksВаши подписки
PATCH /webhooks/{id}Изменить адрес, события или включить/выключить
DELETE /webhooks/{id}Удалить
POST /webhooks/{id}/testТестовое событие — проверить связь до того, как она понадобится
GET /eventsСписок событий. Он закрытый: подписка на выдуманное имя молча не сработала бы

Ошибки#

Отказ — это всегда 4xx и всегда тело с error.code. Ветвитесь по коду, а не по тексту: текст переводится и может стать понятнее, код — нет.

HTTPcodeЧто произошло
401invalid_api_keyКлюча нет, он отозван или скопирован не целиком
402insufficient_fundsНа балансе меньше, чем стоит отправка
404not_foundЗадания с таким номером у вас нет
409conflictНи одной пригодной ссылки, или задание уже нельзя отменить
422validation_errorЗапрос не разобрался. В details.fields — что именно не так
429rate_limitedСлишком часто. Подождите и повторите

Повторы и пределы#

Повтор запроса не должен стоить дважды

Ответ потерялся, соединение оборвалось, скрипт перезапустился — обычное дело. Передайте idempotency_key: повтор с тем же ключом вернёт то же задание и не спишет токены второй раз. Ключ ваш и приватный — если сосед по сервису выберет такой же, ваши задания не пересекутся.

json
{ "urls": ["https://site.com/a"], "idempotency_key": "nightly-2026-08-23" }

Пределы

  • per_page — до 100 в списках заданий и движений баланса, до 1000 в списке ссылок (по умолчанию 200).
  • Ключей на аккаунт — до 10.
  • Размер одной отправки ограничен балансом, а не числом строк: списывается за принятые ссылки.

Слишком частые запросы получают 429. Разумный опрос статуса — раз в минуту; лучше вебхук, тогда опрашивать не придётся вовсе.

Примеры на языках#

Python

python
import httpx

API = "https://dobermanindexztoken.com/api/v1"
KEY = "ik_ВАШ_КЛЮЧ"
head = {"X-Api-Key": KEY}

with httpx.Client(timeout=30) as client:
    created = client.post(f"{API}/tasks", headers=head, json={
        "urls": ["https://site.com/a", "https://site.com/b"],
        "idempotency_key": "nightly-2026-08-23",
    }).json()

    if not created["success"]:
        raise SystemExit(created["error"]["message"])

    task_id = created["task"]["id"]
    print("списано", created["charged_tokens"], "осталось", created["balance_after"])

    rows = client.get(f"{API}/tasks/{task_id}/urls", headers=head).json()["urls"]
    for row in rows:
        # crawled_at — заход бота, checked_at — проверка выдачи. Пусто
        # во втором означает, что проверки не было
        print(row["url"], row["status"], row["crawled_at"])

JavaScript

javascript
const API = 'https://dobermanindexztoken.com/api/v1';
const head = { 'X-Api-Key': process.env.INDEXER_KEY, 'Content-Type': 'application/json' };

const created = await fetch(`${API}/tasks`, {
  method: 'POST',
  headers: head,
  body: JSON.stringify({ urls: ['https://site.com/a'], idempotency_key: 'nightly-1' })
}).then((r) => r.json());

if (!created.success) throw new Error(created.error.message);

const { urls } = await fetch(`${API}/tasks/${created.task.id}/urls`, { headers: head })
  .then((r) => r.json());

console.table(urls.map(({ url, status, crawled_at }) => ({ url, status, crawled_at })));

PHP

php
<?php
$api = 'https://dobermanindexztoken.com/api/v1';
$key = getenv('INDEXER_KEY');

$ch = curl_init("$api/tasks");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["X-Api-Key: $key", 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode([
        'urls' => ['https://site.com/a', 'https://site.com/b'],
        'idempotency_key' => 'nightly-2026-08-23',
    ]),
]);
$created = json_decode(curl_exec($ch), true);
curl_close($ch);

if (!$created['success']) {
    exit($created['error']['message']);
}
echo "Задание {$created['task']['id']}, списано {$created['charged_tokens']}\n";

Обход сайта

Отдельный вызов, потому что заказывается иначе: не списком ссылок, а адресом карты сайта. Сколько там адресов, знает только сама карта — поэтому стоимость становится известна после её чтения, а не в момент заказа.

bash
curl -X POST https://dobermanindexztoken.com/api/v1/site-crawl \
  -H "X-Api-Key: ik_ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "https://site.ru/sitemap.xml",
    "source_kind": "sitemap",
    "title": "Обход перед переездом"
  }'

source_kindsitemap или list. Дальше задание живёт по общим правилам: состояние читается через GET /tasks/{id}, строки — через GET /tasks/{id}/urls, отмена до старта — как у остальных заданий.

Работаете с ИИ-агентами? Те же действия доступны по протоколу MCP — агент сам отправляет ссылки и читает статусы, без обёртки на HTTP. Как подключить.

Ключ создаётся за минуту

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

Получить ключ APIБез карты · без подписки