Key Takeaway
Most accuracy problems in scraped Google review data come from the storage layer. A stable primary key plus a change log fixes nearly all of them. Add a soft delete flag and you are covered.
Here is the pattern I see over and over. You pull reviews into a database and it looks perfect on day one. Six weeks later your average rating is off by half a star and reviews that were deleted still sit in the count. Nothing crashed. The pipeline just quietly drifted.
This walks through the mistakes that cause that drift. Each one is cheap to fix now and painful to unwind after a few hundred thousand rows.
Mistake 1: You keyed reviews on something that changes
The fastest way to wreck a review table is to use author name or review text as the unique key. Both change. People edit their reviews and rename their profiles.
When the key changes, your pipeline sees a brand new review. You get duplicates and your rating average skews toward whoever edits the most.
According to public reports, Google’s Places API does not always return a permanent review ID. So build a composite key from fields that survive edits. A workable version:
review_key = account_id + reviewer_profile_url + review_time_utc
The profile URL and the original review time stay put. The text and the star rating do not belong in the key. They belong in the payload.

Mistake 2: You overwrite rows instead of logging changes
An update that says set rating = 2 destroys the fact that this review was once a 5. Now you cannot answer the one question every marketing lead asks: are people downgrading us over time?
Store current state in one table. Store every change in a second table. The second table holds the review key, the field that changed, the old value, the new value and the moment you caught it. This is your audit trail and it costs almost nothing to run.
When a customer says “that reviewer flipped on us last Tuesday,” you either have the answer or you do not.
Mistake 3: You re-scrape everything on every run
Full re-scrapes look simple and behave badly. They burn your API quota and they multiply the chance of a duplicate slipping through.
Go incremental. Track the newest review timestamp you already stored. On the next run, fetch pages until you hit a review you recognize, then stop. Store a last_seen_at field so you know which reviews still exist upstream.
One caveat: edited old reviews sit outside your window. Run a full reconciliation on a slow schedule, maybe once a month, to catch them.

Mistake 4: You hard deleted reviews that vanished
A vanishing review can mean something other than a deletion. Sometimes it is a pagination glitch. Sometimes the profile went private and came back. If you delete the row the moment it disappears, your history has holes you can never fill.
Use a flag instead. Set is_deleted = true with a deleted_at timestamp and keep the row. If the review returns, flip the flag back and note the gap.
This one change protects your trend charts: a deleted review should drop out of your current rating but never out of your records.
Mistake 5: You dumped the whole payload into one text column
Storing raw JSON in a single column feels safe. It hides what you need: you cannot filter by star rating or join reviews to locations. Every query turns into string parsing that breaks the moment the payload shape shifts.
Split it. Give rating, review time, language and location their own typed columns. Keep the raw payload in a separate column for debugging. Query the columns. Fall back to the payload only when something looks wrong.
Mistake 6: You forgot the scrape timestamp and the timezone
A review time on its own is incomplete. You know when the customer wrote it. You do not know when you first saw it or when you last confirmed it.
Add first_seen_at and last_seen_at. Normalize every timestamp to UTC before it touches the table.

Mistake 7: You relied on fuzzy text matching for dedupe
Fuzzy matching sounds smart. In practice it merges two different one-star reviews from two different people and drops a real complaint. It also misses the same review that came back with a small edit.
Deduplicate on the composite key from Mistake 1. That is deterministic. If you still want a fuzzy pass, run it as a report for a human to review. Never let it delete rows on its own.
What to do this week
Pick one thing. Add the composite key and the change log to your reviews table first. That single move makes every downstream chart reliable again.
Then add the soft delete flag and the scrape timestamps. After that, switch the nightly job to incremental and schedule a monthly full pass.
The real fix is a schema that assumes the data will change. Build it once and your review data stays trustworthy for years.
Read the full guide: Using Scraped Google Review Data in an App: A Straight FAQ