AI Data Gate API
Поисковый API для AI-агентов: принимает запрос, собирает выдачу из внешних источников, скачивает и очищает страницы, ранжирует фрагменты под запрос и возвращает готовый для LLM контекст. Интерфейс - обычный JSON по HTTP, плюс MCP-сервер для ИИ-ассистентов: подключение описано в разделе «Подключение через MCP».
- Базовый адрес:
https://aidatagate.ru - Авторизация: заголовок
Authorization: Bearer <ключ>. Принимается такжеX-API-Key: <ключ>(голый ключ, без слова Bearer - так проще настраивать клиентов, которые заполняются формой) и, для совместимости, полеapi_keyв теле запроса. MCP-клиенты могут вместо ключа войти через OAuth, см. «Подключение через MCP» - Формат: JSON, UTF-8
- Версия API отдаётся в заголовке ответа
X-AIDataGate-Version - Тарификация видна в заголовках ответа:
X-Credits-Charged(списано за этот запрос),X-Credits-Remaining(остаток на балансе) иX-AIDataGate-Cache(hit- запрос обслужен из кэша и потому дешевле, см. «Кэш и тарификация»)
POST /search
Запрос:
{
"query": "льготная ипотека условия 2026",
"search_depth": "basic",
"topic": "general",
"max_results": 5,
"include_answer": false,
"include_raw_content": false,
"include_images": false,
"include_domains": [],
"exclude_domains": [],
"days": 7,
"time_range": null,
"country": "ru"
}
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
query |
string | обязательно | Запрос, до 400 символов |
search_depth |
basic / advanced |
basic |
advanced опрашивает два источника и склеивает топ-3 фрагмента документа |
topic |
general / news |
general |
news - выдача, отсортированная по времени: сначала свежие материалы |
max_results |
int 1-20 | 5 | Сколько документов вернуть; больше 20 не ошибка, а те же 20. Тарифицируется за каждые 10: топ-20 стоит вдвое дороже топ-10 |
include_answer |
bool / basic / advanced |
false | Короткий ответ по фрагментам; advanced - развёрнутый. См. answer_source ниже |
include_raw_content |
bool / markdown / text |
false | Полный очищенный текст документа, до 50 000 символов |
include_domains / exclude_domains |
string[] | [] | Фильтр по доменам, совпадение по суффиксу |
days |
int | - | Свежесть: не старше N дней |
time_range |
day / week / month / year |
- | Свежесть окном |
start_date / end_date |
YYYY-MM-DD |
- | Явное окно дат публикации, включительно |
country |
string | ru |
Страна: ISO-код (ru, kz) или полное название (Russia, Россия) |
language |
string | - | Язык выдачи; по умолчанию определяется по самому запросу |
filter_by_language |
bool | false | Отбросить документы на других языках (документы без определённого языка остаются) |
include_domains_mode |
filter / boost |
filter |
boost не отсекает чужую выдачу, а поднимает свои домены в ранжировании |
exact_match |
bool | false | Требует фразу в кавычках в query, иначе 400 |
chunks_per_source |
int 1-5 | 3 | Сколько фрагментов документа склеивать в content при search_depth=advanced |
include_images |
bool | false | Картинки найденных страниц - в поле images ответа |
include_favicon |
bool | false | Значок сайта в поле favicon каждого результата |
include_usage |
bool | false | Цена запроса в кредитах в поле usage ответа |
auto_parameters |
bool | false | Вернуть в auto_parameters параметры, с которыми запрос выполнен |
Неизвестные поля игнорируются, сервис на них не падает. null читается как «параметр
не задан»: обёртки вокруг SDK шлют его вместо отсутствующего ключа.
Фильтры свежести. days, time_range, start_date и end_date не подсказка
поисковику, а отсечение: документ с известной датой публикации вне окна в ответ не
попадает. Заданные вместе, они сужают окно, а не расширяют. Документы, у которых дата
не определилась, остаются в выдаче: дата читается меньше чем у половины страниц, и
отсечение по её отсутствию выкосило бы выдачу целиком.
Фильтр по доменам. include_domains уходит прямо в запрос к поисковику оператором
site: - иначе фильтровать было бы нечего: по общему запросу нужный домен в топ обычно
не попадает. Список до 8 доменов; длиннее - оператор не добавляется, и работает только
фильтрация уже полученной выдачи. Если запрос с оператором вернул пустоту, он
автоматически повторяется без него. exclude_domains применяется к выдаче.
Ответ:
{
"query": "льготная ипотека условия 2026",
"answer": null,
"follow_up_questions": null,
"images": [],
"results": [
{
"title": "Условия льготной ипотеки в 2026 году",
"url": "https://example.ru/ipoteka",
"content": "Программа льготной ипотеки в 2026 году...",
"score": 0.93,
"raw_content": null,
"raw_content_truncated": false,
"published_date": "Tue, 01 Sep 2026 10:00:00 GMT"
}
],
"response_time": 1.84,
"request_id": "0f2a...",
"answer_error": null,
"answer_source": null
}
request_id, answer_error, answer_source и raw_content_truncated - наши
расширения; сторонние клиенты их просто не читают.
answer_source говорит, чем отвечено:
extractive- выжимка из найденных фрагментов, без модели. Работает всегда, тарифицируется как обычный поиск, ссылки на источники в тексте -[1],[2]по номерам результатов;llm- пересказ моделью, включается флагомFEATURE_ANSWER, стоит +2 кредита;null- ответ не получился (например, по фрагментам нечего сказать), причина вanswer_error.
Выбор источника выдачи не предусмотрен, и это решение, а не упущение. Параметра
source нет, имена поисковых движков наружу не отдаются, и по ответу нельзя определить,
кто именно его нашёл. Сервис отвечает за результат, а не за то, чьими руками он получен:
набор источников меняется, часть из них работает по договорам, и обещать клиенту
конкретный движок значит обещать то, чем мы не управляем. Маршрутизация описана ниже в
разделе про источники - она зависит от языка и региона запроса, а не от параметров.
exact_match фильтрует по-настоящему: в выдаче остаются только страницы, где фраза
из кавычек встречается дословно (ищется в заголовке, тексте страницы и сниппете выдачи).
Без кавычек в запросе параметр даёт 400. Выдача при этом может оказаться пустой - это
правильный ответ на запрос про фразу, которой нет.
Фильтры дат и документы без даты. time_range и days - пожелание свежести:
страницы с неизвестной датой публикации из выдачи не выбрасываются, иначе она обмелела бы
(дата известна меньше чем у половины страниц). start_date/end_date - точный запрос:
там документ с неизвестной датой не отвечает условию и в выдачу не попадает.
Картинки. include_images отдаёт картинки найденных страниц: сначала объявленная
страницей главная (og:image), затем иллюстрации по порядку, до пяти на ответ
(PAGE_IMAGES_MAX). Счётчики посещений (1x1) и data:-картинки отбрасываются.
Картинки собираются при разборе страницы и хранятся рядом с её текстом, поэтому
приходят и с закэшированных страниц; у страниц, попавших в кэш до появления этой
возможности, их не будет до обновления записи (TTL кэша - PAGE_CACHE_TTL_DAYS).
raw_content_truncated поднимается, когда текст документа обрезан: по лимиту
RAW_CONTENT_MAX_CHARS (50 000 символов) или потому, что у PDF прочитаны только первые
PDF_MAX_PAGES страниц. Обрезка идёт по границе слова.
POST /extract
{"urls": ["https://habr.com/ru/articles/1/"], "extract_depth": "basic", "format": "markdown"}
Дополнительно: include_images (картинки страницы), include_favicon (значок сайта),
include_usage (цена запроса в кредитах в поле usage).
Ответ: {"results": [{"url", "title", "raw_content", "raw_content_truncated", "images", "favicon", "structured"}], "failed_results": [{"url", "error"}], "response_time", "request_id", "usage"}.
До 20 URL за запрос. urls принимается и списком, и одной строкой. Ошибка одного URL
не ломает весь запрос: он уходит в failed_results.
format=markdown (по умолчанию) отдаёт разметку, format=text - простой текст: если сама
страница является markdown-документом, разметка снимается, а не просачивается в «текст».
PDF извлекается наравне с HTML - первые PDF_MAX_PAGES страниц, с пометкой
raw_content_truncated, если документ длиннее. У PDF без заголовка в метаданных
в title попадает имя файла из адреса: пустая строка не говорит ни о чём.
structured заполняется ТОЛЬКО вместе с instructions: это JSON, который LLM
собирает со страницы по вашей инструкции. Без instructions поле равно null, и это
не признак поломки - просто инструкции не было. Пример:
{"urls": ["https://example.com/prices"], "instructions": "верни массив тарифов: name, price"}
Если пересказ на сервере выключен (FEATURE_ANSWER=false), instructions игнорируется
и structured остаётся null.
POST /crawl
BFS-обход сайта от стартового URL по ссылкам того же корневого домена (включая поддомены).
{
"url": "https://docs.example.ru/",
"max_depth": 1,
"max_breadth": 20,
"limit": 50,
"select_paths": ["^/ru/"],
"exclude_paths": ["/archive/"]
}
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
url |
string | обязательно | Стартовый URL, абсолютный http(s) |
max_depth |
int 1-3 | 1 | Глубина обхода от стартовой страницы |
max_breadth |
int 1-100 | 20 | Сколько новых ссылок брать с каждой страницы |
limit |
int 1-50 | 50 | Максимум страниц за обход (MVP: не больше 50) |
select_paths |
string[] | [] | Regex по пути URL: обходить только совпавшие |
exclude_paths |
string[] | [] | Regex по пути URL: совпавшие не обходить |
Числа выше потолка (max_depth, max_breadth, limit) прижимаются к потолку, а не
дают 400: отказ ломал бы работающий код там, где достаточно вернуть максимум.
| instructions | string | - | Принимается для совместимости со сторонними клиентами и игнорируется: в ответе появляется warning |
Битый regex в select_paths / exclude_paths - ошибка 400 bad_request, limit больше 50 - тоже 400.
Ответ:
{
"base_url": "https://docs.example.ru/",
"results": [
{"url": "https://docs.example.ru/ru/start", "raw_content": "# Начало работы\n..."}
],
"response_time": 3.12,
"request_id": "0f2a...",
"warning": null
}
raw_content - markdown страницы. request_id и warning - наши расширения.
Каждая страница проходит те же механизмы, что и /search: SSRF-защита, robots.txt,
лимиты одновременных запросов на хост. Бинарные файлы (pdf, картинки, архивы) не обходятся.
Редирект, уводящий с корневого домена стартового URL, отбрасывает страницу целиком.
Бюджет всего обхода - 60 секунд (настройка CRAWL_BUDGET); по истечении возвращается
собранное к этому моменту.
Если не открылся сам стартовый URL, ответ - 400 с объяснением, а не пустой список:
пустой ответ читается как «сайт обойдён, брать нечего», и это худший вид ошибки для
агента. Такой запрос не тарифицируется.
Списание: 1 кредит за каждые 5 успешно обработанных страниц, округление вверх.
POST /map
Тот же обход, что и /crawl, но без извлечения контента - только карта ссылок.
Параметры те же (url, max_depth, max_breadth, limit, select_paths, exclude_paths).
{"url": "https://docs.example.ru/", "max_depth": 2}
Ответ:
{
"base_url": "https://docs.example.ru/",
"results": ["https://docs.example.ru/", "https://docs.example.ru/ru/start"],
"response_time": 1.20,
"request_id": "0f2a...",
"warning": null
}
В карту попадают и ссылки на бинарные файлы (pdf и т.п.), хотя сам обход по ним не идёт.
Недоступный стартовый URL - 400, как и в /crawl.
Списание: 1 кредит за каждые 10 URL в ответе, округление вверх.
GET /usage
Баланс кредитов, расход за месяц, лимит запросов на ключ, расход по дням за 30 дней.
GET /healthz
Состояние приложения: 200 - всё живо, 503 - деградация.
Ошибки
Единый формат: {"detail": {"error": "..."}, "code": "..."}. Текст ошибки всегда лежит
в detail.error - именно оттуда его читают совместимые SDK. code - наше расширение,
сторонние клиенты его просто не читают.
Ошибки валидации (400) добавляют разбор по полям:
{
"detail": {"error": "query: String should have at most 400 characters"},
"code": "bad_request",
"errors": [{"field": "query", "message": "String should have at most 400 characters"}]
}
| Код | HTTP | Когда |
|---|---|---|
invalid_api_key |
401 | Ключ неверный или отсутствует |
insufficient_credits |
402 | Не хватает кредитов (фаза 2) |
rate_limited |
429 | Превышен лимит запросов в минуту |
provider_unavailable |
503 | Все источники выдачи недоступны |
bad_request |
400 | Некорректные параметры |
internal |
500 | Внутренняя ошибка |
Повтор запроса: что повторять и как
Сервис живёт за обратным прокси, и в редких окнах перегрузки хозяина клиент может
получить 502/503 или обрыв соединения вместо ответа. Это сетевой сбой, а не отказ
API: повтор через паузу проходит. Агентные клиенты сами обычно не повторяют, поэтому
ретрай стоит заложить в код.
Повторять имеет смысл 429, 502, 503, 504 и обрывы соединения - с удвоением
паузы. Повторять 400, 401 и 402 бессмысленно: ответ не изменится.
import time
import httpx
from httpx import HTTPTransport
# 3 повтора с нарастающей паузой - на соединение и на сетевые сбои
client = httpx.Client(transport=HTTPTransport(retries=3), timeout=30.0)
RETRY_ON = {429, 502, 503, 504}
def search(query: str, key: str, attempts: int = 3) -> dict:
for attempt in range(attempts):
r = client.post(
"https://aidatagate.ru/search",
headers={"Authorization": f"Bearer {key}"},
json={"query": query},
)
if r.status_code not in RETRY_ON or attempt == attempts - 1:
r.raise_for_status()
return r.json()
# 1 с, 2 с, 4 с; Retry-After у 429 важнее нашей паузы
time.sleep(float(r.headers.get("Retry-After") or 2 ** attempt))
raise RuntimeError("unreachable")
В curl то же самое одним флагом:
curl --retry 3 --retry-delay 1 --retry-all-errors -s https://aidatagate.ru/search \
-H "Authorization: Bearer $AIDATAGATE_KEY" -H "Content-Type: application/json" \
-d '{"query": "ключевая ставка ЦБ"}'
Примеры
curl
curl -s https://aidatagate.ru/search \
-H "Authorization: Bearer rs-dev-0000000000000000" \
-H "Content-Type: application/json" \
-d '{"query": "льготная ипотека условия 2026", "max_results": 5}'
Python, httpx
import httpx
resp = httpx.post(
"https://aidatagate.ru/search",
headers={"Authorization": "Bearer rs-dev-0000000000000000"},
json={"query": "postgres vacuum full блокировки", "max_results": 5},
timeout=30,
)
for item in resp.json()["results"]:
print(item["score"], item["title"], item["url"])
LangChain
Инструмент для агента - тридцать строк на httpx, отдельный пакет не нужен.
import httpx
from langchain_core.tools import tool
BASE = "https://aidatagate.ru"
KEY = "rs-dev-0000000000000000"
@tool
def aidatagate(query: str, max_results: int = 5) -> str:
"""Поиск в вебе: возвращает фрагменты страниц по запросу."""
resp = httpx.post(
f"{BASE}/search",
headers={"Authorization": f"Bearer {KEY}"},
json={"query": query, "max_results": max_results},
timeout=30,
)
resp.raise_for_status()
return "\n\n".join(
f"[{i}] {r['title']} ({r['url']})\n{r['content']}"
for i, r in enumerate(resp.json()["results"], start=1)
)
n8n (HTTP Request node)
- Method:
POST - URL:
https://aidatagate.ru/search - Authentication: Header Auth, имя
Authorization, значениеBearer rs-dev-0000000000000000 - Body (JSON):
{"query": "{{ $json.query }}", "max_results": 5}
Подключение через MCP
MCP-сервер работает в том же процессе по адресу https://aidatagate.ru/mcp
(транспорт streamable HTTP) и даёт агентам четыре инструмента - search, extract,
crawl и map с теми же параметрами, что и у HTTP-эндпоинтов (кроме instructions
у crawl/map: в MVP параметр всё равно игнорируется, поэтому в MCP-схеме его нет).
Авторизация - тот же API-ключ в заголовке Authorization: Bearer, списание кредитов
как у обычных запросов (в журнале такие вызовы помечены mcp:search / mcp:extract /
mcp:crawl / mcp:map). Неверный ключ
даёт ошибку авторизации (HTTP 401), при нехватке кредитов инструмент возвращает
понятное сообщение с кодом insufficient_credits.
Четыре поля для ручного добавления
Клиенты, которые добавляют сервер формой, а не файлом конфигурации, спрашивают поля по отдельности:
| Поле | Значение |
|---|---|
| Адрес сервера (URL) | https://aidatagate.ru/mcp |
| Транспорт | HTTP, streamable (не SSE и не stdio) |
| Имя заголовка | Authorization либо X-API-Key |
| Значение заголовка | Bearer rs-ВАШ_КЛЮЧ либо просто rs-ВАШ_КЛЮЧ |
Больше ничего вписывать не нужно: сервер авторизует по этому единственному
заголовку. В Authorization значение вписывается целиком, вместе со словом
Bearer и пробелом; в X-API-Key - голый ключ, и ошибиться там негде.
У Claude Desktop поле для заголовка живёт в разделе Request headers диалога
добавления коннектора, и вписывать туда надо значение целиком, вместе со словом
Bearer, см. раздел "Claude Desktop" ниже.
Готовый фрагмент со своим ключом можно скопировать в кабинете на странице
"API-ключи". Ниже те же фрагменты с плейсхолдером rs-ВАШ_КЛЮЧ.
Claude Code
Одной командой:
claude mcp add --transport http aidatagate https://aidatagate.ru/mcp \
--header "Authorization: Bearer rs-ВАШ_КЛЮЧ"
Либо файлом ~/.claude.json или .mcp.json в корне проекта:
{
"mcpServers": {
"aidatagate": {
"type": "http",
"url": "https://aidatagate.ru/mcp",
"headers": {
"Authorization": "Bearer rs-ВАШ_КЛЮЧ"
}
}
}
}
Проверка: claude mcp list должен показать aidatagate со статусом connected,
после чего агенту доступны инструменты search, extract, crawl и map.
Claude Desktop
Два способа, оба без правки файлов конфигурации.
Способ 1: вход через OAuth (работает у всех)
Customize -> Connectors -> Add custom connector:
| Поле диалога | Что вписать |
|---|---|
| Name | AI Data Gate |
| MCP server URL | https://aidatagate.ru/mcp |
| Authentication | Sign in now (или оставьте определённое автоматически) |
Ключ вписывать никуда не нужно: Claude сам найдёт наш сервер авторизации,
откроет страницу входа, вы войдёте своей почтой и нажмёте "Разрешить". Доступ
после этого виден в кабинете на странице "API-ключи" под именем
OAuth: <название клиента> и отзывается там же одной кнопкой.
Так подключаться и удобнее, и безопаснее: ключ не проходит через буфер обмена и не оседает в скриншотах.
Способ 2: ключом в заголовке
Если в диалоге есть раздел Request headers (он в бете и открыт не всем):
| Поле диалога | Что вписать |
|---|---|
| Authentication | No sign-in |
| Request headers | имя x-api-key, значение rs-ВАШ_КЛЮЧ |
Заголовок x-api-key принимает голый ключ. Если выбрать authorization, то
значение надо вписывать целиком - Bearer rs-ВАШ_КЛЮЧ, вместе со словом и
пробелом: Claude отправляет значение дословно и схему сам не подставляет. Из-за
этого x-api-key надёжнее.
Настройки авторизации после добавления не редактируются. Чтобы сменить ключ, коннектор удаляют и добавляют заново.
Если не подошло ни то, ни другое
Мост mcp-remote (нужен Node.js). В claude_desktop_config.json:
{
"mcpServers": {
"aidatagate": {
"command": "npx",
"args": [
"-y",
"mcp-remote@latest",
"https://aidatagate.ru/mcp",
"--header",
"Authorization:${AIDATAGATE_AUTH}"
],
"env": {
"AIDATAGATE_AUTH": "Bearer rs-ВАШ_КЛЮЧ"
}
}
}
}
Значение заголовка вынесено в env не для красоты: в args строка с пробелом
после двоеточия разбирается неправильно, поэтому подставляется переменной.
Свой stdio-сервер без Node.js - обёртка sdk/mcp/aidatagate_mcp.py, см.
раздел "Клиенты без HTTP-транспорта" ниже. Ключ там передаётся переменной
окружения.
После правки файла Claude Desktop нужно перезапустить.
Вход через OAuth вместо ключа
Клиенту, который умеет OAuth, ключ не нужен: он сам найдёт сервер авторизации и проведёт человека через вход. Реализовано по спецификации авторизации MCP - OAuth 2.1, authorization code с PKCE (S256).
| Документ или точка | Адрес |
|---|---|
| Метаданные ресурса (RFC 9728) | /.well-known/oauth-protected-resource |
| Метаданные сервера авторизации (RFC 8414) | /.well-known/oauth-authorization-server |
| Регистрация клиента (RFC 7591) | POST /oauth/register |
| Страница согласия | GET /oauth/authorize |
| Обмен кода на токен | POST /oauth/token |
Что стоит знать:
- Секрет клиенту не выдаётся: MCP-клиенты публичные, подлинность доказывает
PKCE. Метод
plainне поддерживается, толькоS256. - Выданный токен доступа - это обычный API-ключ сервиса. Он виден в кабинете на
странице "API-ключи" под именем
OAuth: <название клиента>, тарифицируется как любой другой и отзывается одной кнопкой. Отзыв ключа обрывает доступ приложения немедленно. - Обращение к
/mcpбез токена отвечает 401 с заголовкомWWW-Authenticate, в котором указан адрес метаданных - по нему клиент и находит, где входить.
Cursor
Файл ~/.cursor/mcp.json (или .cursor/mcp.json в проекте):
{
"mcpServers": {
"aidatagate": {
"url": "https://aidatagate.ru/mcp",
"headers": {
"Authorization": "Bearer rs-ВАШ_КЛЮЧ"
}
}
}
}
Continue
В config.json (раздел experimental):
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "streamable-http",
"url": "https://aidatagate.ru/mcp",
"headers": {
"Authorization": "Bearer rs-ВАШ_КЛЮЧ"
}
}
}
]
}
}
Клиенты без HTTP-транспорта (stdio)
Для клиентов, которые запускают MCP-серверы только локальным процессом, есть
stdio-обёртка sdk/mcp/aidatagate_mcp.py: она проксирует те же четыре инструмента
в HTTP API. Установка и примеры конфигурации - в sdk/mcp/README.md; ключ и
адрес задаются переменными окружения AIDATAGATE_API_KEY и AIDATAGATE_BASE_URL.
Сколько стоит запрос
Цена поиска берётся за каждые 10 результатов: топ-10 - один раз цену глубины,
топ-20 - два раза. search_depth=advanced дороже basic, поэтому топ-20 в
режиме advanced стоит вчетверо дороже топ-10 в basic. Сколько ссылок поисковик
отдаёт за один заход, на цену не влияет: платить за то, как нам удобнее забрать
выдачу, клиент не должен. Наценка за include_answer берётся один раз на запрос -
ответ в выдаче один.
Актуальные цифры - на странице /pricing, их правит администратор.
Кэш и тарификация
Выдача и страницы кэшируются: повторный запрос приходит за доли секунды вместо секунд и стоит дешевле - за то, что не пришлось запрашивать заново, денег не берут.
/searchтарифицируется по ценеsearch_cached(по умолчанию 0), если выдача взята из кэша целиком, то есть к поисковику не было ни одного обращения. Частичное попадание считается по обычной цене./extractтарифицируется по ценеextract_cached_per5(по умолчанию 0), если все URL запроса отданы кэшем страниц. Скидка «всё или ничего»: считать группы отдельно нельзя, округление вверх в каждой сделало бы запрос с частичным попаданием дороже, чем он же без кэша.- Наценка за
include_answerприбавляется и к кэшированному запросу, если ответ написала модель: кэшируется выдача, а не пересказ.
Что именно случилось, видно в ответе: заголовок X-AIDataGate-Cache равен hit
или miss, а X-Credits-Charged показывает фактическое списание. Резерв на
время обработки берётся по полной цене - заранее неизвестно, попадёт ли запрос
в кэш, поэтому баланса должно хватать на обычный запрос даже перед бесплатным.
Актуальные цены - на странице /pricing, их правит администратор.
Ограничения
- Лимит запросов: 60 в минуту на ключ (заголовок ответа при превышении - HTTP 429).
- Тело запроса - до 1 МБ (
MAX_REQUEST_BODY_BYTES), иначе HTTP 413. - Максимум 20 URL в
/extract, тело страницы до 2 МБ, таймаут скачивания 4 с. /crawlи/map: не больше 50 страниц за обход (MVP), глубина до 3, бюджет обхода 60 с.- Робот представляется как
AIDataGateBot/0.1и уважает robots.txt (страница/bot).