Google reviews to BigQuery

Every Google review as a row in your own project, carrying the site that earned it. Which means the question stops being what the rating is and becomes which site diverges from the ones that look like it.

Set up in under five minutes • No demo required

BigQuery halcyon-analytics.reviews.google_reviews
Google Business Profile 61,482 rows · 34 locations
location
review_date
score
Sheffield Crookes
2026-09-05 09:12
2.0
Bristol Gloucester Rd
2026-09-05 08:47
5.0
Leeds Headingley
2026-09-04 19:30
4.0
Manchester Chorlton
2026-09-04 17:02
5.0
Sheffield Crookes
2026-09-04 16:41
1.0
  • chewy
  • qonto
  • brevo
  • jins
  • filmin
  • lugg

The question is which site diverges

Google Business Profile gives you a rating per location and a list of reviews under it. It will never tell you that thirty-one sites sit between 4.5 and 4.7 while one sits at 3.4, because it has no way of putting those numbers beside each other. In the warehouse that is a GROUP BY and an ORDER BY, and it fits on one screen.

Google files every review under a location, and Reviewflowz gives every location its own profile, so the site name is written onto every row rather than inferred from the text. A site-shaped problem then has a shape you can query for.

That shape is the diagnosis. A price change reads as the whole estate drifting together. A rota gap, a card reader that stopped working, a manager who left in March, reads as one site falling while its neighbours hold steady. Which site tells you who to call, and it takes a query rather than a hunch.

The follow-up is a WHERE clause, because the full review text is an ordinary column. Count the reviews mentioning parking since March, split by site, and you have the estate problem before anyone raises it in a regional meeting.

BigQuery Query editor
-- which sites are behind their own region
SELECT r.location,
       ROUND(AVG(r.score), 1) AS rating,
       COUNT(*) AS reviews
FROM `halcyon-analytics.reviews.google_reviews` r
JOIN `halcyon-analytics.ops.sites` s USING (location)
WHERE r.review_date >= '2026-03-01'
GROUP BY r.location
ORDER BY rating
Results 5 of 34 locations in 0.6s
location rating reviews
Sheffield Crookes 3.4 96
Nottingham W Bridgford 4.5 88
Leeds Headingley 4.6 118
Manchester Chorlton 4.6 173
Bristol Gloucester Rd 4.7 141

A table, not a report

What lands is a table, not a dashboard somebody else designed. One row per Google review: the location, the review id, the timestamp, the score, the full text, the detected language, the tags and a link back to the review on Maps. Plain columns, nothing to reverse engineer, nothing you have to ask us to add.

The location is on every row because Google gives you one profile per site and Reviewflowz keeps it that way. It arrives as the name Google holds rather than an id, so you name that column yourself when you declare the table.

The review text is a STRING column like any other, so the term you care about is a WHERE clause instead of an afternoon of reading. Reviewflowz detects the language from the text itself, which is why a French review left on a Brussels listing still groups the way you would expect it to.

One column you will not find, and it is worth saying out loud: this table carries the review, not the reply. Reply coverage per site is a fair question, and a page that quietly implied this one answered it would cost you a morning.

BigQuery halcyon-analytics.reviews.google_reviews Schema
location STRING

One row per review, tagged to the site

Join key
review_id STRING Stable, so updates land in place
review_date TIMESTAMP When the review was posted
score FLOAT64 1.0 to 5.0
review_text STRING The full text, queryable
language STRING Detected from the text
tags STRING Themes, filled as reviews arrive
review_url STRING Back to the review on Google Maps

Google gives one profile per location, so the site name is on every row.

How the rows actually reach BigQuery

There is no managed BigQuery destination to click, and this page will not pretend there is. What Reviewflowz gives you is a Google Sheet in your own Drive that it keeps current: a new Google review is a new row within half an hour, an edited review updates its row in place, and a review Google removes loses its row.

BigQuery reads a Drive-backed Google Sheet as an external table. One CREATE EXTERNAL TABLE against the sheet URL and google_reviews exists in your dataset, queries like any other table, and is right at query time because it reads the sheet rather than a copy of it. Nothing to schedule, nothing to keep alive.

If you would rather have a native partitioned table, the Review API is the other road. Poll it or take the webhook, then load it with the job you already run for every other source. That is work a data team writes once.

Either way the history comes with it. On a paid plan, one click loads every past Google review across every site, so the trend line starts full rather than filling in over the next quarter.

resyncs every 30 min
Google Business Profile Google Sheets BigQuery
Google
reviews
Sheet in
your Drive
External table
in BigQuery
BigQuery Run once
CREATE EXTERNAL TABLE
  `halcyon-analytics.reviews.google_reviews`
OPTIONS (
  format = 'GOOGLE_SHEETS',
  uris = ['https://docs.google.com/spreadsheets/d/1a9F...'],
  skip_leading_rows = 1
)

Reads the sheet at query time, so there is nothing to schedule and nothing to keep alive.

Join it to what you already keep per site

Location is a join key you already have. Every business with more than one site keeps a table with a row per site: revenue, staffing hours, footfall, opening times, the date of the last refit. Google reviews arrive tagged the same way, so putting the two together is a JOIN rather than a project.

That is where the interesting numbers live. Rating beside revenue per visit shows what a bad quarter at one branch actually costs. Rating beside rota hours shows whether the site everyone complains about is the site nobody staffed.

It also fixes the comparison. A 3.4 only means something next to the sites that look like it, so join to your own segments, high street against retail park, and compare a site to its peers rather than to an estate average.

This is the whole reason to put Google reviews in a warehouse rather than read them in a tool. A review tool can only ever tell you about reviews. The warehouse is where a review sits beside the staffing, the takings and the refit that explain it, and where the explanation is a column rather than a theory.

BigQuery
reviews.google_reviews
location
review_date
score
review_text
USING (location)
Your data
ops.sites
location
revenue
staff_hours
footfall
Result
location rating ops.sites revenue_per_visit
Sheffield Crookes 3.4 GBP 11.20
Leeds Headingley 4.6 GBP 18.40
Bristol Gloucester Rd 4.7 GBP 19.10

What a monthly average hides

Google review volume tracks footfall, not a release schedule. It climbs on Friday, doubles on Saturday and stays high on Sunday, then jumps again for a fortnight after a local campaign. A monthly average sits above five of those days and well under the other two, so it describes none of them.

That is a modelling problem rather than a reporting one, which is exactly what a warehouse is for. Compare like for like on day of week, put a seven day window on the trend, and a bad Saturday stops hiding inside a monthly number.

You choose what flows out. Pick the locations, and add a score filter if you want one, so the estate team gets every site and the support lead gets a table holding one to three stars and nothing else.

And the table lives in your own Google Cloud project, in the region you picked, under the IAM you already run and the retention policy you already wrote. Granting a data scientist access is the same few clicks it is for every other dataset in there, and taking it away again is the same few clicks back.

BigQuery reviews by day_of_week 1,092 reviews · 34 locations
avg 72
41
38
44
47
66
148
121
Mon Tue Wed Thu Fri Sat Sun

A monthly average sits above five days and under two, which is why the warehouse models this and a report does not.

A rating on its own explains nothing. The site beside it does.

Set up in under five minutes. No demo required.

Is there a native BigQuery connector?

No, and it would be dishonest to imply one. Reviewflowz syncs a Google Sheet in your own Drive, and BigQuery reads that sheet as an external table with a single CREATE EXTERNAL TABLE statement. If you would rather have a native partitioned table, the Review API plus a scheduled load job gets you there. Both are ordinary work for a data team, and neither one needs a demo.

Does the location really land on every row?

Yes. Google gives you one profile per location, and Reviewflowz keeps that one-to-one, so every review row carries the site it belongs to. It arrives as the location name Google holds rather than as an id of yours, so you declare that column as location when you create the external table and map the names to your own site keys once. After that it is an ordinary join key.

How current is the table?

The sheet resyncs every 30 minutes, and an external table reads the sheet at query time, so a query you run now sees whatever the last sync wrote. A load job into a native table is as current as whatever schedule you give it.

Which columns does the table have?

The synced sheet has a fixed 28 column header, one row per review: the review id, the location, the platform, the review date, the score, the title, the full text, the detected language, the reviewer name, the tags, a link back to the review, and the rest. There is no reply column and there are no per-platform tabs, so everything sits in one flat table you can point a schema at.

Does the first load include our Google review history?

Yes, on a paid plan: one click loads every past Google review across your locations, so the trend line is full from day one rather than in three months. During the free trial new reviews arrive as they are posted, and the history loads once you subscribe.

What happens when a reviewer edits or deletes their review?

The row updates in place with the new score and text, so a query over it moves with the correction. When a review comes off your Google listing, its row goes on the next sync, which means the table never holds a version of the truth that Google no longer does.

Do I need a demo to get started?

No. Connect your Google Business Profile and the Google Sheet yourself in under five minutes, then run one CREATE EXTERNAL TABLE against it. 14 day free trial, no credit card.