Skip to content
ScrapeField

Blog · Guides ·

Every review of a Google Maps place

Reviews come back a page at a time, with a cursor for the next. A short loop pages through all of them, and sorting by newest makes a daily check cost one page.

ScrapeField

A place’s reviews are the most honest data on Google Maps: what customers liked, what went wrong, and whether the owner answers. This guide fetches all of them for one place, then turns the same loop into a daily check that costs one page.

One page

GET /v1/google-maps/reviews takes the place’s place_id, or the full URL of the place on Google Maps. It answers with up to 8 reviews on the first page, up to 20 on each page after it, and a cursor for the next page:

curl -H "Authorization: Bearer $SCRAPEFIELD_KEY" \
  "https://api.scrapefield.com/v1/google-maps/reviews?place_id=ChIJN1t_tDeuEmsRUsoyG83frY4&sort=newest"

Each review has review_id, rating (1 to 5), text, publish_time, the author with their name and review count, and owner_response where the owner replied. A rating with no words has text null, never an empty string. Every field is in the reviews reference.

A page costs 6 credits, whether it comes from our cache or not: $4.68 per 1,000 pages on the smallest pack, $2.64 on the largest. A page that fails is not charged.

All of them

Pass meta.next_cursor back as cursor until it is null. In TypeScript:

const base = 'https://api.scrapefield.com/v1/google-maps/reviews';
const headers = { Authorization: `Bearer ${process.env.SCRAPEFIELD_KEY}` };
const reviews = [];
let cursor: string | null = null;
do {
  const q = new URLSearchParams({ place_id: 'ChIJN1t_tDeuEmsRUsoyG83frY4', sort: 'newest' });
  if (cursor) q.set('cursor', cursor);
  const res = await fetch(`${base}?${q}`, { headers });
  const body = await res.json();
  if (!res.ok) throw new Error(body.error.message);
  reviews.push(...body.data);
  cursor = body.meta.next_cursor;
} while (cursor);

And in Python:

import os, requests

base = "https://api.scrapefield.com/v1/google-maps/reviews"
headers = {"Authorization": f"Bearer {os.environ['SCRAPEFIELD_KEY']}"}
reviews, cursor = [], None
while True:
    params = {"place_id": "ChIJN1t_tDeuEmsRUsoyG83frY4", "sort": "newest"}
    if cursor:
        params["cursor"] = cursor
    body = requests.get(base, params=params, headers=headers).json()
    if "error" in body:
        raise RuntimeError(body["error"]["message"])
    reviews += body["data"]
    cursor = body["meta"]["next_cursor"]
    if cursor is None:
        break

A cursor lasts as long as the answer it belongs to is cached, up to six hours for reviews. Run the loop in one go; a cursor kept past that is refused with invalid_cursor, and nothing is charged.

Every day, one page

For monitoring, keep the review_ids you have, ask with sort=newest, and stop at the first review you already know. On most places that is the first page, so a daily check costs 6 credits. Add fresh=true when you need reviews from the last few hours rather than our cached copy; it needs any purchase, and costs the same.

The same loop pages every list we serve, from TikTok comments to LinkedIn job searches, because every list pages the same way (how lists work). For thousands of places, run several loops side by side, up to your limit of calls at once.