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.
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:
{
"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.
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;
providernames 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.