Iskalo (Искало)

Search the Russian web with city, contact and provider filters, retrieve indexed page content, and use semantic reranking through a hosted MCP server.

Server MCP Terhosting

npx add-mcp 'https://iskalo.ru/mcp'

Terpasang ke Claude Code, Codex, Cursor, dan lainnya

Dokumentasi

API и MCP для ИИ-агентов

Искало — собственный индекс русскоязычного интернета. Поиск отдаёт не только ссылки, но и профиль сайта: телефоны, e-mail, город, соцсети, ИНН и год последнего обновления. Оплата в рублях, без VPN и иностранных карт.

— в личном кабинете: вход по коду на почту, ключ сразу, без звонков и договоров. Каждый месяц 300 ₽ на запросы — бесплатно, дальше — пополнение картой или СБП. Там же баланс, расход по дням и ключи для каждого проекта.

Цены

ОперацияЦена
Поиск /v1/search, до 50 результатов с профилем сайта0,30 ₽ за запрос
… с полным текстом страниц (include_content)+0,05 ₽ за результат, не больше +0,30 ₽
Профиль сайта /v1/site/{host}0,10 ₽
Текст страницы /v1/contents0,10 ₽ за адрес
Нейроответ с источниками /v1/answer2 ₽
Пачка queries до 20 запросовкак отдельные запросы

Деньги списываются до выполнения и возвращаются, если запрос не выполнился. Ошибки 400, 401, 404 и 429 бесплатны.

Быстрый старт: curl

curl -s https://iskalo.ru/v1/search \
  -H "Authorization: Bearer $ISKALO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "кавер-группа Казань", "limit": 20,
       "filters": {"has_phone": true, "exclude_domains": ["leadbook.ru"]}}'

Ответ (сокращён):

{
  "query": "кавер-группа казань", "mode": "hybrid", "mode_used": "classic",
  "semantic_rerank_used": true, "semantic_rerank_reason": null,
  "semantic_coverage": {"matched_vectors": 72, "candidates": 300},
  "page": 1, "limit": 20, "has_more": true,
  "total_estimate": 214, "total_is_lower_bound": false, "candidates_cap": 300,
  "warnings": ["neural_off"],
  "warning_text": {"neural_off": "векторный отбор кандидатов не использован; смысловая переранжировка показана отдельно"},
  "results": [{
    "title": "Кавер-группа …", "url": "https://example.ru/", "host": "example.ru",
    "snippet": "…", "score": 0.81,
    "profile": {"phones": ["+79870000000"], "emails": ["band@example.ru"],
                "socials": {"vk": "https://vk.com/…"}, "city": "Казань",
                "inn": null, "org": null, "year": 2025}
  }],
  "cost_rub": 0.3
}

Python

Готовый клиент — один файл без зависимостей: tools/iskalo_client.py в репозитории. Кэш ответов на диске, темп, повторы по Retry-After, пачки и обход всех страниц.

from iskalo_client import Iskalo

isk = Iskalo()                      # ключ — из переменной ISKALO_KEY
for hit in isk.search_all("ведущий на свадьбу Пермь", filters={"has_phone": True}):
    print(hit["host"], hit["profile"]["phones"])

for r in isk.batch(["фокусник Омск", "фокусник Тюмень"], limit=50):
    print(r["query"], len(r.get("results", [])))

print(isk.site("av55.ru")["profile"])

Без клиента — обычный httpx или requests:

import httpx
r = httpx.post("https://iskalo.ru/v1/search", json={"query": "фаер-шоу Самара"},
               headers={"Authorization": "Bearer " + KEY}, timeout=60)
if r.status_code == 429:
    time.sleep(int(r.headers["Retry-After"]))

MCP для Claude Code и других агентов

Сервер MCP — https://iskalo.ru/mcp (Streamable HTTP). Подключение в Claude Code:

claude mcp add --transport http iskalo https://iskalo.ru/mcp \
  --header "Authorization: Bearer msk_…"

Или в .mcp.json проекта:

{"mcpServers": {"iskalo": {"type": "http", "url": "https://iskalo.ru/mcp",
  "headers": {"Authorization": "Bearer ${ISKALO_KEY}"}}}}

Инструменты: web_search (те же параметры и фильтры, что у REST), site_profile, get_contents, answer, usage. Ошибка инструмента — например, «пополните баланс» — приходит модели текстом.

Методы

МетодЧто делает
POST /v1/searchПоиск. Один запрос — query, пачка — queries
GET /v1/site/{host}Профиль сайта: контакты, город, соцсети, год, страниц в индексе. 404 — сайта нет в индексе (бесплатно)
POST /v1/contentsТекст страниц из индекса: {"urls": [...]}, до 20 адресов
POST /v1/answerКороткий ответ со ссылками [1], [2]: {"query": "..."} — на платном тарифе (с первого пополнения); на бесплатном — 403 без списания
GET /v1/usageБаланс, бесплатный остаток, расход за месяц — по операциям и по каждому ключу
GET /v1/keys, POST /v1/keys, DELETE /v1/keys/{key_id}Ключи аккаунта с метками: {"label": "katalog-artistov"} выпускает ещё один ключ — отдельный на проект или сессию агента, чтобы видеть, кто сколько потратил
GET /v1/pricesПрайс и лимиты, без ключа
POST /v1/billing/topupПополнение: {"amount_rub": 500} → ссылка на оплату

Параметры поиска

ПолеЗначение
queryЗапрос, до 400 символов
limitРезультатов на странице, 1–50, по умолчанию 10
pageСтраница, 1–30. Есть ли следующая — поле has_more
modehybrid (по умолчанию), neural, classic. Отбор кандидатов — mode_used, смысловая переранжировка — semantic_rerank_used
autocorrectИсправлять опечатки и раскладку, по умолчанию да. Исходный запрос — в corrected_from; с false — подсказка в did_you_mean
include_contentДобавить полный текст страниц (доплата, см. цены)
fieldsКакие поля профиля вернуть: phones, emails, socials, city, inn, org, year. По умолчанию все, [] — без профиля
filters.has_phone, filters.has_emailТолько сайты с телефоном / e-mail
filters.require_any_contactЕсть телефон, e-mail или соцсеть в сохранённом профиле. Это не проверка актуальности или владельца контакта.
filters.provider_typeТип по заголовку и описанию страницы: performer, speaker, talent_agency, venue_operator, event_agency, conference_organizer, costume_shop, education, equipment_vendor, other_business, unknown. Необязательный строгий фильтр: при его включении неизвестные типы отсекаются.
filters.cityГород сайта по профилю: «Казань»
filters.fresh_sinceСайт обновлялся не раньше этого года: 2024
filters.include_domains, filters.exclude_domainsИскать только на этих сайтах / без них, до 100 доменов
filters.site_typedirect — без каталогов и агрегаторов; тип поставщика задаётся отдельно, catalog — только они
Операторы в query"точная фраза", -слово, site:домен, -site:домен
queriesПачка до 20 запросов: строки или объекты с теми же полями. Общие поля запроса действуют на все. Ответ — responses в том же порядке

Что в ответе

  • profile — то, что робот нашёл на главной сайта. year — самый свежий год: из текста главной или из sitemap. Пустые поля — [] или null.
  • site_type — catalog, если сайт похож на каталог или агрегатор, иначе direct. Отсечь каталоги в выдаче — filters.site_type: "direct".
  • relevance — эвристические признаки по метаданным страницы: provider_type, provider_type_source, query_intent, intent_reason, page_city, city_conflict. Упоминание города в заголовке не подтверждает адрес или город базирования. Несовпадение роли с запросом и явное противоречие города понижают результат; неизвестный тип сам по себе не штрафуется.
  • filter_diagnostics — счётчики выбранного пула (scope: retrieved_candidates), а не всего индекса: candidates, after_contact, after_city, after_site_type, after_provider_type, before_dedup, after_dedup. Этапы последовательны: контакт считается после исключения запрещённых сайтов и доменных фильтров; между типом поставщика и дедупом также действуют текстовая релевантность и год обновления.
  • score — только для сортировки внутри одного запроса. Сравнивать его между запросами нельзя, бывает больше 1.
  • total_estimate — сколько результатов можно пролистать. Лексический отбор ограничен candidates_cap: обычно 300, с фильтрами 800; отдельный векторный отбор может добавить кандидатов, поэтому совпадений в индексе может быть больше; тогда total_is_lower_bound: true. Поле total оставлено для совместимости, оно равно total_estimate.
  • mode_used и neural описывают отбор кандидатов. При classic кандидаты лексические; отдельная переранжировка по векторам работает и при MS_NEURAL=0. semantic_rerank_used подтверждает её выполнение для запроса; при пропуске semantic_rerank_reason содержит причину (disabled, no_candidates, vectors_missing, encoder_unavailable, vectors_error, no_vectors, invalid_vectors). При векторном отборе причина — neural_used. Ответ из кэша сохраняет статус исходного вычисления.
  • semantic_coverage: matched_vectors — число кандидатов с собственным вектором, candidates — размер пула переранжировки (до 300, до склейки и пагинации). При fallback matched_vectors=0. У результата semantic_vector_matched: false означает медиану, true — собственный вектор; отсутствие поля — результат не участвовал в отдельной переранжировке (в том числе синтетическая ссылка).
  • warnings — что учесть:
    • neural_off — векторный отбор не использован, кандидаты отобраны как в classic; статус переранжировки сообщается отдельно; повторять запрос в другом режиме незачем;
      • semantic_rerank_unavailable — переранжировка не выполнена из-за недоступных или некорректных данных/службы; причина в semantic_rerank_reason;
      • query_corrected — запрос исправлен, исходный — в corrected_from;
      • candidates_capped — дошли до конца пула кандидатов: дальше страниц нет, уточните запрос или фильтры.

Ошибки

КодЧто значит и что делать
400Неверный параметр — текст в error
401Нет ключа или ключ отозван. Перебор ключей блокирует адрес на 30 минут
402Не хватает денег. В теле — balance_rub, free_left_rub, price_rub: пополните баланс
404Сайта нет в индексе (/v1/site)
413Тело запроса больше 64 КБ
429Превышен лимит скорости. Подождите столько секунд, сколько в заголовке Retry-After (то же число — в retry_after), и повторите
501Параметр ещё не включён на сервере
503Поиск временно недоступен — повторите через минуту

Тело ошибки: {"error": "текст", "status": 429, "retry_after": 3}. В пачке queries ошибка одного запроса не отменяет остальные: у него в ответе поля error и status.

Лимиты

  • Скорость — на ключ: бесплатный тариф — 1 запрос в секунду (всплеск до 5), платный — 10, бизнес — 50. Запросы пачки считаются по отдельности.
  • Бесплатная выдача на сайте и /api/search — для людей: до 10 поисков в минуту с адреса, без профилей сайтов. Для программ — только API с ключом.