# 2GIS Places Scraper (`nice_dev/2gis-places-scraper`) Actor

Scrape 2GIS business listings by keyword, category, city or map area: name, address, phones, e-mails, website, socials, opening hours, rating, reviews and photos. Russia, Kazakhstan, UAE, Gulf and 20 countries. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice_dev/2gis-places-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Lead generation, Travel, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.15 / 1,000 places

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### 🏢 What is 2GIS Places Scraper?

**2GIS Places Scraper** extracts **business listings from [2GIS](https://2gis.ru)**: **name, address, coordinates, categories, opening hours, rating, website and attributes** of every place, and on request **phone numbers, e-mails, WhatsApp / Telegram / social links, reviews and photos**. It covers the **206 regions of 2GIS in 20 countries** (Russia, Kazakhstan, the UAE and the Gulf, Central Asia, the Caucasus, Cyprus, Czechia, Italy, Chile…). It works as a **2GIS API alternative**: run it on a schedule, call it from your code, or plug it into Make, Zapier or n8n.

Type a **keyword** (`кафе`, `dentist`) and a **city** (`Москва`, `Dubai`, `Almaty`), or draw an area on the map, or paste any 2GIS URL, click **Start**, and download the places in JSON, CSV or Excel. No login, nothing to set up, and **no cap on the number of results**: every place 2GIS counts for a search can be saved. 300 places with their contacts take about 50 seconds.

### 📋 What data can you extract from 2GIS?

One item per place (branch), 101 fields:

| Category | What you get |
| --- | --- |
| 🏷️ **Place** | name, short name and type, legal name, brand and its number of branches, place page — `Руки Вверх!, бар` |
| 🗂️ **Categories** | main category, all categories with their 2GIS ids, map icon type, short summary — `Бар`, `Кафе` |
| 📍 **Address and map** | full address, street, house number, postal code, floor / office, district, city, region, country, latitude / longitude, time zone — `Москва, Можайское шоссе, 45Б` |
| 🏬 **Building and access** | building id and name, entrances, nearest metro stations with walking distance, car parks, busy hours |
| ☎️ **Contacts** (option) | phone numbers, e-mails, WhatsApp, Telegram, Viber, VK, Instagram, Facebook, YouTube and other social links — `+74951205368` |
| 🌐 **Website** | website without tracking parameters, and whether the place has a phone, an e-mail, a website (always filled) |
| 🕒 **Opening hours** | day by day, in one line, open 24/7 or not, schedule comment |
| ⭐ **Rating** | rating of the branch and of the brand, number of reviews and ratings, Flamp rating — `4.8` from `335` reviews |
| ✨ **Attributes** | Wi-Fi, vegan menu, card payment… with their 2GIS tags, awards, average bill — `Средний счёт 1500 ₽` |
| 📷 **Photos** | number of photos, albums and main photo (always); the full gallery (option) |
| 💬 **Reviews** (option) | text, stars, date, author, likes, photos, the owner's answer, source (2GIS, Flamp, Booking) |
| 🔎 **Search** | keyword, location, rank in the results and number of results of the search that found the place |

Every field, with an example, is listed in the **Output** section below.

Contacts, reviews and the photo gallery are **options, off by default**: tick **Include contacts**, **Include reviews** or **Include photos** to add them (each is charged as its own event, see the pricing below). Without contacts, the phone and e-mail fields are `null` (not asked), never an empty list.

### ✅ Why use 2GIS Places Scraper?

- ♾️ **No cap on results**: the whole search is read, page after page (all 8,600+ cafés 2GIS counts in the Moscow region, not the first few hundred).
- 🌍 **Several cities and countries in one run**: keywords × locations (`Москва`, `Dubai`, `Almaty`… up to 500 searches per run), each place saved once, with a cap per search so that one big city cannot eat the whole budget.
- 🗺️ **Search the map your way**: a city, a point and a radius, a rectangle or a drawn area (GeoJSON) — or paste 2GIS search, place, branch, building or short-link URLs, or place IDs.
- 🎛️ **Filters 2GIS has, and more**: website, photos, reviews, open now, open 24 hours, attribute tags, metro stations, average bill — plus minimum rating, minimum review count, phone or e-mail required, promoted places skipped, words or places excluded.
- 💬 **Reviews you can filter**: by stars, by date, with text only, with the owner's answer only, by source (2GIS, Flamp, Booking) or by keyword.
- 🔔 **Monitoring built in**: tick **Only new places**, schedule the Actor, and each run returns (and charges) only the places it has never delivered before.
- 🌐 **Local languages**: names, addresses and categories in Russian, Kazakh, English, Arabic, Czech, Spanish, Italian and the other languages each region offers.
- 🔌 API, scheduling, monitoring, integrations (Make, Zapier, n8n, Google Sheets…), proxy rotation and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape 2GIS

1. Create a free Apify account.
2. Open **2GIS Places Scraper**, type a **Search keyword** (e.g. `кафе`) and a **Location** (e.g. `Москва`, `Dubai`, `Almaty`, or a 2GIS region ID).
3. Or paste your own 2GIS URLs into **Start URLs** (search results, place pages, branch lists, building pages, short links), or place IDs into **Place IDs**.
4. Tick the options you need (**Include contacts**, **Include reviews**, **Include photos**), set **Max places** (100 by default, 0 = no limit) — and **Max places per search** when you run several searches — then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape 2GIS?

This Actor uses **pay per event** pricing. You pay for the places saved, and for each option only when it delivers something:

| What you pay for | Event | Price per 1,000 (Free plan → Gold) |
| --- | --- | --- |
| Place saved (address, map, categories, hours, rating, website, attributes, photo count) | `apify-default-dataset-item` | $0.18 → $0.15 |
| Contacts of a place, only with **Include contacts** (once per place saved with at least one phone, e-mail, messenger or social link) | `place-contacts` | $0.27 → $0.24 |
| Review, only with **Include reviews** (once per review saved) | `review` | $0.18 → $0.15 |
| Photo gallery of a place, only with **Include photos** (once per place saved with at least one gallery photo) | `place-photos` | $0.45 → $0.089 |
| Place read, then dropped by one of the Actor's own filters | `filter-check` | $0.051 → $0.021 |
| Place whose results page came through the fallback connection — only when the default proxy stops answering 2GIS (3 calls in a row), never otherwise | `residential-fallback` | $0.039 → $0.01 |
| Run start | `apify-actor-start` | $0.01 per run |

The three options are **off by default**: nothing is charged for them unless you tick them. The filters 2GIS applies itself (places with a website, photos, reviews, open now, open 24 hours, attribute tags, metro stations, average bill) cost nothing extra. The others (places without a website, minimum rating, minimum review count, phone / e-mail / website required, promoted places skipped, excluded words or IDs) are checked by the Actor: a place one of them drops is not saved, not counted in **Max places**, and costs only the `filter-check` fee; a place kept pays its normal price only. Platform usage (compute, proxy) is included in the price.

Prices go down with your Apify plan (Free, Bronze, Silver, Gold). Two examples on the Gold plan: **1,000 places with their contacts** cost at most $0.01 + 1,000 × ($0.15 + $0.24) / 1,000 = **$0.40** (places without any contact do not pay the contacts fee); a **daily `onlyNew` check** that finds 30 new places costs $0.01 + 30 × $0.15 / 1,000 ≈ **$0.015** per day.

### ⚙️ Input

```json
{
    "query": "кафе",
    "location": "Москва",
    "maxItems": 100,
    "includeContacts": true
}
```

Several keywords in several cities and countries, a cap per search, only well-rated places, only the ones not delivered before:

```json
{
    "searchQueries": ["кафе", "стоматология"],
    "locations": ["Москва", "Almaty"],
    "maxItemsPerQuery": 50,
    "minRating": 4.5,
    "onlyNew": true,
    "stateKey": "cafes-dentists-weekly"
}
```

Around a point on the map, nearest first, in English:

```json
{
    "query": "coffee",
    "location": "Dubai",
    "coordinates": "25.1972,55.2744",
    "radiusKm": 3,
    "sortBy": "distance",
    "language": "en_AE"
}
```

The complaints of the last 30 days about one place:

```json
{
    "placeIds": ["70000001063551091"],
    "includeReviews": true,
    "maxReviewsPerPlace": 100,
    "postedAfter": "30 days",
    "maxReviewRating": 2
}
```

Or with your own URLs:

```json
{
    "startUrls": [
        { "url": "https://2gis.ru/moscow/search/кафе" },
        { "url": "https://2gis.ru/moscow/branches/70000001034739764" }
    ],
    "maxItems": 500
}
```

| Field | Notes |
| --- | --- |
| `query`, `searchQueries` | What to look for, as typed on 2GIS, in any language (`кафе`, `dentist`, `coffee`); `searchQueries` adds more keywords (one search each). A keyword needs a location or a map area. Ignored when `startUrls` or `placeIds` is set. |
| `location`, `locations` | City or region covered by 2GIS: name in Russian or English (`Москва`, `Dubai`, `Almaty`), URL slug (`moscow`) or 2GIS region ID (`32`); `locations` adds more places. Every keyword is searched in every location (max 500 searches per run). A country counts one search per region (Russia: 154). |
| `rubricIds` | 2GIS category IDs (e.g. `161` = cafés; each place gives the IDs of its categories in the rubrics column). With no keyword, the whole category is listed in the location. |
| `coordinates`, `radiusKm` | Search around a point instead of a named place: latitude first (`55.7558,37.6173`), with a radius from 0.05 to 40 km (5 km by default). |
| `boundingBox`, `customGeolocation` | A rectangle, south, west, north, east (`55.57,37.35,55.92,37.85`), or a drawn area as a GeoJSON Polygon or MultiPolygon (longitude first, as in GeoJSON). The drawn area wins over the rectangle, the rectangle over the point. With a location too, the area decides where to search and the location only picks the 2GIS region and the language. |
| `startUrls` | 2GIS URLs on any 2GIS domain: search results (`https://2gis.ru/moscow/search/кафе`, filters set on the site are kept), place pages (`https://2gis.ru/moscow/firm/70000001063551091`), all the branches of a brand (`https://2gis.ru/moscow/branches/70000001034739764`), building pages (`https://2gis.ru/moscow/geo/4504235295703276`) or short links (`https://go.2gis.com/dGVzM`). Up to 1,000 URLs. |
| `placeIds` | Place IDs, the number after /firm/ in a place URL (`70000001063551091`). Up to 500. |
| `maxItems`, `maxItemsPerQuery` | Stop after this many places for the whole run (`0` = unlimited); cap for EACH search (keyword × location, or search URL), `0` = no per-search cap. |
| `language` | `auto` (the main language of each region, such as Russian in Moscow, Azerbaijani in Baku or English in Dubai) or a 2GIS locale such as `en_AE`, `ru_RU`, `kk_KZ`. A language the region does not offer is replaced by the main language of the region, with a warning in the log. Review texts stay as written. |
| `sortBy` | `relevance` (2GIS default), `rating`, `name`, `opened_time` (newest first, upcoming openings included), or distance from `coordinates`. |
| `includeContacts` | Option, off by default: phones, e-mails, messengers and social links of each place, charged as **place-contacts**. The website and the has-phone / has-e-mail / has-website flags are always filled. |
| `minRating`, `minReviewCount` | Only places rated at least this (`4.5`; places without a rating are dropped) or with at least this many reviews. |
| `hasWebsite` | `any`, `with` (only places with a website, applied by 2GIS at no extra cost) or `without` (only places without one: checked by the Actor, each place dropped costs one filter-check). |
| `hasPhotos`, `hasReviews`, `openNow`, `open24h` | 2GIS filters: only places with photos, with reviews, open at the time of the run, open around the clock. |
| `requirePhone`, `requireEmail`, `requireWebsite` | Drop the places without a phone, an e-mail or a website. The first two need `includeContacts`. |
| `attributeTags` | Only places with all these 2GIS attributes (`food_service_food_vegan`, `wifi`); the tag of each attribute is in the attributes column. |
| `metroStationIds` | Only places near any of these stations, by 2GIS station ID (`4504385606385704`, also given in the nearestStations column). |
| `avgPriceMin`, `avgPriceMax` | Average bill range in the local currency (`1500`), for restaurants, cafés and other food places. |
| `excludeKeywords` | Drop the places whose name, category or address contains one of these words (`шаурма`, a street or a mall; case and accents ignored). A city name drops every place of that city. |
| `excludePlaceIds` | Never save these places (`70000001063551091`): your own branches, or places you already have. |
| `skipAds` | Drop the places 2GIS shows as promoted (advertising). |
| `includeReviews`, `maxReviewsPerPlace` | Option, off by default: the reviews of each place (20 per place by default; 0 = all, up to about 6,000 per place: a row is capped at 6 MB, and the run log says when), from 2GIS, Flamp and Booking, charged as **review** per review saved. A place is never dropped by the review filters below. |
| `reviewSort` | `date_created` (newest first), `date_edited`, `friends` or `trust` (most trusted first). |
| `postedAfter`, `postedBefore` | Review date range: `2026-09-01`, or a period before now (`7 days`, `2 weeks`, `1 month`, `24 hours`). |
| `minReviewRating`, `maxReviewRating` | Star range of the reviews kept (`2` as maximum = complaints only); 0 = all. |
| `onlyWithText`, `onlyWithBusinessReply`, `includeUnratedReviews` | Only reviews with text, only reviews the business answered; add the 2GIS reviews that carry no stars. |
| `reviewKeywords` | Keep only the reviews whose text contains one of these words (`холодно`, `cold`). |
| `reviewSources` | Sites the reviews come from: `2gis`, `flamp`, `booking`; empty = all. |
| `includePhotos`, `maxPhotos` | Option, off by default: the photo gallery of each place (full-size URL, size, description), 20 photos per place by default (0 = all), charged as **place-photos**. The cover photo and the photo count are always in the output. |
| `photoAlbums` | Albums read: `common` (photos of the place, default), `view` (photos of its building), `facilities`. |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the places never delivered under this memory key (`moscow-cafes`); `resetState` forgets the memory. |
| Advanced | `proxyConfiguration` (Apify proxy by default, included in the price; the residential proxy is not available), `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

One place, with **Include contacts** on (shortened: the empty and optional fields are left out here):

```json
{
    "id": "70000001063535285",
    "url": "https://2gis.ru/moscow/firm/70000001063535285",
    "name": "Чисто Ростовский Бар",
    "nameShort": "Чисто Ростовский Бар",
    "nameExtension": null,
    "orgId": "70000001045316941",
    "orgName": "Чисто Ростовский Бар",
    "branchCount": 5,
    "isPromoted": false,
    "category": "Бары",
    "categories": ["Кафе", "Бары"],
    "rubrics": [{ "id": "161", "name": "Кафе", "alias": "kafe", "kind": "additional" }, { "id": "159", "name": "Бары", "alias": "bary", "kind": "primary" }],
    "address": "Садовая-Кудринская улица, 32 ст1",
    "fullAddress": "Москва, Садовая-Кудринская улица, 32 ст1",
    "postalCode": "123001",
    "addressComment": "1 этаж",
    "city": "Москва",
    "district": "Пресненский",
    "region": "Москва",
    "country": "Россия",
    "countryCode": "ru",
    "regionId": "32",
    "regionSlug": "moscow",
    "buildingId": "4504235282748336",
    "latitude": 55.765404,
    "longitude": 37.590643,
    "phone": "+74951234567",
    "phones": ["+74951234567"],
    "email": "chistorostovskiybar0000@yandex.ru",
    "emails": ["chistorostovskiybar0000@yandex.ru"],
    "vkontakte": "https://vk.com/chistorostovbar",
    "website": "http://hrbbar.ru",
    "websites": ["http://hrbbar.ru"],
    "hasPhone": true,
    "hasEmail": true,
    "hasWebsite": true,
    "openingHoursText": "Mon-Tue 12:00-24:00; Wed-Fri 12:00-03:00; Sat 14:00-03:00; Sun 14:00-24:00",
    "is24x7": false,
    "rating": 4.9,
    "reviewsCount": 131,
    "ratingCount": 239,
    "orgRating": 4.8,
    "orgReviewsCount": 261,
    "attributes": [
        { "group": "Ресторан / Кафе", "name": "Чек 1250 ₽", "tag": "food_service_avg_price" },
        { "group": "Ресторан / Кафе", "name": "Пивной бар", "tag": "food_service_bar_type_beer_bar" }
    ],
    "avgCheck": "Чек 1250 ₽",
    "photosCount": 134,
    "mainPhotoUrl": "https://i6.photo.2gis.com/main/branch/32/70000001063535285/common",
    "createdAt": "2022-08-04T00:00:00.000Z",
    "updatedAt": "2026-09-21T15:08:08.000Z",
    "searchQuery": "кафе",
    "searchLocation": "Москва",
    "searchRank": 272,
    "totalResults": 8605,
    "searchUrl": "https://2gis.ru/moscow/search/%D0%BA%D0%B0%D1%84%D0%B5",
    "scrapedAt": "2026-10-15T14:19:55.660Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 101 fields

| Fields | What you get |
| --- | --- |
| `id`, `url`, `name`, `nameShort`, `nameExtension`, `legalName` | **Place**: 2GIS place (branch) ID, place page, name, name without its type, type (`бар`), legal name when 2GIS shows it |
| `orgId`, `orgName`, `branchCount`, `orgShortName`, `orgExtension` | **Brand**: organization ID and name, number of branches (`9`), name without its type, type (`сеть баров`) |
| `isPromoted`, `adText`, `adWarning` | promoted (advertising) place or not, its advertising text and small print |
| `category`, `categories`, `rubrics`, `poiCategory`, `summary` | **Categories**: main category, all of them, with their 2GIS IDs (`161` = cafés), map icon type (`cafe`), short description |
| `address`, `fullAddress`, `street`, `houseNumber`, `postalCode`, `makani`, `addressComment`, `landmark` | **Address**: street and number, with the city, street, house number, postal code, Makani number (Dubai), floor / office, landmark |
| `city`, `district`, `districtArea`, `livingArea`, `settlement`, `region`, `country`, `countryCode`, `regionId`, `regionSlug` | city, district, area, neighbourhood, village, region, country, ISO code (`ru`), 2GIS region ID (`32`) and URL slug (`moscow`) |
| `buildingId`, `buildingName`, `entrances`, `nearestStations`, `parking`, `congestion` | **Building and access**: building ID and name, entrances on the map, nearest metro / train / tram stations with distance, car parks, busy hours |
| `latitude`, `longitude`, `timezone`, `timezoneOffset` | coordinates (WGS84), time zone and its offset from UTC in minutes |
| `phone`, `phones`, `phonesFormatted`, `phoneDetails`, `email`, `emails` | **Contacts** (Include contacts): first phone and all of them, as 2GIS stores them (mostly international format such as `+74951234567`; toll-free numbers and extensions kept as is), with their labels; e-mails — `null` when the option is off |
| `whatsapp`, `telegram`, `viber`, `vkontakte`, `odnoklassniki`, `instagram`, `facebook`, `youtube`, `twitter`, `socialLinks` | **Messengers and social links** (Include contacts): one column per network, and all links with their type — `null` when the option is off |
| `website`, `websites`, `hasPhone`, `hasEmail`, `hasWebsite` | **Website**: first and all websites, tracking parameters removed; whether the place has a phone, an e-mail, a website (always filled) |
| `openingHours`, `openingHoursText`, `is24x7`, `scheduleComment` | **Opening hours**: one row per day and time range, in one line, open 24/7, comment (breaks, holidays) |
| `rating`, `reviewsCount`, `ratingCount`, `orgRating`, `orgReviewsCount`, `flampRating`, `flampReviewsCount`, `isReviewable` | **Rating**: rating of the branch (`4.8`), reviews and ratings counts, rating and reviews of the brand, Flamp rating and reviews, reviews open or not |
| `attributes`, `badges`, `avgCheck` | **Attributes**: group, name and tag of each fact (Wi-Fi, vegan menu…), awards, average bill as written by 2GIS |
| `photosCount`, `photoAlbums`, `mainPhotoUrl` | **Photos** (always): number of photos, albums with their cover, main photo |
| `hasGoods`, `hasPinnedGoods`, `hasDiscount`, `hasRealty` | the place lists goods or a menu, pins featured goods, shows a discount, lists real estate offers |
| `reviews` | **Reviews** (Include reviews): ID, stars, text, dates, author, likes, photos, owner's answer, source — `null` when the option is off |
| `photos` | **Photo gallery** (Include photos): ID, full-size and preview URL, size, description, album, upload date — `null` when the option is off |
| `createdAt`, `updatedAt` | date the place was added to 2GIS, last update (ISO 8601 UTC) |
| `searchQuery`, `searchLocation`, `searchRank`, `totalResults`, `searchUrl`, `searchRubricId`, `scrapedAt` | **Search**: keyword, location, rank (`1` = first), number of results 2GIS announces, search page, category ID of the search (`null` for URLs and IDs), extraction timestamp |

### 💡 Tips

#### How to get more results

Set `maxItems` to `0`: there is no cap, every place 2GIS counts for a search is read. To cover a whole country, put all its cities in `locations`; to cover a whole category, give its ID in `rubricIds` with or without a keyword. A place found by several searches is saved — and charged — once.

#### How to reduce costs

The price is per place, so the levers are `maxItems`, `maxItemsPerQuery`, the filters 2GIS applies itself (free: places with a website, photos, reviews, open now, attribute tags, average bill; places without a website are checked by the Actor, one `filter-check` per place dropped), and `onlyNew` for recurring runs (you never pay twice for the same place). Contacts, reviews and photos are only charged when you tick them; set `maxReviewsPerPlace` and the review filters to pay only for the reviews you need.

#### Several searches in one run

Fill `searchQueries` and / or `locations`: the Actor runs one search per keyword × location (3 keywords × 4 cities = 12 searches, up to 500 per run; a country is searched region by region, so `Россия` alone is 154 searches). The single `query` and `location` fields still work and are added to the lists. Set `maxItemsPerQuery` to give every search its own cap: without it the first searches can use up the whole `maxItems` budget. You can also paste several search URLs into `startUrls`: each one is a search of its own, with the same cap. Each place says which search found it (`searchQuery`, `searchLocation`, `searchRank`, `totalResults`).

#### Monitoring: only the new places

Tick **Only new places** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the places already delivered: they are not saved and not charged. The memory lives in a named key-value store of your account (`2gis-places-scraper-seen`, up to 150,000 places per key) and is only updated with places that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's places), and tick `resetState` once to start over.

#### Filter reviews by date

`postedAfter` and `postedBefore` are dates of the **reviews**, not of the places: a date (`2026-09-01`, the whole day is included) or a period before now (`7 days`, `2 weeks`, `1 month`; via the API also `24 hours` or a full ISO date-time). They only choose which reviews are kept: a place is never dropped by them. Combine them with `maxReviewRating: 2` to collect the recent complaints, or with `onlyWithBusinessReply` to see how a business answers.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per 2GIS place (branch). Actor id: `nice_dev/2gis-places-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/2gis-places-scraper`.

Smallest input, for a cheap first call:

```json
{
    "query": "кафе",
    "location": "Москва",
    "maxItems": 10
}
```

Key output fields: `name`, `fullAddress`, `latitude`, `longitude`, `category`, `rating`, `website`, `phones` and `emails` (with `includeContacts`), `reviews` (with `includeReviews`).

Cost: see the pricing section above (per place saved, per option delivered, plus a small fee per run start). Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape 2GIS?

The Actor only reads what 2GIS shows publicly to any anonymous visitor. It logs in to nothing and solves no captcha. Business listings are public, but some results can contain personal data — the names of review authors, the phone number of a sole trader — which is protected by GDPR and similar laws: do not store it without a legitimate reason. You are responsible for using the data in compliance with 2GIS's Terms of Use and applicable law. This Actor is not affiliated with 2GIS.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). If the default proxy stops answering 2GIS (3 calls in a row with no answer), the Actor switches the place searches to its own fallback connection for the rest of the run: the run goes on, and each place read that way pays the small `residential-fallback` fee. This never happens with your own proxy URLs. A request the site turns away is retried at once on a new proxy session, up to 10 times on top of the retries (without a proxy, after a pause of 5 seconds, doubled at each retry up to 150 seconds).

#### Which cities and countries are covered?

All the regions of 2GIS: 206 regions in 20 countries — Russia, Kazakhstan, Kyrgyzstan, Uzbekistan, Tajikistan, Belarus, Armenia, Azerbaijan, Georgia, the United Arab Emirates, Saudi Arabia, Qatar, Kuwait, Bahrain, Oman, Iraq, Cyprus, Czechia, Italy and Chile. Type the city in Russian or English, or its 2GIS region ID.
A place 2GIS does not know stops the run during its start, before any place is read or charged, and the message gives the 5 closest region names.

#### Is the data safe to open in Excel or to show on a web page?

Names, descriptions and reviews are the businesses' and visitors' own words, copied as they are. Most phone numbers start with `+`, and a text can begin with `-`, `=` or `@`: Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. On a web page, escape every field like any text written by a stranger.

#### Known limitations

- 2GIS only lists places that are open for business: a closed place is not returned.
- A branch-list URL (`/branches/<id>`) gives the branches of the brand in that 2GIS region only, not in the other regions.
- A building page (`/geo/<id>`) lists the places inside that building.
- Filters of the 2GIS site that are not in the input can still be used by pasting a filtered search URL into `startUrls`.
- `onlyNew` remembers place IDs, not their content: a place whose rating or phone changed is not returned again.
- Two runs sharing the same `stateKey` at the same time may both return the same new place.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options).

On the platform, 300 places take about 50 seconds, start included: a one-hour run holds well over 10 000 places. Reviews and photos add pages to read for each place.

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the places already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the places it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the places of its last 2 seconds once more.

#### Something doesn't work?

The last line of the log counts the places saved, filtered out and no longer on 2GIS, and the requests that failed after every retry. Those requests are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause.

If 2GIS changes its data, you are told instead of paying for blank rows. If the first 20 places read all lack their name, organization ID, coordinates or categories, the run saves nothing more, stops and fails, and its last message names the missing field: at most those first places are charged. A run of fewer places (a scheduled `onlyNew` run that finds a few new ones, a small **Max places**) is checked at its end, from 5 places: its places are already saved, and the run fails with the missing field instead of ending green.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

## `startUrls` (type: `array`):

2GIS URLs on any 2GIS domain: search results (`https://2gis.ru/moscow/search/кафе`; a category or filters after the text are kept: `https://2gis.ru/moscow/search/кафе/rubricId/161`), place pages (`https://2gis.ru/moscow/firm/70000001063551091`), lists of all the branches of a company (`https://2gis.ru/moscow/branches/4504136498310300`), area pages (`https://2gis.ae/dubai/geo/13933647002593361`) or short links (`https://go.2gis.com/abc123`). When this list or **Place IDs** is not empty, the search fields below (keywords, locations, map area) are ignored; filters, caps, reviews and monitoring still apply. Paste the URLs themselves: a link to a text file of URLs is not read. Max 1 000 URLs.

## `placeIds` (type: `array`):

2GIS place (branch) IDs: the number after `/firm/` in a place URL, e.g. `70000001063551091`. Added to **Start URLs**: the search fields below are then ignored. Max 500.

## `query` (type: `string`):

What to look for, as typed on 2GIS, in any language (e.g. `cafe`, `кафе`, `dentist`). Leave empty and give **Category IDs** to list a whole category.

## `searchQueries` (type: `array`):

Several keywords in one run: one search per keyword (times each location below). Added to **Search keyword**; places found by several searches are saved once.

## `location` (type: `string`):

City or region covered by 2GIS, by name in Russian or English (e.g. `Москва`, `Dubai`, `Almaty`), by URL slug (`moscow`) or by 2GIS region ID (`32`). Empty = the map area below.

## `locations` (type: `array`):

Several locations in one run: every keyword is searched in every location (3 keywords × 4 locations = 12 searches, max 500). A country counts one search per region. Added to **Location**.

## `rubricIds` (type: `array`):

Only places of these 2GIS categories (rubric IDs, e.g. `161` = cafés; the IDs of each place's categories are in the `rubrics` output field). With no keyword, the whole category is listed in the location.

## `coordinates` (type: `string`):

Search around this point instead of a named location: `latitude,longitude` in decimal degrees, latitude FIRST (e.g. `55.7558,37.6173` = Moscow center). Used with **Radius**. When a location is also given, the area wins and the location only picks the 2GIS region and language.

## `radiusKm` (type: `number`):

Search radius around **Map center**, from 0.05 to 40 km (e.g. `5` = places within 5 km). Default 5.

## `boundingBox` (type: `string`):

Search this rectangle: `south,west,north,east` in decimal degrees (e.g. `55.57,37.35,55.92,37.85` = Moscow inside the ring road). Wins over Map center. When a location is also given, the area wins and the location only picks the 2GIS region and language.

## `customGeolocation` (type: `object`):

Search inside a drawn area: a GeoJSON `Polygon` or `MultiPolygon` (coordinates as `[longitude, latitude]`, as in GeoJSON). Wins over Map rectangle and Map center. When a location is also given, the area wins and the location only picks the 2GIS region and language.

## `maxItems` (type: `integer`):

Maximum number of places to save for the whole run (after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (keyword × location, or search URL), so that the first search cannot use up the whole **Max places** budget. 0 = no per-search cap.

## `language` (type: `string`):

Language of names, addresses, categories, opening-hours comments and attribute labels, as a 2GIS locale (language_COUNTRY, e.g. `en_AE`). Automatic = the main language of the region of each search (the first one it offers among the languages of the 2GIS regions: Russian in Moscow, Azerbaijani in Baku, English in Dubai). A language the region of a search does not offer is replaced by the main language of the region, with a warning in the log. Review texts stay as written.

## `sortBy` (type: `string`):

Order of the 2GIS results. Relevance is the 2GIS default. Distance needs **Map center** (or a Start URL with a point).

## `includeContacts` (type: `boolean`):

Add phones, e-mails, messengers (WhatsApp, Telegram, Viber) and social networks of each place. Charged as a separate event, see Pricing. Off = those fields are null; the website and the `hasPhone` / `hasEmail` / `hasWebsite` flags are always filled.

## `minRating` (type: `number`):

Only places rated at least this (0-5, e.g. `4.5`). Places without a rating are dropped. 0 = no minimum.

## `minReviewCount` (type: `integer`):

Drop the places with fewer reviews than this. 0 = no minimum.

## `hasWebsite` (type: `string`):

Keep all places, only the places with a website (applied by 2GIS, no extra cost), or only the places without one (checked by the Actor: each place dropped is charged as a Filter check event).

## `hasPhotos` (type: `boolean`):

Only places that have photos (2GIS filter).

## `hasReviews` (type: `boolean`):

Only places that have reviews (2GIS filter).

## `openNow` (type: `boolean`):

Only places open at the time of the run (2GIS filter).

## `open24h` (type: `boolean`):

Only places open around the clock (2GIS filter).

## `requirePhone` (type: `boolean`):

Drop the places without a phone number. Needs **Include contacts**.

## `requireEmail` (type: `boolean`):

Drop the places without an e-mail address. Needs **Include contacts**.

## `requireWebsite` (type: `boolean`):

Drop the places without a website.

## `attributeTags` (type: `array`):

Only places that have all these 2GIS attributes, as tags (e.g. `food_service_food_vegan`, `wifi`). The tag of each attribute of a place is in the `attributes` output field. 2GIS filter.

## `metroStationIds` (type: `array`):

2GIS IDs of metro stations (digits, e.g. `4504385606385704`): filters places near any of these stations. 2GIS filter.

## `avgPriceMin` (type: `integer`):

Only places whose average bill is at least this, in the local currency (restaurants, cafés and other food places). 0 = no minimum.

## `avgPriceMax` (type: `integer`):

Only places whose average bill is at most this, in the local currency (restaurants, cafés and other food places). 0 = no maximum.

## `excludeKeywords` (type: `array`):

Drop the places whose name, category or address contains one of these words (case and accents ignored). A city name drops every place of that city.

## `excludePlaceIds` (type: `array`):

Never save these places (e.g. your own branches, or places you already have).

## `skipAds` (type: `boolean`):

Drop the places 2GIS shows as promoted (advertising).

## `includeReviews` (type: `boolean`):

Add the reviews of each place (50 per request) from 2GIS, Flamp and Booking, with author, rating, date, text, likes, photos and the owner's answer. Charged per review as a separate event, see Pricing.

## `maxReviewsPerPlace` (type: `integer`):

Maximum number of reviews per place. 0 = all, up to about 6,000 per place (a row is capped at 6 MB; the run log says when).

## `reviewSort` (type: `string`):

Order in which reviews are read (and cut by Max reviews per place).

## `postedAfter` (type: `string`):

Only reviews written on or after this date: `2026-09-01`, or a period before now such as `7 days`, `2 weeks`, `1 month` (API: `24 hours` and full ISO date-times work too). Places are never dropped by this filter.

## `postedBefore` (type: `string`):

Only reviews written on or before this date (the whole day is included), or older than a period such as `30 days`.

## `minReviewRating` (type: `integer`):

Only reviews rated at least this many stars (1-5). 0 = all.

## `maxReviewRating` (type: `integer`):

Only reviews rated at most this many stars (e.g. `2` = complaints only). 0 = all.

## `onlyWithText` (type: `boolean`):

Drop the ratings without any text.

## `onlyWithBusinessReply` (type: `boolean`):

Keep only the reviews the business answered.

## `reviewKeywords` (type: `array`):

Keep only the reviews whose text contains one of these words (case and accents ignored).

## `reviewSources` (type: `array`):

Sites the reviews come from. Empty = all of them.

## `includeUnratedReviews` (type: `boolean`):

2GIS reviews that carry only text or a comment, with no stars. Off = only rated reviews.

## `includePhotos` (type: `boolean`):

Add the photo gallery of each place (full-size URL, size, description), up to **Max photos per place**. Charged as a separate event, see Pricing. The cover photo and the photo count are always in the output.

## `maxPhotos` (type: `integer`):

Maximum number of photos per place. 0 = all.

## `photoAlbums` (type: `array`):

Albums read when **Include photos** is on: the photos posted for the place, the photos of its building, or its facilities. Empty = the place photos only.

## `onlyNew` (type: `boolean`):

Skip the places that a previous run (same **Memory key**) already delivered: they are not saved and not charged. First run = everything is new.

## `stateKey` (type: `string`):

Name of the memory used by **Only new places**. Give each schedule / task its own key (e.g. `moscow-cafes`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) everything again. Untick it afterwards.

## `proxyConfiguration` (type: `object`):

Apify Proxy or your own proxies. Keep the default: it is included in the price. The residential Apify proxy is not available in this Actor.

## `maxConcurrency` (type: `integer`):

Maximum number of requests processed in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Most requests to the site in any 60 seconds, all of them counted: results pages, listing pages and the extra page read for a listing. A budget, not an even pace: up to this many can leave at once when the minute starts (spread them with the minimum delay below). Lower it if the site answers HTTP 429 / 403 in the log.

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests, in milliseconds. Unlike the per-minute rate, this spreads the requests evenly instead of letting them go out in a burst. 0 = no gap.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed. Behind a proxy, a request the site turns away is also retried on a new proxy session up to 10 times without using up these retries.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [],
  "placeIds": [],
  "query": "кафе",
  "searchQueries": [],
  "location": "Москва",
  "locations": [],
  "rubricIds": [],
  "radiusKm": 5,
  "maxItems": 20,
  "maxItemsPerQuery": 0,
  "language": "auto",
  "sortBy": "relevance",
  "includeContacts": false,
  "minRating": 0,
  "minReviewCount": 0,
  "hasWebsite": "any",
  "hasPhotos": false,
  "hasReviews": false,
  "openNow": false,
  "open24h": false,
  "requirePhone": false,
  "requireEmail": false,
  "requireWebsite": false,
  "attributeTags": [],
  "metroStationIds": [],
  "avgPriceMin": 0,
  "avgPriceMax": 0,
  "excludeKeywords": [],
  "excludePlaceIds": [],
  "skipAds": false,
  "includeReviews": false,
  "maxReviewsPerPlace": 20,
  "reviewSort": "date_created",
  "minReviewRating": 0,
  "maxReviewRating": 0,
  "onlyWithText": false,
  "onlyWithBusinessReply": false,
  "reviewKeywords": [],
  "reviewSources": [],
  "includeUnratedReviews": false,
  "includePhotos": false,
  "maxPhotos": 20,
  "photoAlbums": [
    "common"
  ],
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 8,
  "maxRequestsPerMinute": 120,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "кафе",
    "location": "Москва",
    "maxItems": 20,
    "includeContacts": false,
    "includeReviews": false,
    "includePhotos": false,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/2gis-places-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "query": "кафе",
    "location": "Москва",
    "maxItems": 20,
    "includeContacts": False,
    "includeReviews": False,
    "includePhotos": False,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/2gis-places-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "кафе",
  "location": "Москва",
  "maxItems": 20,
  "includeContacts": false,
  "includeReviews": false,
  "includePhotos": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/2gis-places-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/2gis-places-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/6GtHdDot6kdbNckoL/builds/qicx0ZH6ty3wuJpEY/openapi.json
