💻
Разработка в IT
Опубликовано:
15.04.2026
Обновлено:
15.04.2026

Парсинг 2ГИС на Python: три способа собрать организации с телефонами, рейтингами и отзывами

Алексей Иванов

В прошлом квартале мне нужно было собрать базу всех стоматологий Новосибирска для клиента: название, адрес, все телефоны, рейтинг, количество отзывов, часы работы. В 2ГИС таких карточек оказалось 800+. Через внутренний API каталога скрипт на 40 строк собрал всё за 4 минуты — без Selenium, без браузера, чистыми HTTP-запросами. 2ГИС это крупнейший городской справочник в России и СНГ: карты 650+ городов с детализацией до входов в здания и этажей, а каталог организаций содержит десятки миллионов карточек компаний с контактами, расписанием и отзывами.

В этой статье разбираю три подхода к парсингу 2ГИС: официальный Catalog API 3.0, перехват внутренних HTTP-запросов и автоматизацию браузера через Playwright. Для каждого — рабочий код, ограничения и практические рекомендации.

Какие данные можно собрать

Карточка организации в 2ГИС содержит одну из самых полных структур данных среди российских справочников.

  • Название организации, юридическое название, бренд
  • Полный адрес с комментарием (этаж, офис, вход)
  • Координаты (широта/долгота) и геометрия здания
  • Все телефоны (несколько номеров, с комментариями «отдел продаж», «сервисный центр»)
  • Email и сайт
  • Рубрики (категории деятельности)
  • Расписание работы по дням недели, включая специальные даты
  • Рейтинг, количество отзывов и рекомендаций
  • ИНН организации
  • Количество филиалов
  • Количество сотрудников (диапазон: «до 15», «15–50» и т.д.)
  • Фотографии, внешний контент, атрибуты (Wi-Fi, парковка, оплата картой)

Способ 1: Catalog API 3.0 (официальный)

2ГИС предоставляет официальный API каталога для поиска организаций по запросам, рубрикам, координатам и даже ИНН. Базовый эндпоинт — catalog.api.2gis.com/3.0/items.

Получение API-ключа

  1. Зарегистрируйтесь на портале разработчиков: dev.2gis.ru.
  2. Создайте проект и получите API-ключ (key).
  3. Выберите нужные API: «Поиск мест и организаций».

Бесплатный тариф покрывает базовые нужды. Расширенные поля (ИНН, поиск по ИНН) доступны за дополнительную плату.

Минимальный запрос: поиск организаций

import requests


API_KEY = 'ваш-ключ'
BASE_URL = 'https://catalog.api.2gis.com/3.0/items'

params = {
    'q': 'стоматология',
    'region_id': 32,  # Новосибирск
    'key': API_KEY,
    'page_size': 50,  # максимум 50 за запрос
    'page': 1,
    'fields': 'items.contact_groups,items.schedule,items.reviews,items.org',
}

response = requests.get(BASE_URL, params=params)
response.raise_for_status()
data = response.json()

for item in data['result']['items']:
    print(f'Название: {item['name']}')
    print(f'Адрес: {item.get('full_address_name', '')}')

    # Телефоны
    for group in item.get('contact_groups', []):
        for contact in group.get('contacts', []):
            if contact['type'] == 'phone':
                print(f'Телефон: {contact['value']}')

    # Рейтинг
    reviews = item.get('reviews', {})
    print(f'Рейтинг: {reviews.get('rating', 'нет')}')
    print(f'Отзывов: {reviews.get('review_count', 0)}')
    print('-' * 40)

Параметр region_id определяет город. ID регионов можно получить через эндпоинт catalog.api.2gis.com/3.0/regions. Параметр fields управляет тем, какие дополнительные данные включаются в ответ — без него телефоны, расписание и отзывы не возвращаются.

Вывод:

Название: Солнышко, стоматология
Адрес: Новосибирск, Димитрова проспект, 7
Телефон: +7 (383) 555-12-34
Рейтинг: 4.73
Отзывов: 32
----------------------------------------

Пагинация: сбор всех организаций

API возвращает до 50 результатов за запрос. Общее количество — в поле result.total.

import requests
import time


API_KEY = 'ваш-ключ'
BASE_URL = 'https://catalog.api.2gis.com/3.0/items'


def get_all_items(query, region_id, max_items=5000):
    all_items = []
    page = 1

    while len(all_items) < max_items:
        params = {
            'q': query,
            'region_id': region_id,
            'key': API_KEY,
            'page_size': 50,
            'page': page,
            'fields': 'items.contact_groups,items.schedule,'
            'items.reviews,items.org,items.name_ex',
        }

        response = requests.get(BASE_URL, params=params)
        response.raise_for_status()
        data = response.json()

        items = data.get('result', {}).get('items', [])
        total = data.get('result', {}).get('total', 0)

        if not items:
            break

        all_items.extend(items)
        print(f'Страница {page}: получено {len(items)}, всего {len(all_items)}/{total}')

        if len(all_items) >= total:
            break

        page += 1
        time.sleep(0.5)  # пауза между запросами

    return all_items[:max_items]


items = get_all_items('стоматология', region_id=32)
print(f'Итого: {len(items)} организаций')

Параметр page указывает номер страницы (нумерация с 1). Максимально допустимое значение page — 1 000 000, но на практике API ограничивает глубину выдачи. Для полного покрытия крупных рубрик разбивайте поиск по районам или используйте координатные ограничения.

Поиск по координатам

params = {
    'q': 'кафе',
    'key': API_KEY,
    'point': '82.9208,55.0302',  # центр Новосибирска (долгота, широта)
    'radius': 2000,  # радиус поиска в метрах
    'page_size': 50,
    'page': 1,
    'fields': 'items.contact_groups,items.schedule,items.reviews',
}

Параметр point задаёт центр поиска в формате долгота,широта, а radius ограничивает зону в метрах (максимум 40 000). Этот подход позволяет «сканировать» город сеткой точек, перекрывая всю территорию.

Сетка точек для полного покрытия города

import itertools


def generate_grid(lat_min, lat_max, lon_min, lon_max, step_km=2):
    '''Генерирует сетку точек с шагом step_km километров.'''
    lat_step = step_km / 111.0  # 1 градус широты ≈ 111 км
    lon_step = step_km / 65.0  # 1 градус долготы ≈ 65 км (на широте ~55°)

    lats = []
    lat = lat_min
    while lat <= lat_max:
        lats.append(lat)
        lat += lat_step

    lons = []
    lon = lon_min
    while lon <= lon_max:
        lons.append(lon)
        lon += lon_step

    return list(itertools.product(lats, lons))


# Новосибирск: примерные границы
grid = generate_grid(
    lat_min=54.85, lat_max=55.10, lon_min=82.75, lon_max=83.15, step_km=2
)
print(f'Точек в сетке: {len(grid)}')

# Обход сетки
all_items = {}
for lat, lon in grid:
    params = {
        'q': 'стоматология',
        'key': API_KEY,
        'point': f'{lon},{lat}',
        'radius': 1500,
        'page_size': 50,
        'page': 1,
        'fields': 'items.contact_groups,items.reviews',
    }

    response = requests.get(BASE_URL, params=params)
    data = response.json()

    for item in data.get('result', {}).get('items', []):
        item_id = item['id']
        if item_id not in all_items:
            all_items[item_id] = item

    time.sleep(0.3)

print(f'Уникальных организаций: {len(all_items)}')

Дедупликация по item_id исключает повторы на перекрытиях сетки. Шаг 2 км при радиусе 1500 м даёт достаточное перекрытие для покрытия без пробелов.

Поиск по ИНН

API позволяет найти организацию по ИНН через отдельный эндпоинт.

url = 'https://catalog.api.2gis.com/3.0/items/byitin'
params = {
    'itin': '5405123456',
    'key': API_KEY,
    'fields': 'items.contact_groups,items.schedule,items.reviews',
}

response = requests.get(url, params=params)
data = response.json()

for item in data['result']['items']:
    print(f'{item['name']}{item.get('full_address_name', '')}')

Метод byitin доступен за дополнительную плату. Он возвращает все филиалы организации, привязанные к указанному ИНН.

Сохранение в CSV

import csv


def extract_phones(item):
    phones = []
    for group in item.get('contact_groups', []):
        for contact in group.get('contacts', []):
            if contact['type'] == 'phone':
                phones.append(contact['value'])
    return '; '.join(phones)


def extract_emails(item):
    emails = []
    for group in item.get('contact_groups', []):
        for contact in group.get('contacts', []):
            if contact['type'] == 'email':
                emails.append(contact['value'])
    return '; '.join(emails)


def extract_websites(item):
    sites = []
    for group in item.get('contact_groups', []):
        for contact in group.get('contacts', []):
            if contact['type'] == 'website':
                sites.append(contact.get('url', contact.get('value', '')))
    return '; '.join(sites)


def extract_schedule(item):
    schedule = item.get('schedule', {})
    if schedule.get('is_24x7'):
        return 'Круглосуточно'
    days_map = {
        'Mon': 'Пн',
        'Tue': 'Вт',
        'Wed': 'Ср',
        'Thu': 'Чт',
        'Fri': 'Пт',
        'Sat': 'Сб',
        'Sun': 'Вс',
    }
    parts = []
    for eng, rus in days_map.items():
        day = schedule.get(eng)
        if day and 'working_hours' in day:
            hours = day['working_hours']
            if hours:
                h = hours[0]
                parts.append(f'{rus}: {h['from']}-{h['to']}')
    return '; '.join(parts)


with open('2gis_orgs.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(
        [
            'id',
            'name',
            'address',
            'phones',
            'emails',
            'websites',
            'rating',
            'reviews',
            'schedule',
            'rubrics',
        ]
    )

    for item in all_items.values():
        reviews = item.get('reviews', {})
        rubrics = ', '.join(r['name'] for r in item.get('rubrics', []))

        writer.writerow(
            [
                item['id'],
                item['name'],
                item.get('full_address_name', ''),
                extract_phones(item),
                extract_emails(item),
                extract_websites(item),
                reviews.get('rating', ''),
                reviews.get('review_count', ''),
                extract_schedule(item),
                rubrics,
            ]
        )

print(f'Сохранено {len(all_items)} организаций в 2gis_orgs.csv')

Функции extract_phones, extract_emails и extract_websites обрабатывают вложенную структуру contact_groups. У одной организации может быть несколько телефонов с разными комментариями («отдел продаж», «бухгалтерия»), поэтому все номера объединяются через точку с запятой.

Способ 2: внутренний API (без ключа)

2ГИС использует внутренние API-эндпоинты для загрузки данных на фронтенде. Перехватить их можно через DevTools (вкладка Network → фильтр XHR). Этот подход не требует API-ключа, но нестабилен — эндпоинты меняются без предупреждения.

import requests
import time

session = requests.Session()
session.headers.update(
    {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
        'AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
        'Accept': 'application/json',
        'Referer': 'https://2gis.ru/',
    }
)


def search_internal(query, city='novosibirsk', page=1):
    '''Поиск через внутренний API 2ГИС.'''
    url = 'https://catalog.api.2gis.com/3.0/items'
    params = {
        'q': query,
        'city_id': get_city_id(city),
        'key': 'rurbbn3446',  # публичный ключ из JS-бандла 2ГИС
        'page_size': 12,
        'page': page,
        'locale': 'ru_RU',
        'fields': 'items.contact_groups,items.schedule,'
        'items.reviews,items.org,items.rubrics',
    }

    response = session.get(url, params=params)
    return response.json() if response.status_code == 200 else None


# Публичный ключ зашит в JS-коде 2gis.ru — найдите актуальный через DevTools

Публичный ключ (key) вшит в JavaScript-бандл сайта 2gis.ru. Он периодически меняется. Чтобы найти актуальный: откройте 2gis.ru → DevTools → Network → выполните поиск → найдите запрос к catalog.api.2gis.com → скопируйте key из параметров.

Парсинг отзывов

Отзывы загружаются через отдельный эндпоинт.

def get_reviews(branch_id, page=1):
    '''Получить отзывы для организации по ID филиала.'''
    url = f'https://public-api.reviews.2gis.com/2.0/branches/{branch_id}/reviews'
    params = {
        'page': page,
        'limit': 50,
        'is_advertiser': 'false',
        'fields': 'meta.providers,meta.branch_rating,' 'meta.branch_reviews_count',
        'sort_by': 'date_created',
        'key': 'rurbbn3446',
        'locale': 'ru_RU',
    }

    response = session.get(url, params=params)
    if response.status_code == 200:
        return response.json()
    return None


# Пример: сбор всех отзывов
def get_all_reviews(branch_id, max_reviews=500):
    all_reviews = []
    page = 1

    while len(all_reviews) < max_reviews:
        data = get_reviews(branch_id, page=page)
        if not data:
            break

        reviews = data.get('reviews', [])
        if not reviews:
            break

        for review in reviews:
            all_reviews.append(
                {
                    'author': review.get('user', {}).get('name', ''),
                    'rating': review.get('rating', 0),
                    'text': review.get('text', ''),
                    'date': review.get('date_created', ''),
                }
            )

        page += 1
        time.sleep(0.5)

    return all_reviews[:max_reviews]

Эндпоинт public-api.reviews.2gis.com возвращает отзывы с пагинацией по 50 штук. Apify-парсер использует именно этот подход — чистые HTTP-запросы без браузера.

Способ 3: Playwright (для сложных случаев)

Если внутренние эндпоинты заблокированы или нужна визуальная верификация, используйте Playwright с перехватом сетевых запросов.

import asyncio
import json
import random

from playwright.async_api import async_playwright
from playwright_stealth import stealth_async


captured_data = []


async def intercept_response(response):
    '''Перехватываем ответы API каталога.'''
    if 'catalog.api.2gis.com' in response.url and response.status == 200:
        try:
            data = await response.json()
            if 'result' in data and 'items' in data['result']:
                captured_data.extend(data['result']['items'])
                print(f'Перехвачено: {len(data['result']['items'])} организаций')
        except Exception:
            pass


async def scrape_2gis(query, city='novosibirsk', max_pages=5):
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        context = await browser.new_context(
            user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
            'AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
            viewport={'width': 1920, 'height': 1080},
            locale='ru-RU',
        )
        page = await context.new_page()
        await stealth_async(page)

        # Подписываемся на ответы
        page.on('response', intercept_response)

        # Первая загрузка
        url = f'https://2gis.ru/{city}/search/{query}'
        await page.goto(url, wait_until='networkidle')
        await page.wait_for_timeout(3000)

        # Пагинация: прокрутка списка для подгрузки
        for i in range(max_pages - 1):
            # Скроллим список результатов до конца
            list_container = page.locator('div._1YJXW')  # контейнер списка
            for _ in range(5):
                await list_container.evaluate('el => el.scrollTop += 500')
                await page.wait_for_timeout(random.randint(500, 1000))

            # Кликаем 'Далее' если есть
            next_btn = page.locator('div._pagination button:last-child')
            if await next_btn.count() > 0 and await next_btn.is_enabled():
                await next_btn.click()
                await page.wait_for_timeout(random.randint(3000, 5000))
            else:
                break

            print(f'Страница {i + 2}, перехвачено всего: {len(captured_data)}')

        await browser.close()

    return captured_data


results = asyncio.run(scrape_2gis('стоматология', city='novosibirsk', max_pages=10))
print(f'Собрано {len(results)} организаций')

Перехват через page.on("response", ...) ловит ответы внутреннего API, которые 2ГИС отправляет при навигации. Данные приходят в структурированном JSON — том же формате, что и в официальном API. Этот подход комбинирует преимущества браузерной автоматизации (обход защит) и чистых данных (JSON вместо HTML-парсинга).

Готовый парсер: Parser2GIS

Для тех, кто не хочет писать код с нуля, существует открытый парсер Parser2GIS — расширение для Chrome.

https://github.com/interlark/parser-2gis

Возможности Parser2GIS:

  • Работает как расширение браузера Chrome — не требует Python
  • Собирает: название, адрес, телефоны, email, сайт, координаты, рубрику
  • Экспорт в CSV и Excel
  • Поддерживает Россию, Казахстан, Беларусь, Азербайджан
  • Бесплатный и с открытым исходным кодом

Ограничения: не собирает отзывы и расписание, скорость ограничена скоростью ручного скроллинга в браузере.

Обход блокировок

Антибот-защита 2ГИС

2ГИС защищается от парсинга менее агрессивно, чем Авито, но всё же блокирует массовые запросы .

Механизм Описание Решение
Rate Limiting Ограничение числа запросов с одного IP Задержки 0.3–1 сек, ротация прокси
API-ключ Без ключа API не отвечает Публичный ключ из JS или официальный ключ
CAPTCHA Показывается при подозрительной активности Мобильные прокси, stealth-плагин
Блокировка IP Бан при агрессивном парсинге Ротация резидентных/мобильных прокси

Ротация прокси

import random


PROXIES = [
    'http://user:pass@proxy1.example.com:8080',
    'http://user:pass@proxy2.example.com:8080',
    'http://user:pass@proxy3.example.com:8080',
]


def make_request(url, params, max_retries=3):
    for attempt in range(max_retries):
        proxy = random.choice(PROXIES)
        try:
            response = requests.get(
                url,
                params=params,
                proxies={'http': proxy, 'https': proxy},
                timeout=15,
                headers={
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                    'AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
                },
            )
            if response.status_code == 200:
                return response.json()
            elif response.status_code == 429:
                wait = 5 * (attempt + 1)
                print(f'Rate limit, жду {wait} сек...')
                time.sleep(wait)
        except requests.exceptions.RequestException as e:
            print(f'Ошибка прокси {proxy[:30]}: {e}')
            time.sleep(2)

    return None

Код 429 означает превышение лимита запросов. Экспоненциальная задержка (5, 10, 15 сек) даёт серверу время восстановиться.

Сохранение в SQLite с полной структурой

import json
import sqlite3


def init_db(db_path='2gis.db'):
    conn = sqlite3.connect(db_path)
    conn.execute(
        '''
        CREATE TABLE IF NOT EXISTS organizations (
            id TEXT PRIMARY KEY,
            name TEXT,
            legal_name TEXT,
            address TEXT,
            lat REAL,
            lon REAL,
            phones TEXT,
            emails TEXT,
            websites TEXT,
            rating REAL,
            review_count INTEGER,
            rubrics TEXT,
            schedule TEXT,
            inn TEXT,
            branch_count INTEGER,
            raw_json TEXT,
            scraped_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    '''
    )
    conn.execute(
        '''
        CREATE TABLE IF NOT EXISTS reviews (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            org_id TEXT REFERENCES organizations(id),
            author TEXT,
            rating INTEGER,
            text TEXT,
            date_created TEXT
        )
    '''
    )
    conn.commit()
    return conn


def save_org(conn, item):
    point = item.get('point', '')
    lat, lon = (0, 0)
    if point:
        parts = point.split(',')
        if len(parts) == 2:
            lat, lon = float(parts[0]), float(parts[1])

    name_ex = item.get('name_ex', {})
    reviews = item.get('reviews', {})

    conn.execute(
        '''INSERT OR REPLACE INTO organizations
        (id, name, legal_name, address, lat, lon, phones, emails, websites,
         rating, review_count, rubrics, schedule, inn, branch_count, raw_json)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
        (
            item['id'],
            item['name'],
            name_ex.get('legal_name', ''),
            item.get('full_address_name', ''),
            lat,
            lon,
            extract_phones(item),
            extract_emails(item),
            extract_websites(item),
            float(reviews.get('rating', 0) or 0),
            int(reviews.get('review_count', 0) or 0),
            ', '.join(r['name'] for r in item.get('rubrics', [])),
            extract_schedule(item),
            item.get('itin', ''),
            int(item.get('org', {}).get('branch_count', 0) or 0),
            json.dumps(item, ensure_ascii=False),
        ),
    )
    conn.commit()

Колонка raw_json сохраняет полный ответ API без потерь — можно пересобрать любое поле позже, не обращаясь к API повторно. INSERT OR REPLACE обновляет запись при повторном парсинге.

Сравнение подходов

Характеристика Catalog API 3.0 Внутренний API (без ключа) Playwright + перехват Parser2GIS (расширение)
Скорость 100–200 орг/мин 100–200 орг/мин 20–40 орг/мин 5–15 орг/мин
Нужен API-ключ Да (официальный) Нет (публичный из JS) Нет Нет
Стабильность Высокая Средняя (ключ меняется) Средняя Низкая
Отзывы Ограничено Да (отдельный эндпоинт) Да Нет
Стоимость Зависит от тарифа Бесплатно + прокси Бесплатно + прокси Бесплатно
Программирование Python Python Python Не требуется

Юридические риски

Парсинг 2ГИС сопряжён с правовыми рисками, аналогичными Яндекс Картам .

  • Пользовательское соглашение (п. 2.13): Прямо запрещает применение автоматизированных скриптов, ботов и программ для сбора и выгрузки данных. Администрация вправе заблокировать автоматизированный доступ.
  • Пользовательское соглашение (п. 3.4): Запрещает копирование контента, элементов дизайна, программ и баз данных, их декомпиляцию и модификацию.
  • ГК РФ, ст. 1334: База данных 2ГИС защищена смежным правом изготовителя. Систематическое извлечение существенной части данных нарушает исключительное право. Компенсация до 5 млн рублей.
  • ФЗ-152: Сбор персональных данных (имена авторов отзывов, контакты ИП) без согласия субъектов нарушает закон о персональных данных.
  • УК РФ, ст. 272: Обход технических средств защиты может квалифицироваться как неправомерный доступ к компьютерной информации. Штраф до 200 000 руб. или лишение свободы до 2 лет.

Наиболее безопасный путь — использовать официальный API в рамках тарифного плана, собирать только агрегированные данные без персональной информации и не создавать чрезмерной нагрузки на серверы.

Неочевидные детали

Первый факт: поле point в ответе API возвращает координаты в формате широта,долгота, а параметр запроса point принимает долготу,широту. Это частый источник ошибок — координаты перепутаны местами.

Второй факт: у одной организации может быть несколько contact_groups с разными расписаниями. Например, «Отдел продаж» работает с 9 до 18, а «Сервисный центр» — с 10 до 20. Основное расписание в поле schedule относится к организации в целом.

Третий факт: параметр fields критически важен для полноты данных. Без него API возвращает минимальный набор: ID, название, адрес, тип. Телефоны, расписание, отзывы, рубрики, ИНН — всё это дополнительные поля, которые нужно явно запрашивать.

Четвёртый факт: 2ГИС хранит даты создания и обновления карточки организации в поле dates. Это позволяет фильтровать только новые организации: opened_after_date=2025-01-01 в API 2.0, или сортировка по dates.created_at в ручном парсинге.

Пятый факт: публичный ключ из JS-бандла 2ГИС (rurbbn3446 или аналогичный) обычно работает стабильнее, чем можно ожидать, но привязан к домену через CORS. Из Python-скрипта CORS не проверяется, поэтому ключ работает. Но 2ГИС может заблокировать его для нестандартных User-Agent.

FAQ

Чем отличается Catalog API 2.0 от 3.0?

Версия 2.0 (deprecated) использовала отдельные эндпоинты для зданий, организаций, рубрик. Версия 3.0 объединяет всё в единый метод items с гибкой системой параметров. Используйте 3.0 — старая версия может быть отключена.

Как узнать region_id для нужного города?

Отправьте запрос к catalog.api.2gis.com/3.0/regions?q=Казань&key=ваш-ключ. В ответе будет id региона.

Сколько организаций можно собрать за день?

Через официальный API с ключом — зависит от тарифа (обычно десятки тысяч запросов/день). Через внутренний API без прокси — 2000–5000 организаций до блокировки. С ротацией мобильных прокси — без практических ограничений.

Можно ли собрать фотографии организаций?

Да. Добавьте items.external_content в параметр fields. Фотографии возвращаются как URL-адреса, которые можно скачать отдельным скриптом.

Как парсить конкретную организацию по ссылке?

Извлеките ID из URL (числовая часть после /firm/): https://2gis.ru/novosibirsk/firm/70000001025624768 → ID = 70000001025624768. Затем запросите catalog.api.2gis.com/3.0/items/byid?id=70000001025624768&key=ваш-ключ.

Мой совет: начните с официального Catalog API 3.0. Бесплатного тарифа хватает для сбора 5000–10 000 организаций. Обязательно указывайте fields — без него потеряете 80% полезных данных. Для отзывов используйте отдельный эндпоинт public-api.reviews.2gis.com. Для полного покрытия города разбейте его на сетку точек с радиусом 1.5–2 км и дедуплицируйте по ID. И храните raw_json в базе — API может поменяться, а исходные данные останутся с вами.

Читайте также