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

🏢 2GIS Places Scraper extracts structured business data from 2GIS places — names, addresses, phones, ratings & more. 🚀 Fast, reliable scraping for lead gen, market research, and local SEO insights. 🔎📊

- **URL**: https://apify.com/scraper-engine/2gis-places-scraper.md
- **Developed by:** [Scraper Engine](https://apify.com/scraper-engine) (community)
- **Categories:** Lead generation, SEO tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### 2GIS Places Scraper — Addresses, Contacts and Ratings

2GIS Places Scraper searches 2GIS — the leading maps and business directory across Russia, the CIS, Central Asia and the Middle East — and returns every matching place as structured JSON: name, address, GPS coordinates, rubrics/category, rating and review counts, opening hours, and (opt-in) phone/email/website/social contacts, reviews and photos. No parsing, no HTML, no 2GIS account. Give it a search term and a city, hit **Run**, and get a ready-to-use dataset.

### What is 2GIS Places Scraper?

2GIS Places Scraper is an Apify Actor that runs real search queries against 2GIS's live site (e.g. "ресторан" in "Москва") and extracts a normalized record for every business that comes back, across 19 country domains and 13 interface languages. It does not require a 2GIS account, login, or API key — it reads the same public pages a site visitor sees, through a headless Chromium browser with automatic proxy escalation. It's built for lead-generation teams, local-market researchers, directory builders and developers who need 2GIS business data as clean JSON instead of scraping HTML by hand.

### What 2GIS place data is publicly available to scrape?

Everything a visitor sees on a 2GIS place card and search results page — name, category, address, coordinates, rating, reviews and listed contact details — is visible without signing in. What's gated is content 2GIS only serves inside its own authenticated app session or to the business owner managing the listing.

| Data Category | Publicly Available | Restricted |
| --- | --- | --- |
| Name, rubrics/category, brand info | Yes | — |
| Address, postcode, GPS coordinates | Yes | — |
| Rating score, review count | Yes | — |
| Review text, ratings, dates, business's official replies | Yes | — |
| Phone, email, website, social links shown on the card | Yes | — |
| Opening hours | Yes | — |
| Photo album cover image + total photo count | Yes | — |
| Every individual photo inside a gallery beyond each album's cover | — | Authenticated 2GIS app session only |
| Owner analytics / lead-management dashboard | — | Business-owner login |

2GIS Places Scraper only returns publicly visible data — what any visitor sees. Nothing behind a login wall.

### What data can I extract with 2GIS Places Scraper?

Each place record combines identity and location fields with rating, contact, media and review data, matching the exact JSON keys the Actor pushes to the dataset — not just the default table view.

#### 📍 Identity, address and category fields

| Field Name | Description |
| --- | --- |
| `id` | 2GIS branch/firm numeric ID |
| `title` | Place name |
| `shortName` | Short display name |
| `extension` | Legal-entity suffix (e.g. "ООО") |
| `url` | Canonical 2GIS place page URL |
| `rubrics` | Array of 2GIS category names assigned to the place |
| `category` | Raw `poi_category` value from 2GIS, when present |
| `summary` | Best available description — ad tagline, full description, description or annotation, in that priority order |
| `postcode`, `country`, `region`, `city`, `district` | Address components |
| `street`, `houseNumber` | Parsed street and house number |
| `address` | Full formatted address string |
| `location` | `{ lat, lng }` — real scraped coordinates |
| `working_hours` | Per weekday (`Mon`–`Sun`), each `{ from, to }` for the first schedule interval of the day |
| `nearestStations` | Array of `{ name, distance, comment }` — nearby metro/transport stops |
| `attributeGroups` | Flat list of listed amenities/attributes (e.g. "Wi-Fi", card payment) |
| `brand` | `{ name, shortName, extension, branchCount, rating: { score, reviews } }` — chain/brand-level info |
| `detailsType` | 2GIS's own entity type for the record (always `"branch"` — non-business entities are filtered out before this point) |
| `mainPhotoUrl` | Cover photo URL of the primary photo album |

#### ⭐ Ratings, contacts and review fields

| Field Name | Description |
| --- | --- |
| `totalScore` | Place's overall rating |
| `reviewsCount` | Total review count |
| `ratingCount` | Number of reviews that carry a star rating |
| `phoneText`, `phoneValue` | Phone numbers as displayed / as raw dialable values — only when `includeContacts: true` |
| `email`, `website` | Contact email(s) and website(s) — only when `includeContacts: true` |
| `socials` | `{ vk, telegram, whatsapp }`, each an array — only when `includeContacts: true` |
| `mediaCount` | 2GIS's real total photo count across all albums — only when `maxMediaPerPlace > 0` |
| `media` | Array of `{ url, count }`, one entry per photo album cover, capped at `maxMediaPerPlace` |
| `reviewsMeta` | `{ branchId, branchRating, branchReviewsCount, totalCount, ratedCount, unratedCount, fetchedCount, providers, sortBy, ratings, locale, sentimentFilter }` — present only when `maxReviewsPerPlace > 0` |
| `reviews` | Array of review objects (see below) — present only when `maxReviewsPerPlace > 0` |

Each entry in `reviews[]` carries `id`, `provider`, `rating`, `text`, `dateCreated`, `dateEdited`, `isRated`, `likesCount`, `commentsCount`, `officialAnswer` (`{ id, dateCreated, orgName, text, logoUrl }` or `null`), `emojis`, `trustFactors`, and — only when `includeReviewerData: true` — a `user` object with `{ id, publicId, name, reviewsCount, photoUrl }`.

#### 🕓 Run metadata fields

| Field Name | Description |
| --- | --- |
| `searchString` | The query term this row was found under |
| `searchUrl` | The 2GIS search URL used |
| `pageNumber` | Search-result page the place was discovered on |
| `scrapedAt` | ISO-8601 UTC timestamp of extraction |

#### 🤖 Add-on: Need additional 2GIS data?

Reviews, reviewer profiles and photo albums are already built into this Actor as opt-in toggles (`maxReviewsPerPlace`, `includeReviewerData`, `maxMediaPerPlace`) rather than separate scrapers — turn them on in one run instead of chaining tools. If you're also tracking the same locations on other platforms, pair this with [Google Maps Reviews Scraper](https://apify.com/scraper-engine/google-maps-reviews-scraper) or [Facebook Reviews Scraper](https://apify.com/scraper-engine/facebook-reviews-scraper) to cross-reference reputation across map providers.

### How does 2GIS Places Scraper differ from the official 2GIS API?

2GIS publishes an official [Places API](https://docs.2gis.com/en/api/search/places/overview) that returns only `id`, `name` and `type` by default — every additional field (address, contacts, hours, structure info) is opted into via a `fields` parameter, and some of it requires sales-team approval. Access starts with a demo key from 2GIS's Platform Manager, capped at 1,000 requests per service for one month; production use means purchasing a subscription whose price is negotiated directly with a 2GIS account manager, not published.

| Feature | 2GIS Places API | 2GIS Places Scraper |
| --- | --- | --- |
| Access | API key from Platform Manager, demo or paid subscription | Runs on Apify — no 2GIS account or key |
| Default response | `id`, `name`, `type` only | Full place record (identity, address, rating, hours) by default |
| Contacts, reviews, photos | Some fields gated behind sales approval | Toggled by input parameters (`includeContacts`, `maxReviewsPerPlace`, `maxMediaPerPlace`) |
| Usage limits | Demo key: 1,000 requests/service/month; paid tiers by subscription | Bound by the Actor's own crawl of `maxItems` per search term |
| Pricing | Negotiated with a 2GIS account manager | Apify's platform pricing for this Actor |
| Setup time | Sign up, create a key, negotiate a subscription | Run the Actor with a JSON input |

Use the official API when you need a direct, long-term commercial data relationship with 2GIS. Use 2GIS Places Scraper when you want structured place data immediately, without a 2GIS account or a sales conversation.

### How to use 2GIS Places Scraper

2GIS Places Scraper runs on the Apify platform — no separate signup with 2GIS.

1. Open the Actor's page in [Apify Store](https://apify.com/store) and click **Try for free** (or **Run**, if you already have an Apify account).
2. Provide `query` — one or more search phrases, exactly as you'd type them into the 2GIS search box (this is the only required input).
3. Set `locationQuery` for the city/area, and optionally narrow results with `rubricIds`, the `filterHas*`/`filterCreatedRecently`/`filterWifi` toggles, `filterAvgPriceMin`/`filterAvgPriceMax`, or `filterRating`.
4. Click **Start** to launch the run.
5. Download results from the **Output** tab as JSON, CSV, Excel, HTML, XML or RSS, or stream them via the Apify API.

#### How to scale to bulk place extraction

`query` accepts an array, so one run can search several terms — each term's results are collected and saved as its own batch once that term finishes. `maxItems` applies **per search term**, not per run, so `["ресторан", "кафе", "бар"]` with `maxItems: 50` can return up to 150 places in a single run. There's no separate URL-list input — this Actor works from search terms and filters, not a pre-built list of place links.

### What can you do with 2GIS place data?

- A **lead-generation agency** building call lists for a city uses `phoneText` and `website` to reach every business in a rubric that filters on `filterHasSite: true`.
- A **local SEO consultant** benchmarking a niche uses `totalScore` and `reviewsCount` across every competitor returned by one `query` to size the competitive field.
- A **franchise analyst** mapping a chain's footprint uses `brand.branchCount` and `brand.rating.score` to compare a brand's locations city by city.
- A **reputation manager** uses `reviews[].rating` alongside `reviews[].officialAnswer` to check which negative reviews a business has (or hasn't) responded to.
- An **AI agent or RAG pipeline** indexes `summary`, `rubrics`, `address` and `reviews[].text` as retrievable context for a local-business chatbot or lead-qualification agent, with `location.lat`/`location.lng` for distance-based ranking.

### How does 2GIS Places Scraper handle rate limits and blocking?

Requests start **direct** (no proxy). On a hard failure, or a confirmed empty result on page 1, the search phase escalates to a **datacenter** proxy and then a **residential** proxy, sticking with residential once it succeeds. A homepage visit runs before each search/detail browser context to establish a normal-looking session, since 2GIS's anti-bot layer reacts to a cold, referrer-less deep link into `/search`. The detail-page pool (which loads individual place pages) has its own, independent escalation ladder — if every place in a batch fails on the tier that worked for search, that pool is rebuilt on a fresh context at the next proxy tier and retried, up to twice. Each place page also gets up to 3 retry attempts before being given up on.

⚠️ 2GIS runs an active countermeasure against automated traffic. This Actor gets through it under typical conditions, but on a heavily-loaded IP range some individual place pages within a run can still be lost even after escalation — those are logged and simply skipped, never fabricated.

### ⬇️ Input

`query` is the only required field. Everything else narrows or enriches the search.

| Parameter | Required | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `query` | Yes | array of strings | — | One or more search phrases, like in the 2GIS search box (e.g. *ресторан*, *стоматология*) |
| `locationQuery` | No | string | `"Москва"` | City, district, street or landmark. Ignored if `customGeolocation` is set |
| `maxItems` | No | integer | `10` | Max places **per search term**. `0` tries to take all available (internally capped at 500 per term) |
| `domain` | No | string (enum, 20 values incl. `auto`) | `"auto"` | Which national 2GIS site to use — `2gis.ru`, `2gis.kz`, `2gis.ae`, `2gis.uz` and 15 more; `auto` defaults to Russia |
| `language` | No | string (enum, 14 values incl. `auto`) | `"auto"` | Preferred interface language for the scraped page |
| `includeContacts` | No | boolean | `false` | Also load phones, email, website and socials when shown on the card |
| `maxReviewsPerPlace` | No | integer (0–99999) | `0` | Max reviews per place. `0` skips reviews entirely (faster) |
| `reviewSortBy` | No | string (enum: `trust`, `date_edited`) | `"trust"` | How fetched reviews are ordered |
| `reviewRatings` | No | string (enum: `all`, `negative`, `positive`) | `"all"` | Keep only reviews of the chosen sentiment (negative ≤2, positive ≥4) |
| `includeUnratedReviews` | No | boolean | `false` | Include text-only reviews with no star rating |
| `includeReviewerData` | No | boolean | `false` | Store the reviewer's public profile fields (name, avatar). Personal data — collect only with a lawful basis |
| `maxMediaPerPlace` | No | integer (0–99999) | `0` | When `>0`, adds `mediaCount` and up to this many photo-album cover URLs per place |
| `rubricIds` | No | array of strings (select, 1,706 categories) | `[]` | Narrow results to selected 2GIS category codes |
| `filterHasGoods` | No | boolean | `false` | Only places with a priced menu/catalogue |
| `filterHomeDelivery` | No | boolean | `false` | Only venues offering delivery |
| `filterTakeaway` | No | boolean | `false` | Only places offering takeaway |
| `filterHasSite` | No | boolean | `false` | Only listings with a website |
| `filterHasPhotos` | No | boolean | `false` | Only places with photos |
| `filterPaymentCard` | No | boolean | `false` | Only places accepting card payment |
| `filterAvgPriceMin` | No | integer | — | Minimum average bill (food-service places) |
| `filterAvgPriceMax` | No | integer | — | Maximum average bill (food-service places) |
| `filterCreatedRecently` | No | boolean | `false` | Only recently-added places |
| `filterWifi` | No | boolean | `false` | Only places with Wi-Fi |
| `filterRating` | No | string (enum, 5 star-tier tokens) | — | Keep places at or above the selected rating tier |
| `sortBy` | No | string (enum: `rating`, `opened_time`, `name`) | `"rating"` | How saved records are ordered, applied Actor-side to the already-scraped data (2GIS's site ignores a sort URL parameter). Applied in windows of 15 places as details finish and are pushed to Output — not one global sort held back until the whole run completes — so large runs (`maxItems` in the dozens/hundreds) show real progress throughout instead of one silent wait; re-sort the exported dataset by `totalScore`/`title` yourself if a single strict global order matters more than incremental output. `opened_time` keeps the original crawl order — 2GIS does not publish a place's opening date |
| `customGeolocation` | No | object (GeoJSON Polygon/MultiPolygon) | — | After scraping, keep only places whose real coordinates fall inside this shape. Overrides `locationQuery` as the geographic filter |
| `enableGlobalDataset` | No | boolean | `false` | Remember place IDs saved in previous runs of this Actor (persisted store) and skip them instead of re-saving duplicates |

#### Example input

```json
{
  "query": ["стоматология", "автомойка"],
  "locationQuery": "Новосибирск",
  "maxItems": 40,
  "domain": "auto",
  "language": "auto",
  "includeContacts": true,
  "maxReviewsPerPlace": 25,
  "reviewSortBy": "trust",
  "reviewRatings": "negative",
  "includeUnratedReviews": false,
  "includeReviewerData": false,
  "maxMediaPerPlace": 2,
  "rubricIds": [],
  "filterHasSite": true,
  "filterPaymentCard": true,
  "sortBy": "rating",
  "enableGlobalDataset": false
}
```

### ⬆️ Output

Every place is pushed to the Actor's dataset as one typed JSON object, with a consistent field set across runs. Download it from the **Output** tab as JSON, CSV, Excel, HTML, XML or RSS, or pull it with the Apify API/SDK.

#### Example output

```json
{
  "id": "70000001007179642",
  "title": "Example Café",
  "shortName": "Example",
  "extension": null,
  "url": "https://2gis.ru/moscow/firm/70000001007179642",
  "rubrics": ["Кафе"],
  "category": null,
  "totalScore": 4.6,
  "reviewsCount": 312,
  "ratingCount": 198,
  "summary": "Кофе, завтраки, веранда",
  "postcode": "125009",
  "country": "Россия",
  "region": "Москва",
  "city": "Москва",
  "district": "Тверской",
  "street": "Тверская",
  "houseNumber": "7",
  "address": "Тверская, 7",
  "mainPhotoUrl": "https://i5.photo.2gis.com/main/branch/32/70000001007179642/common",
  "mediaCount": 180,
  "media": [
    { "url": "https://i5.photo.2gis.com/main/branch/32/70000001007179642/common", "count": 167 },
    { "url": "https://i6.photo.2gis.com/main/geo/32/70000001007179642/view", "count": 13 }
  ],
  "location": { "lat": 55.7654, "lng": 37.6055 },
  "working_hours": { "Mon": { "from": "08:00", "to": "22:00" } },
  "nearestStations": [{ "name": "Тверская", "distance": 210, "comment": "5 мин пешком" }],
  "attributeGroups": ["Wi-Fi", "Принимают карты"],
  "brand": {
    "name": "Example",
    "shortName": "Example",
    "extension": null,
    "branchCount": 4,
    "rating": { "score": 4.6, "reviews": 312 }
  },
  "detailsType": "branch",
  "searchString": "кафе",
  "searchUrl": "https://2gis.ru/search/%D0%BA%D0%B0%D1%84%D0%B5?m=37.385264%2C55.581699%2F9",
  "pageNumber": 1,
  "scrapedAt": "2026-08-12T10:00:00Z",
  "phoneText": ["+7 495 123-45-67"],
  "phoneValue": ["+74951234567"],
  "email": [],
  "website": ["http://example-cafe.ru/"],
  "socials": { "vk": ["example_cafe"] },
  "reviewsMeta": {
    "branchId": "70000001007179642",
    "branchRating": 4.6,
    "branchReviewsCount": 312,
    "totalCount": 312,
    "ratedCount": 8,
    "unratedCount": 0,
    "fetchedCount": 8,
    "providers": [{ "tag": "2gis", "isReviewable": true }],
    "sortBy": "trust",
    "ratings": "rated_only",
    "locale": "ru_RU",
    "sentimentFilter": "negative"
  },
  "reviews": [
    {
      "id": "600123456789",
      "provider": "2gis",
      "rating": 2,
      "text": "Долго ждали заказ",
      "dateCreated": "2026-06-01T10:00:00Z",
      "dateEdited": null,
      "isRated": true,
      "likesCount": 1,
      "commentsCount": 0,
      "officialAnswer": null,
      "emojis": null,
      "trustFactors": null
    }
  ]
}
```

`phoneText`, `phoneValue`, `email`, `website`, `socials` appear only when `includeContacts` is `true`. `mediaCount`/`media` appear only when `maxMediaPerPlace > 0`. `reviewsMeta`/`reviews` appear only when `maxReviewsPerPlace > 0` and a review API key was found for that place.

### How does it work?

2GIS Places Scraper drives a real Chromium browser (Playwright) to each 2GIS search results page and place page, the same pages a visitor's browser would load. It reads the page's own client-side state (`window.initialState`) plus DOM and HTML fallbacks to collect place IDs, then opens each place page to pull the full record. A homepage visit warms up every new browser session before the real request, and connections escalate from direct to datacenter to residential proxy tiers when 2GIS's anti-bot layer pushes back. Only data visible on 2GIS's own public pages is returned — nothing behind a login. The output schema (the JSON keys documented above) stays the same run over run, even if 2GIS changes its page layout.

### Integrations

2GIS Places Scraper runs on Apify, so it works with anything that can call the Apify API or connect to Apify's MCP server.

#### Calling 2GIS Places Scraper programmatically

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_API_TOKEN>")

run = client.actor("scraper-engine/2gis-places-scraper").call(run_input={
    "query": ["ресторан"],
    "locationQuery": "Москва",
    "maxItems": 20,
})

for place in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(place["title"], place["address"])
```

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request, using an `Authorization: Bearer <APIFY_API_TOKEN>` header against the Apify API.

#### MCP integration for AI agents

2GIS Places Scraper is reachable through [Apify's MCP server](https://mcp.apify.com) — add it as a tool from the server's configurator (search for the Actor by name) and it becomes callable from any MCP client, including Claude Desktop, Cursor and Claude Code. Authenticate with an Apify API token or OAuth; no separate hosting needed.

#### No-code tools (n8n, Make, LangChain)

In **n8n**, use the HTTP Request node pointed at this Actor's run endpoint (or the community Apify node) to trigger a run and fetch the dataset. In **Make**, the Apify app module lets you start a run and iterate results as part of a scenario. In **LangChain**, use `ApifyDatasetLoader` (or the Apify integration wrapper) to load a finished run's dataset directly into a document pipeline.

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

Scraping publicly available business listing data — the kind 2GIS Places Scraper returns by default — is generally permitted, since it does not bypass a login or access control. 2GIS Places Scraper only returns data visible on public 2GIS pages. Because the default output is business/place data, not personal data, no personal-data regime (like GDPR) applies to a standard run; the relevant framework instead is 2GIS's own Terms of Service and applicable database-rights law, which you should review for your specific use.

⚠️ The one opt-in exception is `includeReviewerData`: turning it on stores a reviewer's public profile fields (name, avatar), which is personal data and does fall under GDPR/CCPA-style regimes — collect it only where you have a lawful basis. Consult legal counsel if your use case involves bulk storage of personal data.

### Frequently asked questions

#### What 2GIS place fields does 2GIS Places Scraper return?

The top fields are `title`, `address`, `location` (GPS), `totalScore`/`reviewsCount`, and — when enabled — `phoneText`/`website` and `reviews[]`. See [What data can I extract](#what-data-can-i-extract-with-2gis-places-scraper) above for the full field list.

#### Does 2GIS Places Scraper require a 2GIS account or login?

No. It scrapes public 2GIS pages through a browser session, with no 2GIS account, password or API key needed on your side.

#### How many places can I extract in one run?

`maxItems` applies per search term, defaulting to 10; setting it to `0` tries for all available results, internally capped at 500 per term. Since `query` accepts multiple terms, one run can return up to `maxItems × number of terms` places.

#### What happens if a search query returns zero results?

If a search term returns nothing on page 1, the Actor treats it as a possible soft block and escalates the proxy tier (up to twice) before accepting a genuine zero-result outcome and moving to the next query term — no fabricated rows are added either way.

#### Can I scrape multiple 2GIS places at once?

Yes — `query` is an array, so one run processes several search terms, each saved as its own batch of results once that term's scrape finishes.

#### Does 2GIS Places Scraper work with Claude, ChatGPT and other AI agent tools?

Yes. It's reachable through [Apify's MCP server](https://mcp.apify.com) for MCP-based clients like Claude Desktop and Claude Code, and callable as a plain HTTP endpoint by any agent framework via the Apify API.

#### How does 2GIS Places Scraper compare to other 2GIS place scrapers?

As observed on their Apify Store listings on 2026-08-16: one competitor returns 51 fields per place at $2.81/1,000 results with no review or geofencing support; another advertises 90+ fields and reviews/photos at tiered pay-per-event pricing starting at $4.50/1,000 places; a third uses a lightweight HTTP crawler (no browser) at $1.80/1,000 results with basic fields only. None of the three publicly document a results-level geofence or cross-run deduplication — both of which 2GIS Places Scraper provides via `customGeolocation` and `enableGlobalDataset`.

#### Does 2GIS Places Scraper return data in a format LLMs can use directly?

Yes. Output is typed, normalized JSON with consistent field names across runs — no HTML parsing or CSS selectors needed. Pass it directly to an LLM, index it into a vector store, or feed it to an agent tool.

#### What happens when 2GIS changes its layout or anti-bot system?

The Actor is maintained and the output schema is designed to stay stable across 2GIS site changes — field names and structure don't change just because 2GIS updates its page markup.

#### Can I use 2GIS Places Scraper without managing proxies or browser infrastructure?

Yes. The Actor runs its own headless Chromium browser and handles proxy tier selection (direct, datacenter, residential) and escalation automatically — you don't configure or maintain any of that yourself.

#### Which 2GIS place fields work best for AI training data and RAG indexing?

For RAG, index `summary`, `rubrics`, `address` and `reviews[].text` — the highest-information text fields. For training data, `id`, `totalScore`, `reviewsCount` and `rubrics` have the most consistent structure across every record, and all fields return as typed primitives (strings, numbers, booleans, arrays) rather than free-form HTML.

### Related scrapers

| Scraper | What it extracts |
| --- | --- |
| [Google Maps Reviews Scraper](https://apify.com/scraper-engine/google-maps-reviews-scraper) | Reviews and ratings for Google Maps places |
| [Facebook Reviews Scraper](https://apify.com/scraper-engine/facebook-reviews-scraper) | Reviews and ratings for Facebook Pages |

### Your feedback

Found a bug or a missing field? Open an issue on this Actor's **Issues** tab in Apify Console, or use the **Contact developer** button on the Store listing — reports like these are how the Actor stays accurate as 2GIS changes.

# Actor input Schema

## `query` (type: `array`):

🇷🇺 Одна или несколько фраз, как в поиске на сайте 2GIS (например: *ресторан*, *стоматология*, *автомойка*). Разные формулировки дают лучшее покрытие.
🇬🇧 One or more phrases, like in the 2GIS search box (e.g. *restaurant*, *dentist*, *car wash*). Varied wording improves coverage.

## `locationQuery` (type: `string`):

🇷🇺 Где искать: город, район, улица или ориентир (например: *Москва*, *Нижний Новгород*).
🇬🇧 Where to search: city, district, street or landmark (e.g. *Moscow*, *Almaty*).

🇷🇺 Если задана пользовательская область ниже — она важнее этого поля.
🇬🇧 If **Custom area (GeoJSON)** is set below, it overrides this field.

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

🇷🇺 Максимум карточек на **каждый** поисковый запрос. **0** — попытаться взять все доступные (ограничено сайтом).
🇬🇧 Maximum listings **per search term**. **0** — try to take all available (still limited by the site).

## `domain` (type: `string`):

🇷🇺 Карта какой страны использовать (**Авто** — по умолчанию Россия / 2gis.ru).
🇬🇧 Which national 2GIS site to use (**Auto** defaults to Russia / 2gis.ru).

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

🇷🇺 Предпочтительный язык страницы и заголовков (**Авто** — язык по умолчанию для выбранного домена).
🇬🇧 Preferred page language (**Auto** — default for the selected domain).

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

🇷🇺 Дополнительно подгружает телефоны, e-mail, сайт и соцсети, если они указаны в карточке.
🇬🇧 Also loads phones, email, website and socials when the listing shows them.

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

🇷🇺 Максимум отзывов на одну организацию. **0** — не собирать отзывы (быстрее).
🇬🇧 Max reviews per place. **0** — skip reviews (faster run).

## `reviewSortBy` (type: `string`):

🇷🇺 Как упорядочивать отзывы при сборе.
🇬🇧 How reviews are ordered when fetched.

## `reviewRatings` (type: `string`):

🇷🇺 Оставлять только отзывы нужной тональности: все, только негативные (оценка ≤2) или только позитивные (оценка ≥4). Отзывы без числовой оценки не считаются ни негативными, ни позитивными и не попадут в фильтрованную выборку.
🇬🇧 Keep only reviews of the chosen sentiment: all, negative only (rating ≤2), or positive only (rating ≥4). Text-only reviews with no numeric rating cannot be classified and are excluded whenever a sentiment filter is active.

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

🇷🇺 Включать отзывы без числовой оценки, если они есть.
🇬🇧 Include text-only reviews without a star rating when present.

## `includeReviewerData` (type: `boolean`):

🇷🇺 Сохранять публичные поля профиля автора (имя, аватар и т.п.), если 2GIS их отдаёт.
🇬🇧 Store public reviewer fields (name, avatar, etc.) when exposed by 2GIS.

⚠️ Персональные данные регулируются GDPR и др. Собирайте только при законной цели.
⚠️ Personal data is regulated (e.g. GDPR). Collect only where you have a lawful basis.

## `maxMediaPerPlace` (type: `integer`):

🇷🇺 При значении **>0** в запись добавляются `mediaCount` (реальное общее число фото по данным 2GIS) и `media[]` — обложки каждого фотоальбома карточки (обычно 1-2: основной и, если есть, «панорама/улица»), максимум столько, сколько указано здесь. **0** — не собирать (быстрее). Полный постраничный перебор отдельных фото галереи 2GIS не отдаёт ни на одной бесключевой поверхности — доступны только обложки альбомов.
🇬🇧 When **>0**, adds `mediaCount` (the real total photo count 2GIS reports) and `media[]` — each photo album's own cover URL on the card (usually 1-2: the main album and, when present, a street-view album), capped at this number. **0** — skip (faster). Enumerating every individual in-gallery photo is not exposed by any keyless 2GIS surface — only album cover photos are obtainable.

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

🇷🇺 Сузить выдачу до выбранных **рубрик** (категорий) из справочника 2GIS. В данных сохраняются числовые коды; здесь — удобные названия.
🇬🇧 Narrow results to selected **2GIS categories**. Stored values are numeric IDs; labels here are for the form only.

## `filterHasGoods` (type: `boolean`):

🇷🇺 Только места, где в карточке есть раздел с ценами/меню (обычно общепит).
🇬🇧 Only places that expose prices or a menu in the listing.

## `filterHomeDelivery` (type: `boolean`):

🇷🇺 Только заведения с доставкой.
🇬🇧 Only venues that offer delivery.

## `filterTakeaway` (type: `boolean`):

🇷🇺 Только заведения с заказом навынос.
🇬🇧 Only places with takeaway / to-go.

## `filterHasSite` (type: `boolean`):

🇷🇺 Только организации с указанным сайтом.
🇬🇧 Only listings that include a website URL.

## `filterHasPhotos` (type: `boolean`):

🇷🇺 Только места с загруженными фотографиями.
🇬🇧 Only places that have photo gallery content.

## `filterPaymentCard` (type: `boolean`):

🇷🇺 Только места, где указана оплата банковской картой.
🇬🇧 Only where card payment is indicated.

## `filterAvgPriceMin` (type: `integer`):

🇷🇺 Нижняя граница среднего чека (для заведений, где он указан).
🇬🇧 Minimum average bill (where available).

## `filterAvgPriceMax` (type: `integer`):

🇷🇺 Верхняя граница среднего чека (для заведений, где он указан).
🇬🇧 Maximum average bill (where available).

## `filterCreatedRecently` (type: `boolean`):

🇷🇺 Показывать недавно появившиеся в городе точки.
🇬🇧 Prefer newly listed places in the area.

## `filterWifi` (type: `boolean`):

🇷🇺 Только места с отмеченным Wi‑Fi.
🇬🇧 Only listings that mention Wi‑Fi.

## `filterRating` (type: `string`):

🇷🇺 Оставить точки не ниже выбранного уровня звёзд (один вариант).
🇬🇧 Keep places at or above the selected star tier (pick one).

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

🇷🇺 Как упорядочить сохранённые записи. Применяется на стороне актора к уже собранным данным (реальный `rating` и `name` с карточек) — сайт 2GIS не поддерживает сортировку через параметр URL. **По дате открытия** не поддерживается: 2GIS не публикует дату открытия места ни на одной из бесключевых поверхностей — при этом варианте сохраняется исходный порядок обхода.
🇬🇧 How saved records are ordered. Applied Actor-side to the already-scraped data (real `rating`/`name` from each card) — 2GIS's own site does not support ordering via a URL parameter. **By opening date** is not supported: 2GIS does not publish a place's opening date on any keyless surface, so this option keeps the original crawl order instead of guessing a value.

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

🇷🇺 Вставьте **Polygon / MultiPolygon** в формате GeoJSON (также принимается Feature / FeatureCollection с такой геометрией). После сбора данных актор оставляет только места, чьи реальные координаты (`location.lat/lng`) попадают внутрь фигуры; остальные отбрасываются. Имеет приоритет над полем «Город» как географический фильтр результатов.
🇬🇧 Paste a **Polygon / MultiPolygon** GeoJSON (a Feature / FeatureCollection wrapping one also works). After scraping, the Actor keeps only places whose real coordinates (`location.lat/lng`) fall inside the shape and drops the rest — a results-level geofence on top of the city search above.

## `enableGlobalDataset` (type: `boolean`):

🇷🇺 Запоминать id уже сохранённых мест в постоянном хранилище этого актора и пропускать их в последующих запусках (не пересохранять повторно). Хранилище общее для всех запусков этого актора на аккаунте, не привязано к конкретному запуску.
🇬🇧 Remember previously-saved place ids in a store persisted by this Actor and skip them on later runs instead of re-saving duplicates. The store is shared across this Actor's runs on the account, not scoped to a single run.

## Actor input object example

```json
{
  "query": [
    "Ресторан"
  ],
  "locationQuery": "Москва",
  "maxItems": 10,
  "domain": "auto",
  "language": "auto",
  "includeContacts": false,
  "maxReviewsPerPlace": 0,
  "reviewSortBy": "trust",
  "reviewRatings": "all",
  "includeUnratedReviews": false,
  "includeReviewerData": false,
  "maxMediaPerPlace": 0,
  "rubricIds": [],
  "filterHasGoods": false,
  "filterHomeDelivery": false,
  "filterTakeaway": false,
  "filterHasSite": false,
  "filterHasPhotos": false,
  "filterPaymentCard": false,
  "filterCreatedRecently": false,
  "filterWifi": false,
  "sortBy": "rating",
  "enableGlobalDataset": false
}
```

# Actor output Schema

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

All scraped 2GIS places, one row per business, in the default dataset.

# 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": [
        "Ресторан"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scraper-engine/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": ["Ресторан"] }

# Run the Actor and wait for it to finish
run = client.actor("scraper-engine/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": [
    "Ресторан"
  ]
}' |
apify call scraper-engine/2gis-places-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scraper-engine/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/1cbdtDuFNNOy0ctas/builds/sUAnGcnH5XM8HwLvw/openapi.json
