2GISAnswer

2GIS reviews API: export every written review

2GIS's Places API returns ratings and review counts, not review text. Our API exports all 1,374 written reviews of one clinic in 28 calls.

4 min read

Short answer

  • 2GIS's Places API returns ratings and review counts, not review text, and sells keys in rubles.
  • ToolzerHub's 2GIS API returns written reviews with rating, source, author and the owner's reply, 50 a call, and walks to the last one: 1,374 of 1,374 for one clinic.
  • The clinic shows 1,477 reviews on 2GIS and 1,374 written ones come back, because star-only ratings are not served. A call costs 1 credit, so 1,000 reviews cost 21.
On this page

2GIS's API has no method that returns review text. Its Places API returns a place's rating and review counts, and its keys are priced in rubles. ToolzerHub's 2GIS API returns the reviews themselves, 50 a call, with the business owner's replies, from a search or a place id.

What 2GIS's API is for

The Places API searches organizations, buildings and landmarks. A place's items.reviews field holds statistics, so you get its rating and review counts, not the reviews. Access is by key: a demo key lasts one month and 1,000 requests, and the paid Places API costs 6,700 RUB a month for 10,000 requests. If you only need ratings at scale, or a contract with 2GIS, use it.

2GIS reviews with ToolzerHub

We make ToolzerHub. Our 2GIS API has four routes: search finds places in a city, place adds phones, websites and hours, reviews returns reviews, and cities lists the 206 city slugs. Each call is a GET with the x-api-key header and costs 1 credit.

bash
export TOOLZERHUB_API_KEY=your_key
curl -H "x-api-key: $TOOLZERHUB_API_KEY" \
  "https://api.toolzerhub.com/v1/2gis/reviews?id=70000001034757015&limit=50"

The newest review of a St Petersburg dental clinic, trimmed, with the reviewer's name and id removed:

json
{
  "id": "308365657",
  "rating": 5,
  "text": "Спасибо за прием, предложили на выбор несколько вариантов.",
  "date_created": "2026-10-21T20:09:21.953306+07:00",
  "provider": "2gis",
  "official_answer": { "text": "Здравствуйте! Благодарим за доверие и добрые слова ..." }
}

The page also carries total (1,374) and branch_review_count (1,477). total counts written reviews, the ones this route serves, while 2GIS's count includes star-only ratings. Send offset in steps of 50 until you reach total.

To find a place id, search a city: /v1/2gis/search?city=spb&query=стоматология returns 20 of 2,195 clinics with ids, ratings and review counts.

Export every review to CSV

The script takes a city and a search, or a place id. It walks the reviews to the end and names the CSV after the place id.

python
import csv
import os
import sys
import time

import requests

BASE = "https://api.toolzerhub.com/v1/2gis"
HEADERS = {"x-api-key": os.environ.get("TOOLZERHUB_API_KEY", "")}
FIELDS = ["id", "date", "rating", "source", "author", "text", "owner_reply", "likes", "photos"]


def get(path, params, tries=3):
    # 429, 5xx and dropped connections are retried; failed calls cost no credits.
    for attempt in range(tries):
        last = attempt == tries - 1
        try:
            r = requests.get(f"{BASE}/{path}", headers=HEADERS, params=params, timeout=90)
        except requests.RequestException:
            if last:
                raise
            time.sleep(10 * (attempt + 1))
            continue
        if r.status_code in (429, 500, 502, 503, 504) and not last:
            time.sleep(10 * (attempt + 1))
            continue
        r.raise_for_status()
        return r.json()["data"]


def find(city, query):
    data = get("search", {"city": city, "query": query, "page_size": 12})
    if data["is_partial_match"] or not data["results"]:  # no real match, only filler rows
        sys.exit(f"2GIS found no match for {query!r} in {city}")
    place = data["results"][0]  # 2GIS's top match; check the name it prints
    print(f"{place['name']}, {place['address']['street_address']} (id {place['id']})")
    return place["id"]


def main(city_or_id, query=None):
    if city_or_id.isdigit():
        place_id = city_or_id
    elif query:
        place_id = find(city_or_id, query)
    else:
        sys.exit("give a city and a search, or a place id")
    out = f"2gis_reviews_{place_id}.csv"
    offset, total, calls = 0, None, 0
    with open(out, "w", newline="", encoding="utf-8-sig") as f:  # -sig: Excel reads UTF-8
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        writer.writeheader()
        while total is None or offset < total:
            page = get("reviews", {"id": place_id, "limit": 50, "offset": offset})
            calls += 1
            total = page["total"]  # written reviews; star-only ratings are not served
            if not page["results"]:
                break
            for r in page["results"]:
                writer.writerow({
                    "id": r["id"], "date": r["date_created"][:10], "rating": r["rating"],
                    "source": r["provider"], "author": (r.get("author") or {}).get("name"),
                    "text": (r.get("text") or "").replace("\n", " "),
                    "owner_reply": ((r.get("official_answer") or {}).get("text") or "")
                    .replace("\n", " "),
                    "likes": r.get("likes_count"), "photos": r.get("photo_count"),
                })
            offset += len(page["results"])
    print(f"{offset} of {total} written reviews from {calls} review calls saved to {out}")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        sys.exit('usage: python twogis_reviews.py dubai "%Arabica"  (or a place id)')
    if not HEADERS["x-api-key"]:
        sys.exit("set TOOLZERHUB_API_KEY first")
    main(*sys.argv[1:])

python twogis_reviews.py spb "Ворлд Дент" saved 1,374 of 1,374 written reviews in 28 calls and 33 seconds.

What the clinic's reviews showed

The clinic's reviews split like this:

  • 1,247 were 5 stars and 41 were 1 star, an average of 4.81.
  • 1,108 came from 2GIS and 266 from Flamp; provider names the source of each.
  • The clinic had replied to 1,248 of them.
  • 9 pairs had the same author, date and text under different review ids, so dedupe on those three together if you count reviews.

What it costs

A review call returns 50 reviews, so 1,000 reviews is 20 calls plus one search, or 21 credits. The clinic's 1,374 reviews took 29 credits. The 100 free credits a month cover almost 5,000 reviews; $19 buys 10,000 credits that never expire, and plans for higher volume cover more places. Set your key and the first clinic is a CSV in under a minute.

The same job on Google Maps is our Google reviews API guide, and for Yandex Maps see the Yandex Maps reviews API guide.

We are not affiliated with 2GIS.

Get this data from your own code

The 2GIS API returns it as JSON with one x-api-key header. 100 free credits monthly.