AI Data Gate API

Поисковый API для AI-агентов: принимает запрос, собирает выдачу из внешних источников, скачивает и очищает страницы, ранжирует фрагменты под запрос и возвращает готовый для LLM контекст. Интерфейс - обычный JSON по HTTP, плюс MCP-сервер для ИИ-ассистентов: подключение описано в разделе «Подключение через MCP».

  • Базовый адрес: https://aidatagate.ru
  • Авторизация: заголовок Authorization: Bearer <ключ>. Принимается также X-API-Key: <ключ> (голый ключ, без слова Bearer - так проще настраивать клиентов, которые заполняются формой) и, для совместимости, поле api_key в теле запроса. Ключ создаётся в кабинете на странице «API-ключи», полное значение показывается один раз. MCP-клиенты могут вместо ключа войти через OAuth, см. «Подключение через MCP»
  • Формат: JSON, UTF-8
  • Успешный ответ POST /search, /search/images, /extract, /crawl, /map и операций скачивания (/download, /download/image, /download/gif) несёт заголовки X-AIDataGate-Version (версия API), X-Credits-Charged (списано за этот запрос) и X-Credits-Remaining (остаток на балансе после списания). У ответов с ошибкой и у GET /usage этих заголовков нет. Цены - в разделе «Сколько стоит запрос»
  • request_id в теле ответа и заголовок X-Request-ID - разные идентификаторы: первый обозначает запрос в журнале, второй - HTTP-обмен. X-Request-ID есть и у ответов с ошибкой, где request_id нет. В обращении в поддержку указывайте request_id, а для ошибки - X-Request-ID

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

  1. Зарегистрируйтесь на https://aidatagate.ru/register и в кабинете на странице «API-ключи» создайте ключ. Полное значение показывается один раз - сразу сохраните его. Каждый месяц на баланс начисляется бесплатная квота кредитов (размер - на /pricing).

  2. Отправьте первый запрос:

    curl -s https://aidatagate.ru/search \
      -H "Authorization: Bearer rs-ВАШ_КЛЮЧ" \
      -H "Content-Type: application/json" \
      -d '{"query": "ключевая ставка ЦБ", "max_results": 5}'
    
  3. В ответе - список results: у каждого документа title, url, score и content - фрагменты страницы, которые отвечают на запрос. Их можно сразу отдавать модели.

Без кода запрос собирается в песочнице кабинета: она показывает ответ, цену запроса и готовый код на curl, Python и JavaScript. ИИ-ассистента (Claude, Cursor, Continue) подключают через MCP, см. «Подключение через MCP».

Как выбрать параметры#

Большинству задач хватает значений по умолчанию: search_depth=basic, max_results=5, без ответа и без полного текста. Ниже - что меняют остальные параметры и сколько это стоит. Цены в этом разделе - стартовые, действующие - на странице Цены.

basic или advanced#

search_depth в /search и extract_depth в /extract выбирают между быстрым и дешёвым режимом (basic) и режимом полноты (advanced). advanced дороже (по стартовому тарифу - вдвое) и работает дольше.

Поиск, search_depth:

basic advanced
Источники выдачи Один - основной для языка запроса; резервный подключается, если основной не ответил или нашёл меньше 3 результатов Два сразу, параллельно: выдачи сливаются, дубли убираются, страница, найденная обоими, поднимается выше
content каждого результата Один лучший фрагмент страницы До 3 лучших фрагментов в порядке текста (chunks_per_source, от 1 до 5) - сколько поместится, см. ниже
Цена (стартовая) 1 кредит за каждые 10 результатов 2 кредита за каждые 10 результатов
Скорость Быстрее Медленнее: ждёт оба источника

Глубина не влияет на include_raw_content, include_answer и число результатов: они работают одинаково. В новостях (topic=news) источник выдачи один и при advanced - отличаются только фрагменты.

Сколько текста в content. На фрагменты всего ответа отведено 6 000 токенов, они делятся поровну между найденными результатами. Фрагмент - около 400 токенов. При 5 результатах на каждый приходится 1 200 токенов, и в advanced обычно помещаются все три фрагмента; при 10 - 600, помещается один-два; при 20 - 300, и даже лучший фрагмент обрезается. advanced берёт фрагменты от лучшего к худшему, пока они помещаются в долю результата, и выводит их в порядке текста; лучший попадает всегда. Поэтому advanced полнее всего при 5 результатах, а не при 20.

Когда брать advanced: нужна полнота, а не первый подходящий ответ (обзор темы, сравнение, узкий или редкий запрос); модели нужно больше контекста с каждой страницы; basic вернул мало или однобокую выдачу.

Извлечение, extract_depth:

basic advanced
Текст страницы До 50 000 символов До 200 000 символов
PDF Первые 20 страниц Первые 60 страниц
Ожидание одного URL До 15 с До 30 с
Кэш страниц Может отдать копию возрастом до 7 дней - это быстро Всегда скачивает заново
Страница не открылась (тайм-аут, обрыв, 403, 429, 5xx) Одна повторная попытка по запасному каналу, только для https Повторы по другим каналам
Цена (стартовая) 1 кредит за каждые 5 успешно извлечённых URL 2 кредита за каждые 5 успешно извлечённых URL

Когда брать advanced: длинные статьи и документация, большие PDF, сайты, которые с первого раза не открываются; нужна свежая версия страницы, а не копия из кэша.

Сколько результатов#

max_results - от 1 до 20, по умолчанию 5. Цена считается за каждые 10 запрошенных результатов: от 1 до 10 - как за десяток, от 11 до 20 - вдвое больше. Агенту обычно хватает 5-10: каждый лишний результат - лишние токены в контексте модели.

Ответ по найденному#

include_answer добавляет поле answer - короткий ответ со ссылками на результаты [1], [2]. Ответ моделью (answer_source=llm) стоит +2 кредита; если модель недоступна, приходит бесплатная выжимка из найденных фрагментов (answer_source=extractive). Подробности - в описании /search.

Фрагменты или полный текст#

content - уже отобранные под запрос фрагменты: смысл страницы доходит до модели в разы короче. include_raw_content добавляет полный очищенный текст каждой страницы (до 50 000 символов) - он нужен, когда страницы читают целиком. Если нужны известные страницы, а не поиск, берите /extract.

Типовые наборы#

Задача Запрос
Факт или короткая справка для агента /search, basic, max_results 5
Обзор темы, сравнение, узкий запрос /search, advanced, max_results 5
Свежие новости /search, topic=news, time_range day или week
Прочитать известные страницы /extract, basic
Длинные документы, большие PDF, капризные сайты /extract, advanced
Собрать раздел сайта /crawl с select_paths; только список ссылок - /map

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": [],
  "time_range": null,
  "country": "ru"
}
Поле Тип По умолчанию Описание
query string обязательно Запрос, до 400 символов
search_depth basic / advanced basic advanced опрашивает сразу два источника выдачи (кроме topic=news) и склеивает в content несколько лучших фрагментов документа (chunks_per_source); стоит вдвое дороже. Сравнение - в разделе basic или advanced
topic general / news general news - выдача с упором на свежие публикации. Итоговый порядок и здесь - по релевантности (score), свежесть учитывается в нём с небольшим весом; отсечь старое - параметрами days или time_range
max_results int 1-20 5 Сколько документов вернуть. Тарифицируется за каждые 10: топ-20 стоит вдвое дороже топ-10
include_answer bool / basic / advanced false Короткий ответ по найденным фрагментам в поле answer; true - то же, что basic. См. answer_source ниже
include_raw_content bool / markdown / text false Полный очищенный текст документа в raw_content, до 50 000 символов: true и markdown - в markdown, text - простым текстом
include_domains / exclude_domains string[] [] Фильтр по доменам, до 50 в каждом списке; домен покрывает и свои поддомены
days int 1-365 - Свежесть: не старше N дней
time_range day / week / month / year - Свежесть окном: 1, 7, 31 или 365 дней
start_date / end_date YYYY-MM-DD - Явное окно дат публикации, включительно
country string ru Страна: ISO-код (ru, kz) или полное название (Russia, Россия)
language string - Язык выдачи; по умолчанию определяется по самому запросу
filter_by_language bool false Отбросить документы на других языках. Язык документа берётся из разметки страницы (атрибут lang, метаданные); страницы, где он не указан, остаются
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.ico его домена) в поле favicon каждого результата
include_usage bool false Цена запроса в кредитах в поле usage ответа: {"credits": N}
auto_parameters bool false Сервис сам подбирает то, чего вы не задали: сейчас это topic=news для запросов о текущих событиях. Платное (search_depth=advanced) сам не включает никогда. Что выбрано - в поле auto_parameters ответа

Неизвестные поля игнорируются, сервис на них не падает. null читается как «параметр не задан»: обёртки вокруг SDK шлют его вместо отсутствующего ключа. Значения, которых у сервиса нет, заменяются ближайшими: search_depth fast и ultra-fast работают как basic, topic=finance - как general. Числа вне диапазона (max_results, days, chunks_per_source) прижимаются к границе, а не дают 400: "max_results": 50 обрабатывается как 20.

Фильтры свежести. days, time_range, start_date и end_date - не только подсказка поисковику, но и отсечение: документ с известной датой публикации вне окна в ответ не попадает.

  • days и time_range отсчитываются от сегодняшнего дня. Заданные вместе, они окно не сужают: days перекрывает time_range, и тот не учитывается.
  • start_date и end_date задают границы явно. Вместе с days или time_range берётся пересечение: окно начинается с более поздней из двух дат.
  • Документы без даты. days и time_range - пожелание свежести: страницы с неизвестной датой публикации из выдачи не выбрасываются, иначе она обмелела бы (дата известна меньше чем у половины страниц). start_date/end_date - точный запрос: там документ с неизвестной датой не отвечает условию и в выдачу не попадает.

Фильтр по доменам. include_domains в режиме filter уходит прямо в запрос к поисковику оператором 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,
      "published_date": "Tue, 01 Sep 2026 10:00:00 GMT",
      "favicon": null,
      "raw_content_truncated": false
    }
  ],
  "response_time": 1.84,
  "request_id": "0f2a...",
  "answer_error": null,
  "answer_source": null,
  "auto_parameters": null,
  "usage": null
}

Результаты отсортированы по score по убыванию. request_id, answer_error, answer_source и raw_content_truncated - наши расширения; сторонние клиенты их просто не читают. auto_parameters и usage заполняются, только если о них попросили. Если страница документа не скачалась (не успела, закрыта в robots.txt или недоступна), его content собран из фрагмента выдачи, а raw_content равен null.

answer_source говорит, чем получен answer:

  • llm - пересказ найденных фрагментов моделью, стоит +2 кредита. Длина такого ответа от include_answer="advanced" не зависит;
  • extractive - выжимка из найденных фрагментов без модели: несколько предложений со ссылками на источники [1], [2] по номерам результатов. Тарифицируется как обычный поиск, без наценки. Приходит, когда ответ моделью недоступен: модель отключена, не ответила вовремя или вернула ошибку. Здесь include_answer="advanced" даёт выжимку длиннее: до 6 предложений и 1600 символов вместо 3 и 700;
  • null - ответ не получился (например, по фрагментам нечего сказать), причина в answer_error.

С include_answer наценка за ответ модели резервируется заранее, даже если в итоге придёт бесплатная выжимка, см. «Сколько стоит запрос».

Выбор источника выдачи не предусмотрен, и это решение, а не упущение. Параметра source нет, имена поисковых движков наружу не отдаются, и по ответу нельзя определить, кто именно его нашёл. Сервис отвечает за результат, а не за то, чьими руками он получен: набор источников меняется, часть из них работает по договорам, и обещать клиенту конкретный движок значит обещать то, чем мы не управляем. Источники подбираются автоматически: по языку запроса (или language), стране (country), topic и search_depth.

exact_match фильтрует по-настоящему: в выдаче остаются только страницы, где фраза из кавычек встречается дословно (ищется в заголовке, тексте страницы и сниппете выдачи). Без кавычек в запросе параметр даёт 400. Выдача при этом может оказаться пустой - это правильный ответ на запрос про фразу, которой нет.

Картинки. include_images отдаёт картинки найденных страниц, до пяти на ответ: по кругу с разных страниц выдачи, чтобы ответ не описывал один источник. С каждой страницы первой идёт объявленная ею главная картинка (og:image), затем иллюстрации по порядку. Счётчики посещений (1x1) и data:-картинки отбрасываются. Картинки хранятся вместе с текстом страницы, поэтому приходят и для страниц из кэша.

raw_content_truncated поднимается, когда текст документа обрезан: по лимиту 50 000 символов (в /extract с extract_depth=advanced - 200 000) или потому, что у PDF прочитаны только первые 20 страниц (60 при advanced). Обрезка идёт по границе слова.

POST /search/images#

Ищет картинки по текстовому описанию.

Параметр Тип По умолчанию Описание
query string - Что должно быть на картинке, до 400 знаков
max_results int 1-30 10 Сколько картинок вернуть
{"query": "рыжий кот на подоконнике", "max_results": 5}

Ответ:

{
  "query": "рыжий кот на подоконнике",
  "results": [
    {
      "url": "https://cats.example.ru/post/1",
      "image_url": "https://img.cats.example.ru/1.jpg",
      "title": "Рыжий кот на подоконнике",
      "width": 1024,
      "height": 768
    }
  ],
  "response_time": 0.84,
  "request_id": "0f2a..."
}
  • image_url - адрес самой картинки; его можно сразу передать в /download/image.
  • url - страница, где картинка найдена. Бывает null: не каждый источник её сообщает. width и height - тоже не всегда.
  • Контент для взрослых отфильтровывается там, где источник это умеет.
  • Ограничения те же, что у поиска: запрещённая тема - 451, картинки и страницы с доменов из реестра запрещённых ресурсов в выдачу не попадают. Выдача кэшируется до 6 часов.

Списание: 1 кредит за запрос, сколько бы картинок ни нашлось. Все источники недоступны - 503 без списания.

POST /extract#

{"urls": ["https://habr.com/ru/articles/1/"], "extract_depth": "basic", "format": "markdown"}
Поле Тип По умолчанию Описание
urls string[] / string обязательно От 1 до 20 адресов, каждый до 2048 символов; одну ссылку можно передать строкой
extract_depth basic / advanced basic advanced - больше текста, дольше ожидание и выше шанс, что страница откроется; стоит вдвое дороже. Сравнение - в разделе basic или advanced
format markdown / text markdown Формат raw_content
instructions string - Что собрать со страницы, до 2000 символов: модель вернёт JSON в поле structured, см. ниже
include_images bool false Картинки страницы (до пяти) - в поле images
include_favicon bool false Адрес значка сайта в поле favicon
include_usage bool false Цена запроса в кредитах в поле usage ответа

Ответ: {"results": [{"url", "title", "raw_content", "raw_content_truncated", "images", "favicon", "structured"}], "failed_results": [{"url", "error"}], "response_time", "request_id", "usage", "warning"}. Ошибка одного URL не ломает весь запрос: он уходит в failed_results и не оплачивается - списание идёт только за успешно извлечённые URL. warning - наше расширение: например, instructions применены не ко всем URL.

extract_depth=advanced - для трудных страниц: больше данных и выше шанс успеха, но дольше и дороже. Чем он отличается от basic по тексту, PDF, ожиданию, кэшу и повторам - в таблице раздела basic или advanced.

format=markdown (по умолчанию) отдаёт разметку, format=text - простой текст: если сама страница является markdown-документом, разметка снимается, а не просачивается в «текст». PDF извлекается наравне с HTML: файл до 32 МБ, первые 20 страниц (60 при advanced), с пометкой raw_content_truncated, если документ длиннее. У PDF без заголовка в метаданных в title попадает имя файла из адреса: пустая строка не говорит ни о чём.

Причины в failed_results. Поле error объясняет, почему URL не извлечён:

error Что случилось
HTTP 404 (и другие коды) Сайт ответил ошибкой
robots.txt disallows fetching Сайт закрыл страницу от роботов в robots.txt
url is blocked by the operator domain list Домен в блок-листе сервиса
domain is in the prohibited registry Домен в реестре запрещённых ресурсов
redirected to a blocked domain Страница перенаправила на домен из блок-листа или реестра
port not allowed: N Порт не из разрешённых: 80, 443, 8080, 8443
scheme not allowed: ..., no host in URL Адрес не http(s) или без домена
timeout > Ns Страница не уложилась в отведённое время
empty content Страница открылась, но текста в ней не нашлось
address could not be fetched Страницу не удалось скачать по сетевой причине

Подробности сетевых сбоев (имя не находится в DNS, адрес ведёт в частную сеть, обрыв соединения, слишком много редиректов) наружу не отдаются: вместо них приходит общая фраза address could not be fetched.

structured заполняется ТОЛЬКО вместе с instructions: это JSON, который модель собирает со страницы по вашей инструкции; если модель ответила не JSON, в поле её текст как есть. Без instructions поле равно null, и это не признак поломки - просто инструкции не было. Пример:

{"urls": ["https://example.com/prices"], "instructions": "верни массив тарифов: name, price"}
  • Модель разбирает не больше 5 URL за запрос: первые 5 успешно извлечённых, в порядке urls. У остальных structured равно null, а в ответе появляется warning.
  • Модель читает первые 15 000 символов извлечённого текста страницы.
  • Цена - +2 кредита за каждый URL, который модель разобрала, сверх цены извлечения. Если модель не ответила или вернула ошибку, у этого URL structured равно null, и разбор не оплачивается.
  • Если модель на сервере отключена, 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 Максимум страниц за обход; считаются и те, что не открылись
select_paths string[] [] Regex по пути URL: обходить только совпавшие; до 20 шаблонов по 200 символов
exclude_paths string[] [] Regex по пути URL: совпавшие не обходить
instructions string - Принимается для совместимости со сторонними клиентами и игнорируется: в ответе появляется warning

Числа вне диапазона (max_depth, max_breadth, limit) прижимаются к границе, а не дают 400: "limit": 100 обойдёт не больше 50 страниц. Отказ ломал бы работающий код там, где достаточно вернуть максимум. Битый regex в select_paths / exclude_paths - ошибка 400 bad_request. Шаблон ищется в пути URL в любом месте, как re.search; если сравнение одного пути длится дольше 50 мс, обход останавливается и возвращает собранное вместе с warning.

Ответ:

{
  "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 страницы, до 50 000 символов. request_id и warning - наши расширения. Каждая страница проходит те же проверки, что и в /search: SSRF-защита, robots.txt, блок-лист и реестр запрещённых ресурсов, лимиты одновременных запросов на хост; на одну страницу отводится до 6 с. Бинарные файлы (pdf, картинки, архивы) не обходятся. Редирект, уводящий с корневого домена стартового URL, отбрасывает страницу целиком. Бюджет всего обхода - 60 секунд; по истечении возвращается собранное к этому моменту, поэтому тайм-аут на стороне клиента ставьте не меньше 60 с. Если не открылся сам стартовый URL, ответ - 400 с объяснением (could not fetch start url: HTTP 404), а не пустой список: пустой ответ читается как «сайт обойдён, брать нечего», и это худший вид ошибки для агента. Такой запрос не тарифицируется. Списание: 1 кредит за каждые 5 страниц в ответе, округление вверх. Перед обходом резервируется цена по limit, см. «Сколько стоит запрос».

POST /map#

Тот же обход, что и /crawl, но без извлечения контента - только карта ссылок. Параметры те же (url, max_depth, max_breadth, limit, select_paths, exclude_paths); limit здесь - максимум URL в карте. Страницы последнего уровня глубины не скачиваются: при max_depth=1 в карту попадают стартовая страница и ссылки с неё.

{"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; бюджет обхода тот же, 60 с. Списание: 1 кредит за каждые 10 URL в ответе, округление вверх; резерв - по limit.

POST /download#

Скачивает mp3 по ссылке целиком и отдаёт сам файл. Скачивание идёт теми же каналами и с теми же проверками адреса, что и страницы в /extract.

Параметр Тип Описание
url string Ссылка на mp3
curl -X POST https://aidatagate.ru/download \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://example.ru/podcast/episode-12.mp3"}' -o episode-12.mp3

Ответ - сам файл: Content-Type: audio/mpeg, имя файла (из адреса) - в Content-Disposition. Русское имя приходит дважды: как есть в filename* (его читают браузеры, Python requests и большинство библиотек) и латиницей в filename - для клиентов, которые filename* не понимают, например curl -OJ. Тела, где вернуть request_id, у такого ответа нет: идентификатор запроса из журнала приходит в заголовке X-Request-ID.

Что проверяется:

  • Файл до 50 МБ. Больше - ошибка file too large (> 50 MB), обрезанный файл не отдаётся. Если сайт сообщает размер заранее, лишнее не скачивается вовсе.
  • Это mp3: по заголовку ответа (audio/* или application/octet-stream; HTML и прочие типы - ошибка сразу, без скачивания) и по первым байтам файла (тег ID3 или кадр MPEG Layer III). Иначе - not an mp3 file.
  • На файл целиком отводится до 120 с, вместе с повторами по другим каналам.

Не скачалось - HTTP 422 с кодом download_failed и причиной в detail.error: HTTP 404, unsupported content-type: text/html, not an mp3 file, file too large (> 50 MB), timeout > 120s и т. п.

Списание: 1 кредит за каждые начатые 10 МБ файла, неудача не оплачивается. Размер до скачивания неизвестен, поэтому резерв - цена файла в 50 МБ, 5 кредитов. Через MCP файл не отдаётся: ассистенту его не передать.

POST /download/image#

Скачивает картинку по ссылке и отдаёт её в нужном формате.

Параметр Тип По умолчанию Описание
url string - Ссылка на картинку
format jpg / png / gif jpg Во что преобразовать; jpeg - то же, что jpg
curl -X POST https://aidatagate.ru/download/image \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://example.ru/photo.webp", "format": "png"}' -OJ

Принимаются JPEG, PNG, GIF, WebP, BMP, TIFF и AVIF; SVG (векторная графика) - ошибка.

  • jpg: прозрачность заливается белым, картинка поворачивается по EXIF - как её показал бы браузер.
  • png: прозрачность сохраняется.
  • gif: первый кадр. Анимация целиком - в /download/gif.

Ответ - сам файл (image/jpeg, image/png или image/gif). Имя - из адреса с новым расширением (photo.webp превращается в photo.jpg), русское имя приходит так же, как у /download.

POST /download/gif#

Скачивает анимацию по ссылке и отдаёт анимированный GIF со всеми кадрами.

Параметр Тип Описание
url string Ссылка на анимацию

Анимированный GIF отдаётся как есть, без пережатия. Анимированные WebP и APNG переводятся в GIF с теми же длительностями кадров и повтором. Картинка без анимации - ошибка not an animated image.

Пределы /download/image и /download/gif. Файл до 20 МБ - и исходный, и готовый; до 50 млн пикселей. У анимации, которая переводится в GIF, - до 300 кадров и до 50 млн пикселей во всех кадрах вместе. Ошибка - HTTP 422 download_failed с причиной в detail.error: not an image, unsupported image format, broken image, image too large (> 50 MP), too many frames (> 300), not an animated image, converted file too large (> 20 MB), unsupported content-type: image/svg+xml, а также сетевые, как у /download.

Списание - как у /download: 1 кредит за каждые начатые 10 МБ отданного файла, неудача не оплачивается. Резерв - цена файла в 20 МБ, 2 кредита.

GET /usage#

Баланс кредитов, расход за текущий месяц и по дням за последние 30 дней. Балансы и расход - по всей учётной записи: со всех её ключей, включая вызовы через MCP и песочницу кабинета. Запрос бесплатный, но учитывается в лимите запросов ключа.

{
  "free_balance": 820,
  "paid_balance": 5000,
  "quota_renews_at": "2026-09-30T21:00:00Z",
  "month_credits_used": 180,
  "month_requests": 143,
  "rate_limit_per_minute": 60,
  "days": [
    {"date": "2026-09-25", "credits": 12, "requests": 9},
    {"date": "2026-09-24", "credits": 30, "requests": 21}
  ]
}
Поле Что это
free_balance Остаток бесплатных кредитов
paid_balance Остаток купленных кредитов
quota_renews_at Когда обновится бесплатная квота: 1-е число следующего месяца, 00:00 по Москве; время в UTC
month_credits_used Сколько кредитов списано с начала текущего месяца; месяц считается по Москве, как и обновление квоты
month_requests Сколько запросов за тот же месяц записано в журнал
rate_limit_per_minute Лимит запросов в минуту у ключа, которым сделан вызов
days Расход по дням за последние 30 дней, новые сверху: date (день по UTC), credits, requests. Дни без запросов в список не попадают

Журнал запросов, по которому считаются эти цифры, хранится 35 дней.

GET /healthz#

Состояние сервиса, без авторизации: 200 и "status": "ok" - всё работает, 503 и "status": "degraded" - недоступно хранилище. Поле search показывает, есть ли сейчас живой источник выдачи; на код ответа оно не влияет.

Ошибки#

Единый формат: {"detail": {"error": "..."}, "code": "..."}. Текст ошибки всегда лежит в detail.error - именно оттуда его читают совместимые SDK. code - наше расширение, сторонние клиенты его просто не читают. Некоторые ошибки добавляют свои поля: errors - ошибки валидации, required и available - 402, rule - 451. Клиенту, который шлёт Accept: text/html (браузеру), ошибка приходит HTML-страницей.

Ошибки валидации (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 Когда
bad_request 400 Некорректные параметры; у /crawl и /map - ещё и недоступный стартовый URL
invalid_api_key 401 Ключ неверный, отозван или не передан; учётная запись заблокирована или её почта не подтверждена
insufficient_credits 402 Не хватает кредитов на резерв запроса, в теле required и available
not_found 404 Нет такого адреса
method_not_allowed 405 Не тот метод, например GET /search вместо POST
bad_request 413 Тело запроса больше 1 МБ, текст request body too large
download_failed 422 /download, /download/image, /download/gif: файл не скачался, не того вида или не преобразовался, причина в detail.error
rate_limited 429 Превышен лимит запросов в минуту; заголовок Retry-After - сколько секунд ждать
forbidden_query 451 Запрос отклонён: тема не обслуживается
internal 500 Внутренняя ошибка
provider_unavailable 503 Все источники выдачи недоступны; текст всегда All search providers failed, без подробностей

Про insufficient_credits. Тело ответа:

{
  "detail": {"error": "Not enough credits for this request"},
  "code": "insufficient_credits",
  "required": 10,
  "available": 3
}

required - сколько кредитов нужно зарезервировать под этот запрос, available - сколько доступно сейчас: бесплатные плюс купленные кредиты минус резервы запросов, которые выполняются параллельно. Как считается резерв - в разделе «Сколько стоит запрос».

Про forbidden_query. Небольшая часть формулировок к обработке не принимается - это прямые запросы на покупку или изготовление того, оборот чего запрещён. Отказ приходит кодом 451, кредиты за такой запрос не списываются, а в теле ответа есть поле rule с именем сработавшего правила - по нему поддержка разберёт случай, если отказ ошибочный. Повторять такой запрос бессмысленно, ответ не изменится.

Ресурсы, доступ к которым ограничен по законодательству РФ. Сервис с ними не работает: сверка с реестром таких ресурсов (обновляется раз в сутки) идёт во всех ручках и в MCP-инструментах, в том числе после редиректов.

  • /search исключает их молча: запрос выполняется, просто часть источников в выдачу не попадает.
  • /extract отдаёт такой URL в failed_results с причиной domain is in the prohibited registry.
  • /crawl и /map: стартовый URL из реестра - ошибка 400, ссылки на такие домены не обходятся и в карту не попадают.
  • Страница, которая редиректом уводит на такой ресурс, не отдаётся: в /extract это причина redirected to a blocked domain, в /search вместо текста страницы остаётся фрагмент из выдачи, в обходе страница пропускается.

Так же действует и собственный блок-лист доменов сервиса; в /extract его причина - url is blocked by the operator domain list.

Повтор запроса: что повторять и как#

В редких окнах перегрузки клиент может получить 502/503/504 или обрыв соединения вместо ответа. Это сетевой сбой, а не отказ API: повтор через паузу проходит. Агентные клиенты сами обычно не повторяют, поэтому ретрай стоит заложить в код.

Повторять имеет смысл 429, 502, 503, 504 и сбои соединения - с удвоением паузы: 1 с, затем 2 с. На 429 сервис присылает заголовок Retry-After - сколько секунд осталось до начала следующей минуты, когда обнулится счётчик лимита; ждать надо столько. Повторять 400, 401, 402, 404, 413 и 451 бессмысленно: ответ не изменится.

Повтор может быть оплачен дважды. Ключа идемпотентности у API нет: каждый запрос, дошедший до обработки, тарифицируется отдельно. Если ответ потерян уже после отправки запроса (обрыв на чтении ответа, тайм-аут на стороне клиента, 502 или 504), запрос мог выполниться и списать кредиты, и повтор спишет их ещё раз. Ответы 4xx и 503 provider_unavailable ничего не списывают. Что на самом деле прошло, видно в журнале запросов в кабинете.

Тайм-аут клиента ставьте с запасом к тому, сколько сервис может работать над запросом: /extract ждёт каждую страницу до 15 с (при extract_depth=advanced - до 30 с), а с instructions к этому добавляется разбор моделью; /crawl и /map обходят сайт до 60 с, поэтому для них тайм-аут клиента - не меньше 60 с. Оборвав ожидание раньше, клиент не узнает результат, а повтор может оплатиться второй раз.

import time

import httpx
from httpx import HTTPTransport

# retries=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 с; на 429 - столько, сколько велит Retry-After
        time.sleep(float(r.headers.get("Retry-After") or 2 ** attempt))
    raise RuntimeError("unreachable")

Обрыв на чтении ответа (httpx.ReadError, httpx.ReadTimeout) этот код нарочно не повторяет: запрос к тому моменту уже ушёл и мог быть оплачен, и повторять ли его - решать вам.

В curl повтор включает флаг --retry: он повторяет тайм-ауты и ответы 408, 429, 500, 502, 503, 504, ждёт 1 с и дальше удваивает паузу, а Retry-After с версии 7.66 соблюдает сам. --retry-delay отключает удвоение, а --retry-all-errors повторяет вообще всё, включая обрывы после отправки запроса. Повтор после тайм-аута тоже может оплатиться дважды, см. выше.

curl --retry 3 -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-ВАШ_КЛЮЧ" \
  -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-ВАШ_КЛЮЧ"},
    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-ВАШ_КЛЮЧ"


@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-ВАШ_КЛЮЧ
  • Body (JSON): {"query": "{{ $json.query }}", "max_results": 5}

Подключение через MCP#

MCP-сервер доступен по адресу https://aidatagate.ru/mcp (транспорт streamable HTTP) и даёт агентам пять инструментов: search, extract, crawl, map и search_images. Авторизация - тот же API-ключ в заголовке Authorization: Bearer или X-API-Key, либо вход через OAuth. Списание кредитов и лимит запросов - как у обычных запросов (в журнале такие вызовы помечены mcp:search / mcp:extract / mcp:crawl / mcp:map / mcp:search_images). Инструмент возвращает тот же JSON, что и HTTP-ручка; заголовков с ценой у MCP нет, списанное видно в журнале кабинета и в GET /usage.

Инструменты принимают не все параметры HTTP API:

Инструмент Параметры Чего нет по сравнению с HTTP
search query, search_depth, topic, max_results, include_answer, include_raw_content, include_images, include_domains, exclude_domains, days, time_range, country start_date, end_date, language, filter_by_language, include_domains_mode, exact_match, chunks_per_source, include_favicon, include_usage, auto_parameters
extract urls, extract_depth, format, include_images instructions, include_favicon, include_usage
crawl url, max_depth, max_breadth, limit, select_paths, exclude_paths instructions
map те же, что у crawl instructions
search_images query, max_results -

Отличаются и общие параметры:

  • include_answer и include_raw_content - только true/false: ответ приходит обычной длины (как basic), текст страниц - в markdown.
  • urls у extract - только список, даже из одного адреса.
  • Значения проверяются строго: число вне диапазона ("max_results": 25, "limit": 100) и значение не из списка ("search_depth": "fast", "topic": "finance") дают ошибку вызова, а не прижимаются к границе и не подменяются, как в HTTP API.

Неверный ключ даёт ошибку авторизации (HTTP 401). Ошибка внутри вызова приходит текстом с кодом в квадратных скобках, например Not enough credits for this request (available=0, required=1) [insufficient_credits] или ... [rate_limited].

Четыре поля для ручного добавления#

Клиенты, которые добавляют сервер формой, а не файлом конфигурации, спрашивают поля по отдельности:

Поле Значение
Адрес сервера (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 строка с пробелом после двоеточия разбирается неправильно, поэтому подставляется переменной.

Без Node.js - stdio-обёртка на Python, её можно получить по запросу через форму обращения на странице /contacts.

После правки файла 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, в котором указан адрес метаданных - по нему клиент и находит, где входить.

Codex (OpenAI)#

Codex CLI умеет удалённые MCP-серверы по streamable HTTP - ровно тот транспорт, который отдаёт /mcp. Конфигурация лежит в ~/.codex/config.toml, а для одного проекта - в .codex/config.toml рядом с кодом:

[mcp_servers.aidatagate]
url = "https://aidatagate.ru/mcp"
bearer_token_env_var = "AIDATAGATE_KEY"

Ключ берётся из переменной окружения, а не пишется в файл: конфигурация проекта попадает в репозиторий, а ключ там не нужен.

export AIDATAGATE_KEY=rs-...
codex mcp list   # в списке должен появиться aidatagate

Если предпочитаете заголовок вместо Authorization, Codex умеет подставлять его из окружения (env_http_headers) - имя заголовка X-API-Key, значение из переменной.

Отдельно про "работает ли через обычный API": да, разумеется. /search и /extract - обычные HTTP-ручки, и код, который пишет любой ассистент, вызывает их как всякий другой сервис - примеры выше в разделе "Примеры". MCP нужен для другого: чтобы ассистент сам мог искать по ходу разговора, без написания кода.

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-серверы только локальным процессом, подходит мост mcp-remote (нужен Node.js): он работает как локальный stdio-сервер и пересылает вызовы на https://aidatagate.ru/mcp. Пример конфигурации - выше, в разделе "Claude Desktop". Если Node.js поставить нельзя, stdio-обёртку на Python можно получить по запросу через форму обращения на странице /contacts.

Сколько стоит запрос#

Цены ниже - стартовые, администратор может их менять: актуальные значения - на странице /pricing.

Операция Кредиты Как считается
/search, search_depth=basic 1 За каждые 10 запрошенных результатов (max_results): топ-10 - 1 кредит, топ-20 - 2
/search, search_depth=advanced 2 Так же за каждые 10: топ-20 - 4 кредита
include_answer +2 Один раз на запрос и только за ответ моделью (answer_source=llm); выжимка без модели бесплатна
/extract, extract_depth=basic 1 За каждые 5 успешно извлечённых URL, округление вверх
/extract, extract_depth=advanced 2 За каждые 5 успешно извлечённых URL, округление вверх
/extract с instructions +2 За каждый URL, который разобрала модель, не больше 5 URL за запрос; сбой модели не оплачивается
/crawl 1 За каждые 5 страниц в ответе, округление вверх
/map 1 За каждые 10 URL в ответе, округление вверх
/download, /download/image, /download/gif 1 За каждые начатые 10 МБ отданного файла; неудача не оплачивается
/search/images 1 За запрос, до 30 картинок

Цена поиска зависит от запрошенного max_results, а не от того, сколько документов нашлось. Сколько ссылок поисковик отдаёт за один заход, на неё тоже не влияет: платить за то, как нам удобнее забрать выдачу, клиент не должен. Ничего не стоят GET /usage, GET /healthz, ответы с ошибкой 4xx и 503 provider_unavailable. Вызовы через MCP тарифицируются так же, как HTTP.

Кэш на цену не влияет. Выдача кэшируется до 6 часов (при topic=news - до 30 минут), страницы - до 7 дней. Повторный запрос из кэша приходит быстрее, но стоит столько же, сколько первый.

Бесплатная квота. Каждый месяц на баланс начисляется 1000 бесплатных кредитов (актуальный размер - на /pricing). Квота обновляется 1-го числа в 00:00 по Москве: несписанный остаток бесплатных кредитов сгорает, и квота начисляется заново. Купленные кредиты обновление квоты не затрагивает. Списываются сначала бесплатные кредиты, потом купленные.

Пополнение - в кабинете, на любую сумму от 20 до 30 000 рублей. Кредит стоит 0,90 рубля независимо от суммы: число кредитов - сумма, делённая на цену кредита, с округлением вниз.

Резерв и ошибка 402. Кредиты списываются по факту, после обработки, но перед ней сервис резервирует верхнюю оценку цены. Если резерв не помещается в доступный баланс (бесплатные плюс купленные кредиты минус резервы запросов, которые выполняются параллельно), запрос сразу получает 402 insufficient_credits и не выполняется. Резерв бывает больше итоговой цены:

  • /search с include_answer резервирует наценку за ответ модели, даже если в итоге ответит бесплатная выжимка;
  • /extract резервирует по числу всех URL в запросе, а с instructions - ещё и разбор моделью до 5 URL;
  • /crawl резервирует по limit: при limit 50 по умолчанию это 10 кредитов, даже если сайт маленький;
  • /map - тоже по limit: 5 кредитов при limit 50;
  • /download - как за файл предельного размера, 50 МБ: 5 кредитов; /download/image и /download/gif - за 20 МБ: 2 кредита.

При малом остатке уменьшите limit или max_results: резерв уменьшится вместе с ними. Сколько кредитов осталось, показывает GET /usage.

Ограничения#

  • Лимит запросов - 60 в минуту на ключ по умолчанию; при создании ключа в кабинете можно выбрать от 1 до 600. Окно - календарная минута: счётчик обнуляется в начале каждой минуты. Лимит считает запросы к /search, /search/images, /extract, /crawl, /map, операциям скачивания, /usage и вызовы инструментов MCP с этим ключом. При превышении - HTTP 429 с заголовком Retry-After.
  • Тело запроса - до 1 МБ, иначе HTTP 413.
  • Скачиваются только адреса http и https на портах 80, 443, 8080 и 8443, не больше 3 редиректов.
  • Страница читается до 2 МБ (что дальше - отбрасывается), PDF - до 32 МБ.
  • /search: на скачивание страниц выдачи отводится 4 с на весь запрос; не успевшие страницы приходят с фрагментом из выдачи вместо текста.
  • /extract: до 20 URL за запрос; на один URL - до 15 с, при extract_depth=advanced - до 30 с; сетевой тайм-аут - 4 с на соединение и на каждое чтение.
  • /crawl и /map: не больше 50 страниц за обход, глубина до 3, до 100 новых ссылок со страницы, до 6 с на страницу, бюджет всего обхода 60 с.
  • /download: только mp3, до 50 МБ, до 120 с на файл.
  • /download/image и /download/gif: до 20 МБ и 50 млн пикселей, анимация - до 300 кадров.
  • /search/images: до 30 картинок за запрос.
  • Робот представляется как AIDataGateBot/0.1 и уважает robots.txt (страница /bot).