← All free tools
Free tool

Free Google Maps reviews scraper

Export every review of a place on Google Maps to CSV or Google Sheets: reviewer, rating, date, the full text and the owner’s response. One bookmark, no sign-up, no install.

1. Add the bookmark

⭐ ScrapePass - Google Maps ReviewsDrag me up to your bookmarks bar ↑

Drag this button to your bookmarks bar

Don’t see a bookmarks bar? Press Ctrl+Shift+B (on a Mac, ⌘+Shift+B). Can’t drag? Create a new bookmark, name it “⭐ ScrapePass - Google Maps Reviews” and paste this code as its URL:

Version 1.0.0. Already have the bookmark? The panel shows its version next to “by ScrapePass”. If it is older, drag the button again to get this one.

2. Use it on Google Maps

  1. 1Sign in to Google, open Google Maps and open a place, for example a restaurant or a plumber, so its name and rating show on the left.
  2. 2Click ⭐ ScrapePass - Google Maps Reviews in your bookmarks bar. To get the newest reviews first, choose Sort → Newest on the place’s Reviews tab before the next step.
  3. 3A small panel opens and asks Ready? Press Go. It opens the Reviews tab, scrolls through every review, opens each one’s full text and reads it, while a purple highlight moves down the list.
  4. 4Press Download CSV to save the file, any time you have enough. It opens in Excel and Google Sheets.

What you get

One row per review, with these columns. The examples are made up.

ColumnWhat it holdsExample
placeThe place the reviews are for.Lone Star Plumbing Co.
reviewerThe reviewer’s name.Jamie R.
reviewer_infoLocal Guide badge, and how many reviews and photos they have posted.Local Guide · 14 reviews
ratingStars, from 1 to 5.5
dateWhen it was posted, as Google Maps shows it.3 months ago
textThe full review, with “More” opened.Fast, friendly and fixed the leak the same day…
detailsExtra details the reviewer added, such as services, price or a restaurant’s food and service scores.Services: Drain cleaning · Price assessment: Reasonable price
likesHow many people liked the review.3
photosHow many photos come with the review.2
owner_responseThe owner’s reply, when there is one.Thank you, Jamie, for the great review!
owner_response_dateWhen the owner replied.2 months ago
reviewer_url, place_urlLinks to the reviewer’s profile and to the place.https://www.google.com/maps/contrib/…

Questions

Is it really free?

Yes. No account with us, no limit on how often you use it, and nothing to install. It is a bookmark that runs a small script on the Google Maps page you have open.

Do I need to be signed in to Google?

Yes. Signed out, Google Maps shows only five reviews of a place and asks you to sign in to see the rest. Sign in to Google first.

Is my data sent anywhere?

No. The script reads the reviews in your own browser and builds the CSV there. Nothing is sent to ScrapePass or anyone else. You can read the full source code below.

How many reviews can I get?

Every review Google Maps shows for the place. It reads several reviews a second as Google loads them, so a place with a thousand reviews takes a few minutes. Keep the tab open and in front while it runs, and press Download CSV any time to save what it has so far.

Can I get the newest reviews first?

Yes. On the place’s Reviews tab, open Google’s Sort menu and choose Newest (or Lowest rating, or any other order) before you press Go. The CSV keeps that order.

Can I export several places?

Run the bookmark once on each place. Every row has the place’s name and link, so you can put the files together in one spreadsheet. To find places in the first place, try the free Google Maps scraper.

Does it work in other languages and countries?

Yes. It works on Google Maps in any language and on any Google domain. Reviews come as Google Maps shows them, including its own translations when it shows those.

Which browsers does it work in?

Desktop browsers with a bookmarks bar, such as Chrome, Edge and Brave. Phones and tablets make bookmarks like this hard to run, so use a computer.

What if it stops working?

Google Maps changes its page from time to time, which can stop the bookmark finding reviews or fill some columns wrongly. Press Report a problem in the panel, or contact us, with the place and a screenshot of the panel. Once it is fixed, drag the new bookmark from this page to your bookmarks bar again.

Only collect data you have the right to use, and follow Google’s terms and the privacy laws where you work. Reviews are personal opinions written by people: handle reviewers’ names with care.

See the source code

This is everything the bookmark runs. It works only on the Google Maps page you have open and sends nothing anywhere. The project is also on GitHub.

(function () {
  var KEY = '__scrapepassMapsReviews';
  var VERSION = '1.0.0';
  if (window[KEY]) window[KEY].close();

  var onMaps = /(^|\.)google\.[a-z.]+$/.test(location.hostname) && location.pathname.indexOf('/maps') === 0;
  var placeTitle = document.querySelector('div[role="main"] h1');
  if (!onMaps || !placeTitle) {
    alert('Open a place on Google Maps (search for it and click it), so its name, rating and reviews show on the left. Then click this bookmark again.');
    return;
  }
  var PLACE = placeTitle.textContent.replace(/\s+/g, ' ').trim();
  var PLACE_URL = location.href.split('?')[0];

  var HOME = 'https://scrapepass.com/';
  var UTM = '?utm_source=bookmarklet&utm_medium=tool&utm_campaign=google-maps-reviews';
  var GITHUB = 'https://github.com/ScrapePass/google-maps-reviews-scraper-one-click';
  var YOUTUBE = 'https://www.youtube.com/@scrapepass';
  var COLS = ['place', 'reviewer', 'reviewer_info', 'rating', 'date', 'text', 'details', 'likes', 'photos', 'owner_response', 'owner_response_date', 'reviewer_url', 'place_url'];

  var rows = [];
  var seen = {};
  var running = false;
  var idle = 0;
  var timer = 0;
  var queue = [];
  var current = null;
  var lightTimer = 0;
  var doneMsg = '';
  var total = 0;

  function el(tag, css, text) {
    var e = document.createElement(tag);
    if (css) e.style.cssText = css;
    if (text != null) e.textContent = text;
    return e;
  }
  function text(root, sel) {
    var e = root.querySelector(sel);
    return e ? e.textContent.replace(/\s+/g, ' ').trim() : '';
  }
  function num(s) {
    var m = /\d[\d,.]*/.exec(s || '');
    return m ? m[0].replace(/[,.](?=\d{3}\b)/g, '').replace(',', '.') : '';
  }

  function reviewsTab() {
    var tabs = document.querySelectorAll('div[role="main"] button[role="tab"]');
    for (var i = 0; i < tabs.length; i++) if (/review|đánh giá|rezension|avis|reseñ|recens/i.test(tabs[i].getAttribute('aria-label') || tabs[i].textContent)) return tabs[i];
    return tabs.length > 2 ? tabs[1] : null;
  }
  function totalReviews() {
    var main = document.querySelector('div[role="main"]');
    if (!main) return 0;
    var head = main.querySelector('.F7nice');
    var b = head ? /\(([\d][\d,.\s\u00a0\u202f]*)\)/.exec(head.textContent) : null;
    if (b) return +num(b[1].replace(/[\s\u00a0\u202f]/g, ''));
    var m = /([\d][\d,. ]*)\s+(reviews|review|bài đánh giá|Rezensionen|avis|reseñas|recensioni)/i.exec(main.innerText);
    return m ? +num(m[1]) : 0;
  }
  function scroller() {
    var r = document.querySelector('div.jftiEf[data-review-id]');
    for (var e = r && r.parentElement; e && e !== document.body; e = e.parentElement) {
      var o = getComputedStyle(e).overflowY;
      if ((o === 'auto' || o === 'scroll') && e.scrollHeight > e.clientHeight) return e;
    }
    return document.querySelector('div.m6QErb.DxyBCb.kA9KIf.dS8AEf');
  }
  function expand(card) {
    var more = card.querySelectorAll('button.w8nwRe[aria-expanded="false"]');
    for (var i = 0; i < more.length; i++) more[i].click();
  }
  function details(card) {
    var out = [];
    var blocks = card.querySelectorAll('.PBK6be');
    for (var i = 0; i < blocks.length; i++) {
      var lines = blocks[i].innerText.split('\n').map(function (s) { return s.trim(); }).filter(Boolean);
      if (lines.length) out.push(lines.length > 1 && !/:/.test(lines[0]) ? lines[0] + ': ' + lines.slice(1).join(', ') : lines.join(' '));
    }
    return out.join(' · ');
  }
  function readReview(card) {
    var profile = card.querySelector('.al6Kxe') || card.querySelector('button[data-href*="/contrib/"]');
    var stars = card.querySelector('.kvMYJc');
    var resp = card.querySelector('.CDe7pd');
    var body = card.querySelector('.MyEned .wiI7pd') || card.querySelector('.wiI7pd');
    if (body && resp && resp.contains(body)) body = null;
    var likes = card.querySelector('.pkWtMe');
    return {
      place: PLACE,
      reviewer: text(card, '.d4r55') || card.getAttribute('aria-label') || '',
      reviewer_info: text(card, '.RfnDt'),
      rating: stars ? num(stars.getAttribute('aria-label')) : num(text(card, '.fzvQIb')),
      date: text(card, '.rsqaWe') || text(card, '.xRkPPb'),
      text: body ? body.textContent.replace(/\s+/g, ' ').trim() : '',
      details: details(card),
      likes: likes ? num(likes.textContent) : '',
      photos: card.querySelectorAll('button.Tya61d').length || '',
      owner_response: resp ? text(resp, '.wiI7pd') : '',
      owner_response_date: resp ? text(resp, '.DZSIDd') : '',
      reviewer_url: profile ? (profile.getAttribute('data-href') || '').split('?')[0] : '',
      place_url: PLACE_URL
    };
  }

  var READY_MS = 700;
  var marked = [];
  function enqueue(card) {
    if (card.hasAttribute('data-scrapepass')) return;
    card.setAttribute('data-scrapepass', String(Date.now()));
    marked.push(card);
    queue.push(card);
    expand(card);
  }
  function paint(card) {
    card.style.outline = '2px solid #4f46e5';
    card.style.outlineOffset = '-2px';
    card.style.background = 'rgba(79,70,229,.12)';
  }
  function plain(card) {
    card.style.outline = '';
    card.style.outlineOffset = '';
    card.style.background = '';
  }
  function unmark() {
    marked.forEach(function (card) {
      card.removeAttribute('data-scrapepass');
      plain(card);
    });
    marked = [];
  }

  function collect() {
    var cards = document.querySelectorAll('div.jftiEf[data-review-id]');
    var added = 0;
    for (var i = 0; i < cards.length; i++) {
      var id = cards[i].getAttribute('data-review-id');
      if (seen[id]) continue;
      seen[id] = true;
      enqueue(cards[i]);
      added++;
    }
    return added;
  }

  var file = 'ScrapePass Google Maps Reviews - ' + (PLACE.replace(/[\\/:*?"<>|\s]+/g, ' ').trim().slice(0, 60) || 'place') + '.csv';

  function cell(v) {
    v = String(v == null ? '' : v);
    if (/^[=@\t\r]/.test(v) || /^[+-]/.test(v) && /[^\d\s().+-]/.test(v)) v = "'" + v;
    return /[",\r\n]/.test(v) ? '"' + v.replace(/"/g, '""') + '"' : v;
  }
  function table() {
    return [COLS.join(',')].concat(rows.map(function (r) {
      return COLS.map(function (c) { return cell(r[c]); }).join(',');
    })).join('\r\n');
  }

  function svg(tag, attrs, parent) {
    var e = document.createElementNS('http://www.w3.org/2000/svg', tag);
    for (var k in attrs) e.setAttribute(k, attrs[k]);
    if (parent) parent.appendChild(e);
    return e;
  }
  function logo(size) {
    var root = svg('svg', { viewBox: '0 0 128 128', width: size, height: size, 'aria-hidden': 'true' });
    var grad = svg('linearGradient', { id: 'scrapepass-reviews-logo', x1: 0, y1: 0, x2: 1, y2: 1 }, svg('defs', {}, root));
    svg('stop', { offset: 0, 'stop-color': '#7c3aed' }, grad);
    svg('stop', { offset: 1, 'stop-color': '#4f46e5' }, grad);
    svg('rect', { width: 128, height: 128, rx: 28, fill: 'url(#scrapepass-reviews-logo)' }, root);
    svg('rect', { x: 20, y: 26, width: 70, height: 18, rx: 9, fill: '#fff' }, root);
    svg('rect', { x: 20, y: 55, width: 70, height: 18, rx: 9, fill: '#fff' }, root);
    svg('rect', { x: 20, y: 84, width: 46, height: 18, rx: 9, fill: '#fff' }, root);
    svg('path', { d: 'M70 66 112 92l-17 4 9.5 18.5-10 4.8-9.5-18.5-13 12z', fill: '#fde68a', stroke: '#4c1d95', 'stroke-width': 5, 'stroke-linejoin': 'round' }, root);
    root.style.cssText = 'flex:none;display:block;';
    return root;
  }

  var GITHUB_LOGO = 'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12';
  var YOUTUBE_LOGO = 'M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z';
  function brandIcon(d, color) {
    var root = svg('svg', { viewBox: '0 0 24 24', width: 14, height: 14, 'aria-hidden': 'true' });
    svg('path', { d: d, fill: color }, root);
    root.style.cssText = 'display:inline-block;vertical-align:-2px;margin-right:4px;';
    return root;
  }

  var PURPLE = '#4f46e5';
  function link(label, href, css, icon) {
    var a = el('a', 'color:' + PURPLE + ';font-weight:600;white-space:nowrap;' + (css || ''));
    if (icon) a.appendChild(icon);
    a.appendChild(document.createTextNode(label));
    a.href = href;
    a.target = '_blank';
    a.rel = 'noopener';
    return a;
  }
  var box = el('div', 'position:fixed;top:72px;right:16px;z-index:2147483647;width:340px;background:#fff;color:#1f2328;border-radius:14px;box-shadow:0 8px 32px rgba(0,0,0,.25);font:15px/1.45 Roboto,Arial,sans-serif;padding:18px;');
  var head = el('div', 'display:flex;align-items:center;gap:10px;margin-bottom:12px;');
  head.appendChild(logo(36));
  var titles = el('div', 'flex:1;min-width:0;');
  titles.appendChild(el('div', 'font-size:18px;font-weight:700;line-height:1.2;', 'Google Maps Reviews'));
  var by = el('div', 'font-size:13px;color:#57606a;', 'by ');
  by.appendChild(link('ScrapePass', HOME + UTM, 'text-decoration:none;'));
  by.appendChild(document.createTextNode(' · v' + VERSION));
  titles.appendChild(by);
  head.appendChild(titles);
  var x = el('button', 'border:0;background:none;font-size:24px;line-height:1;cursor:pointer;color:#57606a;padding:0 4px;align-self:flex-start;', '×');
  x.title = 'Close';
  head.appendChild(x);
  var dial = el('div', 'position:relative;width:128px;height:128px;margin:4px auto 10px;');
  var ring = el('div', 'position:absolute;inset:0;box-sizing:border-box;border-radius:50%;border:8px solid rgba(79,70,229,.15);');
  var spin = null;
  var middle = el('div', 'position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;');
  var ready = el('div', 'font-size:26px;font-weight:700;color:' + PURPLE + ';', 'Ready?');
  var count = el('div', 'display:none;font-size:38px;font-weight:700;line-height:1;', '0');
  var unit = el('div', 'display:none;font-size:13px;color:#57606a;margin-top:4px;', 'reviews');
  middle.appendChild(ready);
  middle.appendChild(count);
  middle.appendChild(unit);
  dial.appendChild(ring);
  dial.appendChild(middle);
  var who = el('div', 'height:20px;margin-bottom:2px;font-size:14px;font-weight:600;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;', PLACE);
  var status = el('div', 'font-size:15px;color:#57606a;text-align:center;margin-bottom:14px;', 'Press Go to collect this place\'s reviews.');
  var BTN = 'display:block;width:100%;border:1px solid ' + PURPLE + ';background:' + PURPLE + ';color:#fff;border-radius:8px;padding:11px 12px;font:600 15px Roboto,Arial,sans-serif;cursor:pointer;';
  var go = el('button', BTN, 'Go');
  var csv = el('button', BTN + 'display:none;', 'Download CSV');
  var buttons = el('div');
  buttons.appendChild(go);
  buttons.appendChild(csv);
  var foot = el('div', 'margin-top:14px;padding-top:12px;border-top:1px solid #eaeef2;font-size:13px;color:#57606a;');
  foot.appendChild(document.createTextNode('Need more data? '));
  foot.appendChild(link('Try the ScrapePass extension, the free web scraper', HOME + UTM));
  var help = el('div', 'margin-top:8px;');
  help.appendChild(document.createTextNode('Something not working? '));
  help.appendChild(link('Report a problem', HOME + 'contact' + UTM + '&utm_content=v' + VERSION));
  foot.appendChild(help);
  var more = el('div', 'margin-top:8px;');
  more.appendChild(link('How to use', HOME + 'tools/google-maps-reviews-scraper' + UTM));
  more.appendChild(document.createTextNode(' · '));
  more.appendChild(link('More free tools', HOME + 'free-tools' + UTM));
  foot.appendChild(more);
  var social = el('div', 'margin-top:8px;');
  social.appendChild(link('GitHub', GITHUB, '', brandIcon(GITHUB_LOGO, '#1f2328')));
  social.appendChild(document.createTextNode(' · '));
  social.appendChild(link('Tutorials on YouTube', YOUTUBE, '', brandIcon(YOUTUBE_LOGO, '#FF0000')));
  foot.appendChild(social);
  [head, dial, who, status, buttons, foot].forEach(function (n) { box.appendChild(n); });
  document.body.appendChild(box);

  function light() {
    var next = queue[0];
    if (next && Date.now() - +next.getAttribute('data-scrapepass') < READY_MS) {
      lightTimer = setTimeout(light, 120);
      return;
    }
    var card = queue.shift();
    if (card) {
      if (current) plain(current);
      paint(card);
      current = card;
      card.scrollIntoView({ block: 'start' });
      expand(card);
      var r = readReview(card);
      rows.push(r);
      who.textContent = r.reviewer;
      count.textContent = String(rows.length);
      if (running && total) status.textContent = rows.length + ' of ' + total.toLocaleString('en-US') + ' reviews';
    }
    if (card || running) lightTimer = setTimeout(light, 120);
    else finish();
  }
  function stop(msg) {
    running = false;
    clearTimeout(timer);
    doneMsg = msg;
  }
  function finish() {
    if (current) plain(current);
    current = null;
    who.textContent = PLACE;
    if (spin) spin.cancel();
    ring.style.borderColor = PURPLE;
    count.textContent = String(rows.length);
    status.textContent = doneMsg;
  }

  function scroll() {
    if (!running) return;
    var added = collect();
    total = totalReviews() || total;
    if (!queue.length) {
      idle = added ? 0 : idle + 1;
      if (!rows.length && idle >= 6) return stop('No reviews here. Google shows the list of reviews only when you are signed in to Google.');
      if (total && rows.length >= total) return stop('Done. Download your CSV.');
      if (idle >= 25) return stop('Google Maps stopped loading more reviews. Download your CSV.');
      var box2 = scroller();
      if (box2) box2.scrollTop = idle % 2 ? box2.scrollHeight - box2.clientHeight - 400 : box2.scrollHeight;
      if (idle > 2) status.textContent = 'Waiting for Google Maps to load more…';
    }
    timer = setTimeout(scroll, 1200);
  }

  function close() {
    running = false;
    clearTimeout(timer);
    clearTimeout(lightTimer);
    unmark();
    box.remove();
    delete window[KEY];
  }

  x.onclick = close;
  go.onclick = function () {
    go.style.display = 'none';
    csv.style.display = 'block';
    ready.style.display = 'none';
    count.style.display = 'block';
    unit.style.display = 'block';
    ring.style.borderTopColor = PURPLE;
    if (ring.animate) spin = ring.animate([{ transform: 'rotate(0deg)' }, { transform: 'rotate(360deg)' }], { duration: 1000, iterations: Infinity });
    status.textContent = 'Opening the reviews…';
    var tab = reviewsTab();
    if (tab && tab.getAttribute('aria-selected') !== 'true') tab.click();
    running = true;
    timer = setTimeout(scroll, tab ? 1500 : 0);
    light();
  };
  csv.onclick = function () {
    var blob = new Blob([String.fromCharCode(0xfeff) + table()], { type: 'text/csv;charset=utf-8' });
    var a = el('a');
    a.href = URL.createObjectURL(blob);
    a.download = file;
    document.body.appendChild(a);
    a.click();
    a.remove();
    setTimeout(function () { URL.revokeObjectURL(a.href); }, 5000);
  };
  window[KEY] = { close: close };
})();

Scrape any site, not only Google Maps

ScrapePass turns any web page into clean data, with prebuilt templates for AliExpress, Craigslist, Agoda, G2 and 26 more. Start free, with no account needed. Need larger runs? Sign up and verify your email to get 5 free days.

Add to Chrome