twogis-service API
Внутренний HTTP-сервис для работы с 2GIS: официальный API и browser-backed сбор через Chromium/CDP. Один сервис на все проекты — ключ 2GIS, browser-session и обход блокировок живут здесь, интеграторам достаточно внутреннего токена.
- Базовый URL:
https://twogis.satushi.me - Внутри кластера:
http://twogis-service.twogis.svc.cluster.local:8081 - Формат: JSON в запросе и ответе, UTF-8
- Машинная схема:
/openapi.json
Офлайн-каталог (дополнительный контракт v1)
Разовый исторический переход
Worker поддерживает --once --regions 68 --require-baseline
--transition-approval /path/approval.json. Этот файл задаёт согласованный переход:
id, region_id, from_version/from_sha256, to_version/to_sha256,
archive_size, counts (branches/organizations/buildings), previous_counts
и missing (branch/building). Все значения сверяются точно; старая версия
проверяется внутри транзакции публикации. Изменившийся manifest, checksum или
количества приводят к отказу. Согласование сохраняется в counts импорта.
Режимов loop/due больше нет; обычный порог исчезновения 15% не меняется.
Загрузками управляет Pulse через /twogis/offline/manifest и
/twogis/offline/operations — контракт в offline.md.
Повторная публикация того же файла идемпотентна. Это операторский CLI, не публичный API.
Экспериментальный поиск с резервом
POST /twogis/search принимает параметры /twogis/offline/search и
mode=auto|local|online (по умолчанию auto). Требуется внутренний токен.
В auto сначала читается PostgreSQL; пустая выдача, недоступная БД или пакет
старше 45 дней переключают запрос на прямой Hybrid API 2ГИС. Никаких Netly/MQTT
и браузерных запусков на каждом поиске. mode=online позволяет явно запросить
резерв даже при непустой, но нерелевантной локальной выдаче.
{"region_id":"68","query":"магнум","mode":"auto","limit":20}
Ответ помечен source=local|online, provider, experimental и
fallback_reason=null|local_empty|local_unavailable|local_stale.
Локальный поиск экспериментальный. Онлайн выдаёт кандидатов Hybrid, не полную
базу: total_scope=returned_candidates, coverage=provider_candidates;
catalog=null и revision=null у онлайн-карточек. Для локальной выдачи
total_scope=catalog_matches. Поля онлайн-карточек могут быть беднее офлайн.
Ответ не импортируется в каталог и не участвует в вычислении закрытий бизнеса.
Для следующих страниц передавать mode, равный source первой страницы:
auto с offset>0 запрещён, чтобы не смешивать выдачи. Это не фиксирует
онлайн-выдачу во времени; стабильного upstream snapshot нет. Hybrid сохраняет
свой порядок результатов, distance_m информационный. При отсутствии координат
он равен null. bbox применяется и к запросу, и к ответу; регион, город,
тип и здание проверяются по данным каждого кандидата, фильтры не снимаются.
Возвращается фактический viewport; без bbox используются подсказочные viewport
городов, это не гарантия полного охвата регионального пакета.
Резерв пока поддерживает регионы 68/67 и текст от двух символов. Для пустого
поиска по зданию/области использовать mode=local; в auto при отсутствии
локальных данных такой запрос вернёт 503, а не результат из другой области.
Невалидный запрос → 422 без обращения к 2ГИС. Ошибка резерва → 502/503 с кодом
online_search_unavailable, а не успешный пустой ответ. Таймаут upstream-сокета
8 секунд, без повторов; тело ≤8 MiB, редиректы с ключом запрещены.
Существующие /twogis/address-search и /twogis/catalog сохраняют свои контракты.
Существующие online/browser маршруты сохранены. Новые маршруты обслуживают локальную PostgreSQL и требуют тот же внутренний токен:
POST /twogis/offline/search:region_id(68 Астана / 67 Алматы),query,kind=branch|building, необязательныеcity_id,building_id,bbox, параlatitude/longitude,limit≤50,offset≤1000.GET /twogis/offline/status?region_id=68: версия, дата пакета и последняя попытка.GET /twogis/offline/entity?region_id=68&kind=branch&id=…: текущая карточка.GET /twogis/offline/snapshot?region_id=68&limit=100: страницы bootstrap; следующие запросы передаютnext. Смена ревизии между страницами → 409/restart.GET /twogis/offline/changes?region_id=68&cursor=0:0&limit=100: события сrevision,ordinal,operation,patch,data_as_of,source_version. Сохранятьnext_cursor, учитыватьhas_moreи публиковать только полную ревизию.
Ответы содержат catalog.contract_version=1, data_as_of и признак stale.
Пакеты региональные, с окрестностями; это не live-спрос и не фактическая дата
открытия бизнеса. missing ≠ подтверждённое закрытие. Ошибка базы/неимпортированный
регион → 503, не пустая выдача. Поиск не обращается к 2ГИС или LLM.
Аутентификация запросов
Все рабочие endpoint требуют заголовок:
X-Internal-Token: <token>
Токен лежит в Infisical: проект twogis, env prod, ключ TWOGIS_INTERNAL_TOKEN.
В Kubernetes он приезжает через External Secrets в secret twogis-secrets —
подключается в под как envFrom, отдельно копировать не надо.
Проверки состояния (/health, /healthz) и документация (/docs, /llms.txt)
работают без токена.
Endpoints
| Метод | Путь | Назначение |
|---|---|---|
POST |
/twogis/address-search |
подсказки адресов внутри города |
POST |
/twogis/apartment |
подъезд, этаж и диапазоны квартир |
POST |
/twogis/place |
карточка организации: рейтинг и число отзывов |
POST |
/twogis/reviews |
полная выгрузка отзывов с пагинацией |
POST |
/twogis/session |
живая browser-session для Catalog API |
POST |
/twogis/search-pages |
browser-захват страниц поиска и сырых Catalog items |
POST |
/twogis/catalog |
нормализованный Catalog/HTML/browser API |
POST |
/scrape |
запустить задание parser-2gis |
GET |
/status/{run_id}, /rows/{run_id}, /result/{run_id} |
статус, поток и CSV |
POST |
/pause/{run_id}, /resume/{run_id}, /cancel/{run_id} |
управление заданием |
GET |
/health, /healthz |
liveness/readiness |
GET |
/, /docs, /llms.txt |
лендинг и эта документация |
POST /twogis/session
Открывает страницу 2GIS в headless Chromium, перехватывает реальные Catalog запросы и возвращает параметры живой browser-session. Контракт совместим с бывшим endpoint Pulse scraper. Pulse теперь обращается сюда только через standalone-провайдера; скрытого отката на старый scraper нет.
{
"source_url": "https://2gis.kz/astana/search/кофе",
"timeout_sec": 45
}
Ответ содержит api_key, base_url, shv, stat_sid, stat_user,
search_user_hash, user_token, viewpoint, browser headers и метаданные
захвата. Эти значения являются внутренними credentials провайдера и не должны
передаваться пользовательским клиентам или писаться в публичные логи.
POST /twogis/search-pages
Открывает точный search URL в headless Chromium и собирает сырые элементы Catalog с последовательных страниц. Используется Pulse для полного покрытия, когда обычный parser пропускает часть поисковой выдачи.
{
"source_url": "https://2gis.kz/astana/search/кофе",
"max_pages": 10,
"timeout_sec": 600
}
max_pages=0 означает все страницы, но действует жёсткий предел 300. Ответ:
total, page_size, pages_captured, массив сырых items, elapsed_ms и
нефатальный error, если после успешных страниц следующую страницу получить
не удалось. HTTP 502 означает, что не удалось получить базовый результат.
POST /twogis/address-search
Подсказки адресов, улиц и организаций. Город обязателен: 2GIS ищет внутри прямоугольника города, а не по тексту запроса.
Запрос:
| Поле | Тип | Обяз. | По умолчанию | Ограничения |
|---|---|---|---|---|
query |
string | да | — | 2–300 символов |
city |
string | да | — | из списка поддерживаемых, регистр не важен |
locale |
string | нет | ru_KZ |
2–16 символов |
limit |
int | нет | 10 |
1–50 |
timeout_sec |
int | нет | 20 |
5–60 |
curl -sS -X POST https://twogis.satushi.me/twogis/address-search \
-H 'Content-Type: application/json' \
-H "X-Internal-Token: $TWOGIS_INTERNAL_TOKEN" \
--data '{"query":"Улы Дала 14","city":"Астана","limit":5}'
Ответ:
{
"query": "Улы Дала 14",
"city": "Астана",
"locale": "ru_KZ",
"items": [
{
"id": "70000001089125216_muke2u...",
"type": "branch",
"name": "14, магазин разливных напитков",
"address": "Астана, проспект Улы Дала, 29",
"city": "Астана",
"latitude": 51.101405,
"longitude": 71.392033,
"source_url": "https://2gis.kz/firm/70000001089125216"
}
],
"total": 1,
"captured_at": "2026-09-06T15:04:58.358902+00:00",
"resolution_method": "hybrid_api"
}
Поля ответа:
| Поле | Описание |
|---|---|
city |
канонический город из справочника, а не то, что прислали |
items[].id |
id объекта 2GIS; у филиалов составной (<branch_id>_<хеш>) |
items[].type |
building, street, branch, adm_div.* и т.п. |
items[].source_url |
ссылка на 2GIS: /firm/<branch_id> для филиалов, иначе /geo/<id> |
total |
сколько объектов реально отдано после фильтрации |
resolution_method |
hybrid_api; official_catalog только при явном выборе legacy-транспорта |
Пустой items — нормальный ответ 200, а не ошибка: 2GIS отвечает
itemNotFound, если ничего не подошло. Так бывает и для существующих улиц,
если указать номер дома, которого нет в подсказках.
Поддерживаемые города. Границы и region_id берутся из таблицы core_city
базы cabin-backend; поддержаны те города, где эта разметка заполнена:
Актау, Актобе, Алматы, Астана, Атырау, Караганда, Каскелен, Кентау, Кокшетау, Костанай, Павлодар, Петропавловск, Семей, Талгар, Тараз, Темиртау, Уральск, Усть-Каменогорск, Шымкент.
Понимаются и алиасы: Нур-Султан, Astana, Almaty, Shymkent и подобные.
Незнакомый город — 400 с текстом city is not supported: <город>.
Один region_id покрывает и пригороды, поэтому выдача дополнительно
отфильтрована: по запросу для Алматы не придут объекты, явно принадлежащие
Талгару или Каскелену.
POST /twogis/apartment
Требует X-Internal-Token и настроенный ключ 2ГИС.
Город задаётся cityId из cabin core_city (например, Астана 710000000,
Алматы 750000000). Viewpoint и region_id сервис выбирает из собственного
справочника 19 городов в collector/twogis_cities.py; клиент не передаёт координаты.
Старые запросы с городом в адресе или полем city сохраняют поддержку.
Неизвестный ID, отсутствие города или конфликт ID и названия дают HTTP 400
без запроса к провайдеру. Квартира — отдельным
строковым полем apartment либо в конце адреса с меткой кв, квартира,
пәтер, apartment, apt. Противоречащие друг другу номера отклоняются.
curl -sS https://twogis.satushi.me/twogis/apartment \
-H 'Content-Type: application/json' \
-H "X-Internal-Token: $TWOGIS_INTERNAL_TOKEN" \
--data '{"address":"Сауран 3","cityId":"710000000","apartment":"12"}'
Эквивалентный запрос: {"address":"Астана, Сауран, 3, кв 12"}.
Необязательный timeout_sec: 5–60 секунд, по умолчанию 20.
Пример ответа, основанный на проверке Hybrid API от 10 сентября 2026:
{
"address": "Сауран 3",
"apartment": "12",
"city": "Астана",
"status": "resolved",
"building": {
"id": "9570784863330957",
"address": "улица Сауран, 3",
"city": "Астана",
"latitude": 51.125736,
"longitude": 71.422064,
"source_url": "https://2gis.kz/geo/9570784863330957"
},
"matches": [{
"entrance_id": "70030076158799869",
"entrance": "1",
"floor": "4",
"floor_source": "calculated",
"calculated": true,
"floor_range": {"start": "1", "end": "4"},
"apartment_ranges": [{"start": "1", "end": "12"}],
"floor_apartments": [{"start": "10", "end": "12"}]
}],
"candidates": [],
"captured_at": "2026-09-10T00:00:00+00:00",
"resolution_method": "hybrid_api"
}
matches содержит все совпадения квартиры внутри одного точно совпавшего
дома. Поиск проверяет город, улицу и номер дома по структурированному ответу:
дом 3/1 не считается домом 3. Нормализуются регистр, пробелы, запятые и
типовые обозначения улицы/города/дома. Произвольные опечатки, транслитерация,
районы и названия ЖК в адресе могут потребовать уточнения по candidates.
Без города запрос отклоняется до обращения к провайдеру. Если точного
совпадения нет, допускаются склонения фамилий (Кошкарбаев/Кошкарбаева),
опущенное имя человека и пробелы в полном номере дома (38вк5/38в к5).
Разные имена людей, города, номера, дроби и корпуса не объединяются;
несколько подходящих домов остаются неоднозначными. Проверенные варианты
написания имён (например, Алихана Бокейханова / Алихан Бокейхан,
Рахимжана Кошкарбаева / Ракымжан Кошкарбаев) сопоставляются по явному
словарю внутри города. Отдельный префикс квартала перед улицей не входит
в её имя. Это не произвольный нечёткий поиск и не замена номера дома.
| Поле совпадения | Значение |
|---|---|
entrance |
Номер подъезда из 2ГИС; null, если отсутствует либо вместо номера пришёл внутренний ID |
floor |
Этаж строкой, включая текстовые обозначения; null, если неизвестен |
floor_source |
reported — конкретный этаж передан API; calculated — вычислен из диапазонов; unknown — определить нельзя |
calculated |
Исходный флаг 2ГИС; null, если провайдер его не прислал. Даже reported может иметь calculated: true |
floor_range |
Исходный диапазон этажей для найденной группы; null, если отсутствует |
apartment_ranges |
Квартиры всей найденной группы, а не обязательно одного этажа |
floor_apartments |
Квартиры определённого этажа; пустой список, если этаж неизвестен |
Все границы диапазонов включительны и представлены строками. Для диапазона этажей точный этаж рассчитывается при предположении равномерного размещения квартир: только для одного непрерывного числового диапазона квартир, положительных последовательных этажей, деления без остатка и не более 20 квартир на этаж. Этот консервативный предел исключает часть разреженной/кодированной нумерации; он не доказывает равномерную планировку. Например, 12 квартир на 4 этажах дают по 3 квартиры на этаж. Это расчётная подсказка, не подтверждение планировки. При пропусках, неравномерном количестве, неподдерживаемой форме или неизвестном этаже сервис сохраняет доступные диапазоны, но не придумывает этаж.
status |
Значение |
|---|---|
resolved |
Найдено одно совпадение с определённым этажом |
floor_unknown |
Квартира найдена, точный этаж неизвестен |
apartment_not_found |
Дом найден, сведений об этой квартире нет |
ambiguous_apartment |
Несколько совпадений квартиры; выбирать автоматически нельзя |
building_not_found |
Точное совпадение дома не найдено; возможные результаты в candidates |
ambiguous_building |
Несколько домов точно соответствуют запросу; требуется уточнение |
Эти статусы возвращаются с HTTP 200. Ошибки: 400 — отсутствующая/неверная квартира или конфликт параметров; 401 — неверный внутренний токен; 422 — ошибка схемы запроса; 503 — отсутствующая конфигурация; 502 — ошибка, отказ доступа, лимит или таймаут провайдера. Ключи и полный ответ провайдера клиенту не возвращаются.
Транспорт: GET https://hybrid-api.2giskz.app/v1/search/markers, параметр s_q
содержит адрес дома, s_V=viewpoint1,viewpoint2 — область карты города
именно обрабатываемого дома в очереди. Это поисковый контекст, не жёсткая
геометрическая граница; город в структурированном ответе проверяется отдельно.
fields=address,adm_div,name,type,links.database_entrances.
Номер квартиры сопоставляется локально по apartments_info.floors. Ключ Hybrid
API может не работать на catalog.api.2gis.com; при необходимости отдельный ключ задаётся через TWOGIS_HYBRID_API_KEY (по умолчанию используется TWOGIS_API_KEY).
Документация 2ГИС по структуре полей Catalog API.
Формат Hybrid API дополнительно проверен живым запросом; покрытие зависит от дома.
POST /twogis/place
Карточка организации: рейтинг и число отзывов.
| Поле | Тип | Обяз. | По умолчанию | Ограничения |
|---|---|---|---|---|
branch_id |
string | да, если нет source_url |
"" |
только цифры, 10–30 знаков |
source_url |
string | да, если нет branch_id |
"" |
ссылка https://2gis.kz/... или короткая https://go.2gis.com/... |
timeout_sec |
int | нет | 20 |
5–60 |
Можно прислать любое одно поле. Если пришли оба и branch_id не совпадает с
id из ссылки — 400.
curl -sS -X POST https://twogis.satushi.me/twogis/place \
-H 'Content-Type: application/json' \
-H "X-Internal-Token: $TWOGIS_INTERNAL_TOKEN" \
--data '{"branch_id":"70000001089125216"}'
{
"branch_id": "70000001089125216",
"source_url": "https://2gis.kz/firm/70000001089125216",
"name": null,
"address": null,
"rating": 4.8,
"reviews_count": 30,
"verified_at": "2026-09-06T18:53:13.824038+00:00"
}
name и address заполняются не всегда — они приходят только из публичного
профиля, когда тот доступен. Рейтинг и число отзывов есть всегда.
POST /twogis/reviews
Полная выгрузка отзывов с пагинацией.
| Поле | Тип | Обяз. | По умолчанию | Ограничения |
|---|---|---|---|---|
branch_id |
string | да, если нет source_url |
"" |
только цифры, 10–30 знаков |
source_url |
string | да, если нет branch_id |
"" |
ссылка 2GIS, в том числе короткая |
max_reviews |
int | нет | 0 (все) |
0–10000 |
timeout_sec |
int | нет | 120 |
10–900 |
curl -sS -X POST https://twogis.satushi.me/twogis/reviews \
-H 'Content-Type: application/json' \
-H "X-Internal-Token: $TWOGIS_INTERNAL_TOKEN" \
--data '{"branch_id":"70000001089125216","max_reviews":3}'
{
"branch_id": "70000001089125216",
"source_url": "https://2gis.kz/firm/70000001089125216/tab/reviews",
"reviews": [
{
"id": "269197433",
"rating": 1,
"text": "…",
"date_created": "2026-08-16T17:02:48.308537+07:00",
"date_edited": null,
"author_id": "…",
"author_name": "…",
"official_answer_text": null,
"official_answer_date": null,
"url": "…",
"provider": "2gis",
"is_hidden": false
}
],
"total_count": 29,
"branch_rating": 4.8,
"branch_reviews_count": 30,
"pages_fetched": 1,
"complete": false,
"captured_at": "2026-09-06T18:55:10.101010+00:00",
"elapsed_ms": 337
}
complete: false означает, что выгружены не все отзывы — сработал
max_reviews или лимит страниц. Ставьте max_reviews: 0, чтобы забрать всё, и
поднимайте timeout_sec: полная выгрузка популярной точки занимает минуты.
total_count — сколько отзывов реально отдано, branch_reviews_count — счётчик
самого 2GIS; они расходятся, потому что 2GIS считает и скрытые отзывы.
GET /health, /healthz
{"status": "ok"}
Без токена, без обращений к 2GIS. Годится для liveness и readiness.
Ошибки
Формат один для всех ошибок — FastAPI-конверт:
{"detail": "city is not supported: Атлантида"}
| Код | Когда | Что делать |
|---|---|---|
400 |
невалидный ввод: короткий query, неизвестный город, кривой branch_id, конфликт branch_id и source_url |
починить запрос, ретрай не поможет |
401 |
нет заголовка X-Internal-Token или он неверный |
проверить токен |
422 |
тело не проходит схему (нет обязательного поля, тип не тот) | починить запрос |
502 |
2GIS ответил ошибкой или не ответил | ретрай с задержкой |
503 |
в поде нет TWOGIS_INTERNAL_TOKEN |
проблема развёртывания, чинится не на стороне клиента |
Ретраить имеет смысл только 502. Сервис уже делает 3 внутренние попытки с
нарастающей паузой, поэтому клиенту хватит одного повтора через несколько секунд.
Интеграция
Таймауты клиента ставьте больше, чем timeout_sec в теле запроса: сервис ждёт
2GIS до указанного предела, а сверху добавляется сеть. Для /twogis/reviews
без max_reviews закладывайте минуты.
Ограничений по частоте на стороне сервиса нет, но ключ 2GIS общий на все проекты — не выгружайте отзывы пачками в несколько потоков.
Пример на Python:
import os
import requests
BASE = "https://twogis.satushi.me"
HEADERS = {"X-Internal-Token": os.environ["TWOGIS_INTERNAL_TOKEN"]}
def search_address(query: str, city: str, limit: int = 10) -> list[dict]:
response = requests.post(
f"{BASE}/twogis/address-search",
json={"query": query, "city": city, "limit": limit},
headers=HEADERS,
timeout=30,
)
response.raise_for_status()
return response.json()["items"]
Пример на Java (Spring, как в cabin-backend):
@FeignClient(name = "twogis", url = "${twogis.base-url}")
public interface TwoGisClient {
@PostMapping("/twogis/address-search")
AddressSearchResponse searchAddresses(
@RequestHeader("X-Internal-Token") String token,
@RequestBody AddressSearchRequest request);
}
Конфигурация сервиса
Нужна только тем, кто разворачивает сервис, а не интеграторам.
| Переменная | Обязательна | Описание |
|---|---|---|
TWOGIS_INTERNAL_TOKEN |
да | токен доступа к рабочим endpoint |
TWOGIS_API_KEY |
для address search и apartment | ключ 2GIS |
TWOGIS_HYBRID_API_KEY |
нет | отдельный ключ Hybrid API; по умолчанию TWOGIS_API_KEY |
TWOGIS_ADDRESS_PROVIDER |
нет | hybrid по умолчанию, catalog для явно выбранного legacy-транспорта |
TWOGIS_CATALOG_BASE_URL |
нет | Catalog, по умолчанию https://catalog.api.2gis.ru; не URL Hybrid |
TWOGIS_LOG_LEVEL |
нет | уровень логов, по умолчанию INFO |
TWOGIS_REVIEWS_MAX_PAGES |
нет | лимит страниц отзывов, по умолчанию 200 |
TWOGIS_REVIEWS_PAGE_DELAY_SEC |
нет | пауза между страницами, по умолчанию 0.25 |
Подсказки и building-address идут напрямую в Hybrid /v1/search/markers.
TWOGIS_API_KEY используется для внешнего провайдера. TWOGIS_INTERNAL_TOKEN
передаётся Pulsary в заголовке X-Internal-Token для авторизации в нашем сервисе.
Catalog и browser-capture — отдельный канал со своей browser-session; Hybrid
не подставляется вместо /3.0/items, /byid или /indoor.
Ingress пропускает наружу только явно перечисленные пути. Новый endpoint не
станет доступен снаружи, пока не добавлен в deploy/ingress.yaml.
Кэш домов для /twogis/apartment
Все подъезды и диапазоны дома сохраняются в common DB (схема twogis, база app) на стороне сервиса.
Можно повторно запрашивать разные квартиры: сервис проверит сохранённые диапазоны,
не обращаясь к 2ГИС снова. Ключ — нормализованный город/улица/дом без квартиры;
пустые/неоднозначные ответы тоже сохраняются без автоматического обновления.
captured_at отражает время загрузки дома, а не время чтения кэша.
HTTP 202 с Retry-After: 5 означает ожидание в очереди. Повторите тот же POST
через указанное время; это не ошибка провайдера, резервный канал пока не нужен.
Внешние запросы идут по одному, минимум через 5 секунд после ответа.
Клиентские опросы продвигают FIFO-очередь; без опросов задачи остаются в очереди.
Нужен TWOGIS_DATABASE_URL к общей PostgreSQL БД. Ошибки провайдера
и прерванные попытки сохраняются без автоматического повтора (HTTP 502).
Повторное обогащение после исправления области поиска
Поколение кеша city-viewpoint-v2 использует отдельные ключи и search_version=2.
Прежние payload остаются в common DB для аудита, но не выдаются и не попадают
в новую очередь. Каждый дом запрашивается заново один раз, по общей очереди
с интервалом минимум 5 секунд. Все подъезды/диапазоны сохраняются для будущих
квартир. Смена поколения — намеренный однократный сброс по согласованной миграции;
обычные рестарты не сбрасывают результаты и не повторяют запросы.
Collection API — перенос из Pulse
Все перечисленные операции требуют X-Internal-Token. Health endpoints публичные.
| Метод | Путь | Ответ |
|---|---|---|
| POST | /scrape | Запустить сбор, получить run_id и текущее состояние |
| GET | /status/{run_id} | Статус, прогресс, хвост логов, checkpoint |
| GET | /rows/{run_id}?after=0&limit=500 | rows, next_after, total, checkpoint |
| POST | /pause/{run_id} | Пауза процесса и Chromium |
| POST | /resume/{run_id} | Продолжение приостановленного процесса |
| POST | /cancel/{run_id} | Остановка, сохранение уже полученных данных |
| GET | /result/{run_id} | CSV без изменения исходных колонок |
| POST | /twogis/catalog | Нормализованные операции Catalog/HTML/browser search |
Тело /scrape: city, category, source_url, resume_from_run_id, paginate_pages. Последние три поля опциональны. Новый запуск с resume_from_run_id использует сохранённую контрольную точку и профиль предыдущего запуска. POST /resume продолжает только ещё существующий приостановленный процесс; после перезапуска нужно новое задание с resume_from_run_id. Файлы прерванного задания доступны. Допустимые статусы: pending, running, paused, completed, failed, cancelled. Мок-CSV при отсутствии команды больше не создаётся: по умолчанию запускается parser-2gis.
Catalog принимает JSON с operation и параметрами:
- public-page / public-pages: source_url, query, page, page_size / max_pages;
- catalog-page / catalog-pages: query, source_url для браузерной сессии, page, page_size / max_pages;
- browser-search: source_url, query, max_pages — браузерные страницы с нормализацией;
- building / branch-building / indoor: id;
- suggests: query, viewport, необязательные session и body;
- byid: id, необязательные fields, viewport, session;
- building-address: query и viewport; прямой Hybrid с точным выбором здания,
source=hybrid_api, оригинальнымhybrid_rawи прежнимparsed_building.items_byidздесь — нормализованная карточка, не сетевой ответ Catalog.
viewport: Lon1, Lat1, Lon2, Lat2. session: StatSID, StatUser, Shv. Ответ: {"data": ..., "error": ""}. Для частичной выдачи data остаётся заполненным, а error описывает ошибку. В SearchPreview отдельно может быть partial_error. Нельзя использовать неполную выдачу для объявления всех отсутствующих организаций закрытыми. Договорённость page=2 у browser-search сохранена: Pulse объединяет её с первой HTML-страницей. raw_payload сохраняет оригинальный JSON каждого элемента.
Сервис запускается одним Uvicorn worker и одной репликой для файловых заданий. DATA_DIR=/app/data должен находиться на постоянном volume с правами UID/GID 10001. Структура старого DATA_DIR Pulse совместима; перед переключением необходима резервная копия. Go worker входит в тот же Docker-образ и не имеет внешнего порта. TWOGIS_CATALOG_WORKER=0 используется только в изолированных unit-тестах.