How to Scrape 2GIS Business Listings for a Local Market Map
If you want to compare restaurants across Dubai districts, the useful unit is a business location. A chain with five branches should occupy five places on the map, even when the branches share a name.
The 2GIS Places Scraper accepts 2GIS search-result URLs and returns addresses, coordinates, categories, ratings, and opening schedules. I maintain this Actor, a tool that runs on Apify. This guide turns a small restaurant search into a district comparison; its documented output does not include telephone or email fields.

Start with a search you can inspect
Search for restaurants in the relevant city on 2GIS, then copy the search-result URL. This Actor takes URLs in startUrls, not a standalone keyword or a list of business names. Check the visible search before running: a broad city search may include cafés, hotel restaurants, and venues outside the district you meant to study.
The following example uses the documented Dubai restaurant search. Open the Actor on Apify, choose the JSON view under Input, and paste:
{
"startUrls": [
{"url": "https://2gis.ae/dubai/search/restaurants"}
],
"maxItems": 20,
"maxPages": 2
}
maxItems limits the whole run to twenty places. maxPages allows up to two result pages for each search URL. Either limit can stop collection before the other is reached. The defaults are fifty places and five pages; zero removes the corresponding limit. Keep both positive while checking a new city or category.
Click Start, wait for completion, and open the output dataset. Inspect name, city, district, and searchUrl before exporting. If several search URLs share the twenty-item allowance, one search can consume much of it. Use separate runs when each district needs its own controlled sample.
Read a place record correctly
This abbreviated example comes from the Actor's published historical sample. It is not a fresh observation of the venue:
{
"id": "70000001041258673",
"name": "Ce La Vi Dubai, restaurant",
"city": "Dubai",
"district": "Burj Khalifa/Downtown Dubai",
"latitude": 25.201722,
"longitude": 55.270756,
"categories": ["Cafe / Restaurants"],
"rating": 4.6,
"reviewCount": 33,
"branchCount": 1,
"url": "https://2gis.ae/dubai/firm/70000001041258673"
}
Use id to identify the location and orgName to help group an organization. Do not deduplicate by name alone: two branches may legitimately have the same name. reviewCount provides context for rating; a highly rated location with two reviews is different evidence from one with hundreds.
The full record also has schedule, attributes, address, and scrapedAt. Schedule values are arrays of time intervals keyed by day. An interval ending at 03:00 after a 12:00 opening crosses midnight. A blank schedule does not establish that a restaurant is closed.

Make the map and the comparison sheet
Export CSV for the location table and JSON if you need the nested schedules or attributes. Keep id, name, orgName, district, latitude, longitude, rating, reviewCount, url, and scrapedAt.
First remove duplicate IDs. Next, filter to your intended restaurant categories and district names. Put records with missing coordinates in a separate review sheet instead of assigning them a city-center point. Import the remaining latitude and longitude columns into your mapping tool, and label each point with its name and source URL.
For the district comparison, count observed locations and show the number of rated locations separately. Review the map for implausible coordinates before comparing clusters. This is a map of the collected search results, not an estimate of every restaurant operating in Dubai. Search ordering and the twenty-item cap shape what you see.
If your question concerns chains versus independents, group possible branches using orgName, inspect their addresses, and confirm the grouping from the source. branchCount can help, but it should not replace location-level checks. For opening-hours research, derive a separate evening-service flag only where a schedule is present and retain the raw intervals alongside it.
Cost and gaps to check
Pricing as of September 6, 2026 is $0.0018 per result. Twenty results incur $0.036 in result events. The start event costs $0.00005 per GB of allocated memory, with a minimum of one event. Check the current pricing and your account's separately billed usage before expanding the search.
An empty dataset calls for checking the exact search URL and run log. Remove an overly narrow search constraint before raising the limits. Missing attributes are different from a failed run: the published sample itself contains null fields such as photoCount.
For repeated mapping, save the working input as an Apify task and retain dated datasets. Match later records by ID. A place missing from a later limited search is not proof of closure. The 2GIS Node.js example provides a starting point for moving a reviewed export into code.
If the next question is about the organizations behind these locations, the LinkedIn decision-maker workflow and company career-page guide cover separate research steps.
Frequently asked questions
Is maxItems applied to each search URL?
No. maxItems caps places across the whole run; maxPages limits pagination separately for each search URL.
Does this Actor return phone numbers and email addresses?
The documented output focuses on business locations, categories, ratings, hours, and attributes. It does not document phone or email fields.
Does a missing business mean it has closed?
No. A business can fall outside the search, page limit, or item limit. Check its source listing before interpreting absence as closure.
