# 2GIS Scraper (`mlg14/2gis-scraper`) Actor

Search public 2GIS places by keyword and city, or collect known place IDs. Export names, categories, ratings, addresses, coordinates, hours, photos, and optional contacts.

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

## Pricing

$1.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## 2GIS Scraper

Scrape 2GIS places by search term and city, then export 2GIS business data to JSON, CSV, or a spreadsheet. This 2GIS API alternative collects public place listings, ratings, addresses, coordinates, hours, category labels, and optional contacts, reviews, and media in one record per place.

Use it for local market research, directory maintenance, location analysis, and checking which businesses appear for a category in a city. You can search by several terms or supply known place IDs. The actor removes duplicate IDs within a run, keeps a single result limit across terms, and records the search term and result page that found each place.

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

Each dataset item represents one public place. Fields remain present when a listing has no value: missing text and numbers are null, while empty collections are arrays or objects. Review and media arrays are added only when their collection limits are greater than zero. The table below lists every output field.

| Field | Description | Example |
| --- | --- | --- |
| `id` | Stable public place ID | `70000001113359188` |
| `title` | Displayed place name | `Здрасте, кофейня` |
| `shortName` | Short name, when supplied | `Здрасте` |
| `extension` | Name extension or type | `кофейня` |
| `url` | Direct place page | `https://2gis.ru/moscow/firm/70000001113359188` |
| `rubrics` | Category names attached to the place | `Кофейни`, `Кафе` |
| `category` | Broad place category code | `cafe` |
| `totalScore` | Overall place rating | `4.8` |
| `reviewsCount` | Count of written reviews | `100` |
| `ratingCount` | Count of rated responses, including those without text | `114` |
| `summary` | Public summary where the place provides one | `null` |
| `country` | Country name | `Россия` |
| `region` | Region name | `Москва` |
| `city` | City name | `Москва` |
| `district` | District name | `Якиманка` |
| `street` | Street from address components | `Крымская набережная` |
| `houseNumber` | Building number from address components | `10 ст2` |
| `address` | Displayed street address | `Крымская набережная, 10 ст2` |
| `latitude` | Place latitude | `55.735952` |
| `longitude` | Place longitude | `37.605268` |
| `mainPhotoUrl` | Main photo URL when available | Public image URL |
| `photoCount` | Reported photo count | `58` |
| `workingHours` | Day names and opening intervals | `Mon: 10:00–22:00` |
| `nearestStations` | Nearby station names, distances, and comments | `Парк культуры`, 950 m |
| `attributeGroups` | Amenities, payment options, food, and service labels | `Wi-Fi для клиентов` |
| `brandName` | Parent organization name | `Здрасте, кофейня` |
| `branchCount` | Reported branches of the parent organization | `104` |
| `detailsType` | Source record type | `branch` |
| `updatedAt` | Listing update time, when supplied | `2026-09-10T04:28:08Z` |
| `sourceQuery` | Term that first found the place | `coffee` |
| `searchPage` | Numbered result page that first found it | `1` |
| `phoneText` | Displayed public phone numbers | `+7‒996‒657‒44‒89` |
| `phoneValue` | Normalized public phone numbers | `+79966574489` |
| `email` | Public contact email addresses | Empty or one or more addresses |
| `website` | Public website URLs | `http://zdrastecafe.ru` |
| `socials` | Public social links grouped by network | `telegram: [...]` |
| `reviews` | Optional recent reviews: ID, rating, text, dates, counts, reply | Up to your per-place limit |
| `media` | Optional photos or videos: ID, type, URL, dimensions, dates | Up to your per-place limit |

Contact fields are empty unless `includeContacts` is enabled, because search results usually do not contain complete contact groups. The main photo URL and photo count often appear in search results without further requests. The `media` collection makes separate requests for individual assets. Review records omit reviewer profiles and keep public review content and business replies only.

Ratings and review counts describe the place when the search ran. They can change as new reviews arrive. A rating count can exceed the written-review count because some customers leave a rating without text. `sourceQuery` reports the term that first produced a duplicate place, so the same place is saved once even when several terms match it.

### How to scrape 2GIS places

1. Enter one or more terms in `query`, such as `coffee`, `dentist`, or a local category name. Set `locationQuery` to the city you want to inspect.
2. Set `maxItems` to the number of places you need. For a small sample, 30–100 places is a practical start. Raise `maxPagesPerQuery` if you need more search pages.
3. Enable contacts, reviews, or media only for the detail needed in the final dataset. Those options add one or more requests per place.
4. Run the actor. Review the Places view, then download the dataset in your preferred format.

If you already have place IDs, put them in `organizationIds`. In that mode the actor opens those places directly and skips keyword search. You can also paste full place URLs into the same input. The city still matters because it forms the place URL, so set it to the city represented by the IDs.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `query` | String list | Prefilled with `coffee shop` | Search terms; multiple terms are deduplicated by place ID. |
| `locationQuery` | String | `Moscow` | City name or Latin city URL slug. |
| `domain` | Choice | Auto | Country domain for cities not recognized automatically. |
| `organizationIds` | String list | Empty | Place IDs or place URLs; skips keyword search when populated. |
| `maxItems` | Integer | `100` | Maximum places across the run; `0` removes this cap. |
| `maxPagesPerQuery` | Integer | `20` | Maximum numbered pages read for each term. |
| `includeContacts` | Boolean | `false` | Open each place page for public contacts. |
| `minRating` | Number | `0` | Keep places with at least this rating; `0` includes unrated places. |
| `maxReviewsPerPlace` | Integer | `0` | Recent reviews to attach per place; `0` skips review requests. |
| `maxMediaPerPlace` | Integer | `0` | Photos or videos to attach per place; `0` skips media requests. |
| `proxyConfiguration` | Object | Enabled | Network route for public place pages. |

For a quick city search:

```json
{
  "query": ["coffee", "cafe"],
  "locationQuery": "Moscow",
  "maxItems": 60,
  "maxPagesPerQuery": 10
}
```

For a smaller, richer sample:

```json
{
  "query": ["coffee"],
  "locationQuery": "Moscow",
  "maxItems": 30,
  "includeContacts": true,
  "maxReviewsPerPlace": 2,
  "maxMediaPerPlace": 2
}
```

Moscow, Saint Petersburg, Almaty, Astana, Dubai, and Tashkent are recognized from common English or local names. For another city, use its Latin URL segment in `locationQuery` and select a country domain. For example, a place URL containing `/novosibirsk/firm/` indicates the city slug `novosibirsk`. Search terms can be in the language used by the local directory; category wording and ranking can differ by city.

### Output example

This item comes from a live Moscow coffee search. Long feature lists and nested opening times are shortened here for readability; the dataset keeps the available fields.

```json
{
  "id": "70000001113359188",
  "title": "Здрасте, кофейня",
  "shortName": "Здрасте",
  "extension": "кофейня",
  "url": "https://2gis.ru/moscow/firm/70000001113359188",
  "rubrics": ["Кофейни", "Кафе", "Доставка еды"],
  "category": "cafe",
  "totalScore": 4.8,
  "reviewsCount": 100,
  "ratingCount": 114,
  "country": "Россия",
  "region": "Москва",
  "city": "Москва",
  "district": "Якиманка",
  "street": "Крымская набережная",
  "houseNumber": "10 ст2",
  "address": "Крымская набережная, 10 ст2",
  "latitude": 55.735952,
  "longitude": 37.605268,
  "photoCount": 58,
  "brandName": "Здрасте, кофейня",
  "branchCount": 104,
  "sourceQuery": "coffee",
  "searchPage": 1
}
```

The default dataset contains one JSON object per place. Its Places view emphasizes name, category, rating, review count, address, and link. Locations, Contacts, Reviews, and Media views make the corresponding columns easier to inspect. Exporting the dataset preserves all fields, including those that are not visible in the current preview view.

### Use cases

- **Local directory updates:** compare a city category with an existing directory, add missing businesses, and revisit known place IDs to refresh addresses or opening times.
- **Retail and service coverage:** plot coordinates for a category, compare neighborhoods, and find areas where similar businesses are sparse.
- **Competitor monitoring:** collect names, ratings, review counts, and feature labels for comparable venues, then schedule repeat runs to spot changes.
- **Location planning:** inspect nearby stations, categories, operating hours, and branches before field research in a district.
- **Contact research:** collect public business phone numbers and websites for a small, relevant place set, then manually review each contact before outreach.
- **Review sampling:** attach recent public review text and ratings to understand recurring themes for places in a chosen category.

Results are best treated as directory observations at run time. A missing listing does not establish that a business is closed, and a feature label should be checked against the current place page before a consequential decision. Search order is the directory's result order, which can include promoted listings and can change between runs.

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

The configured result price is **$1.00 per 1,000 delivered places**, or $0.001 per dataset item. A run that saves 30 places therefore has a result charge of $0.03; 500 places would be $0.50; 2,000 places would be $2.00. The item cap stops output after the requested number. These examples describe result charges, not a guarantee that every search term has enough matching places to reach the cap.

Enrichment changes run time and network use. A plain search reads roughly one result page for every 12 returned places. Contacts add a place-page request for each result. Review and media options add service requests, and higher per-place limits can require more than one service page. Choose the smallest limits that answer your question. A 30-place sample with two reviews and two media items per place is much lighter than requesting 200 of each for every place.

The actor stops pushing results when the configured maximum or a platform charge limit is reached. Blank or rejected search pages do not create billable result items. A run can still consume network and execution resources while retrieving pages, especially when a search requires a slower network route or a detail endpoint takes time to respond.

### Tips for best results

Start with a single, specific term and inspect the first 30 results. Category words used locally often work better than broad English terms. For example, a city with local-language labels may return a broader set for its native term than for `coffee`. If you search several synonyms, compare `sourceQuery` to see which term first found each saved place. Duplicate places are counted only once.

Search pages usually provide 12 places. `maxItems` is the output cap; `maxPagesPerQuery` controls how deeply the actor attempts to page through each term. If a query has many matches, increasing `maxItems` alone may not be enough. Set a higher page limit, or split broad research into narrower categories and city searches. Extremely deep page URLs can be redirected or blocked by the site, so there is no promise that every displayed total is reachable.

Use `minRating` after a broad search when you need rated places. This filter is applied to fetched results, so it may reduce the number saved below `maxItems`. Unrated places are excluded when the minimum is above zero. To inspect unrated new listings, leave the minimum at zero and filter the exported dataset later.

Contacts are best requested for a bounded set of place IDs or a modest search sample. Public listings vary: some expose only a website, some only a phone number, and some no contacts. `website` uses the destination site URL when the place page provides one. Social links are grouped by the contact type reported by the directory.

### Limits

The actor uses public, server rendered place data. Search result fields and detail fields can differ. A search result may have ratings and coordinates yet no phone, email, or website; enabling contacts opens its place page to look for those fields. Empty values mean the source did not provide them during that run. A listing may also omit a district, opening hours, station, category code, or main photo.

The site can challenge or slow requests. The actor starts with a low-cost network route, changes route after repeated blocks, and stops a term when a page cannot be read. Numbered search pages can expose fewer than the reported total, and later pages may redirect to the first page. The actor checks the returned page number to avoid repeating those places. This makes output count an observed count, not a claim of complete directory coverage.

Direct catalog requests currently reject the key shown in public pages. The actor instead reads the page's embedded search and place data. Reviews and media use their separate public endpoints when enabled. Those services may have their own availability and pagination limits; up to ten service pages are read for each place. Reviewer profiles and media author identities are not collected.

A direct ID run assumes all IDs belong to the selected city. It skips malformed IDs, and it skips a place if its public page cannot be read. The actor does not search private accounts, gated data, deleted listings, or businesses absent from the public directory. The country-domain selector covers the supported regional routes; availability of a specific city slug depends on the live site.

### FAQ

#### Is this legal to use?

The actor reads publicly visible directory information. You are responsible for complying with applicable terms, privacy law, and the rules for your intended use. Collect only the data you need. Do not use public contacts or review text for spam, profiling, or other personal-data misuse.

#### Do I need to configure a proxy?

The default configuration supplies a network route. Most users can leave it alone. The site may challenge particular IP addresses or regions; a failed page can sometimes succeed after rotation. Repeated challenges and timeouts can still prevent a complete run.

#### How fast is a run?

A plain search requires a small number of large search pages. Contact enrichment adds a place page per item, while reviews and media add more requests. Speed therefore depends on result count, selected detail options, page response time, and any network retries. Run a 30-place sample to measure the route for your city and terms.

#### Can I schedule and monitor repeated searches?

Yes. Save an input configuration and run it on a schedule. Compare `id` values across datasets to identify places seen in earlier runs, and compare rating, review count, hours, or contact fields when you need change monitoring. A change in ranking or site coverage can affect which places appear in a capped search.

#### Can I export to a spreadsheet?

Yes. Download the dataset in CSV or spreadsheet format, or use its dataset endpoint in an existing reporting workflow. Collections such as `rubrics`, `reviews`, and `media` stay nested in JSON and may need to be expanded into separate rows for a tabular analysis.

#### Why is a field empty?

The source may not publish it, or it may appear only on the detail page. Enable `includeContacts` for phone, email, website, and social links. A review or media array can also be empty because that place has no available items or a separate service could not answer.

#### Why did I receive fewer places than requested?

`maxItems` is a ceiling rather than a promise. A category may have fewer results, several terms may overlap, a rating threshold may exclude places, or deeper numbered pages may be unavailable. Check the chosen city, search wording, page limit, and run log before increasing the cap.

### Integrations

Use the dataset endpoint from an automated workflow, connect completion events to a webhook, or import CSV into a spreadsheet or database. Schedules support repeated collection, while the stable place ID makes it practical to match rows between runs. Keep the raw ID and direct URL in downstream records so a place can be checked again later.

### Support

Open an issue on the Issues tab with the city, search term, and a public example URL. We reply within 24 hours and can add fields on request.

# Actor input Schema

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

Keywords or place categories to search in the selected city. Results from multiple terms are deduplicated.

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

City name or Latin city URL slug. Moscow, Saint Petersburg, Almaty, Astana, Dubai, and Tashkent are recognized automatically.

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

Country domain. Auto chooses from the city name; choose a domain for other city slugs.

## `organizationIds` (type: `array`):

Direct 2GIS place IDs or place URLs. When set, keyword search is skipped.

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

Maximum places across all search terms. 0 removes the result cap; page limits still apply.

## `maxPagesPerQuery` (type: `integer`):

Maximum numbered result pages to read for each term. Each page normally has 12 places.

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

Open each place page to collect public phone numbers, emails, websites, and social links. Increases run time.

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

Keep only places at or above this public 2GIS rating. 0 includes unrated places.

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

Proxy configuration for public 2GIS pages.

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

Add this many recent public reviews to each place. 0 skips review requests. Reviewer profiles are not collected.

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

Add this many public photos or videos to each place. 0 skips media requests.

## Actor input object example

```json
{
  "query": [
    "coffee shop"
  ],
  "locationQuery": "Moscow",
  "domain": "",
  "organizationIds": [],
  "maxItems": 100,
  "maxPagesPerQuery": 20,
  "includeContacts": false,
  "minRating": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxReviewsPerPlace": 0,
  "maxMediaPerPlace": 0
}
```

# Actor output Schema

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

All scraped items 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": [
        "coffee shop"
    ],
    "locationQuery": "Moscow"
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/2gis-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": ["coffee shop"],
    "locationQuery": "Moscow",
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/2gis-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": [
    "coffee shop"
  ],
  "locationQuery": "Moscow"
}' |
apify call mlg14/2gis-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mlg14/2gis-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/1Dejnz8P73JFGlEhP/builds/bRLR5l3apdKBMvjdp/openapi.json
