PlainParse

Business Reviews Exporter / Export format

What's in the Google review export

Thirteen columns in the CSV, fourteen keys in the JSON, and they are not spelled the same way. Here is what each one holds, and the three that surprise people.

Every column, in order

The CSV header row is written exactly as below. The JSON carries the same review data but names its keys differently, so the mapping is set out in the JSON section further down.

ColumnWhat it holds
review_idThe review's own identifier, as the page provides it. It is what rows are keyed on within a capture, so it is the natural column to deduplicate on if you merge two exports.
reviewerThe reviewer's display name as shown on the review card.
rating1 to 5. Can be empty — see below.
relative_dateThe date as the page states it: "3 months ago", "a year ago". Not a calendar date. See below.
textThe review body. Line breaks inside it are preserved, quoted, so a multi-paragraph review stays in one cell.
text_truncatedtrue when the page had collapsed the review behind a "More" link and the full text was not available. See below.
owner_replyYour published response, if there is one. Empty otherwise.
owner_reply_relative_dateWhen the reply was posted, in the same relative form.
photo_countHow many photos the reviewer attached.
reviewer_profile_urlThe link to the reviewer's profile, passed through exactly as the page provided it. Nothing parses or rebuilds this, so a change in how those links are formed cannot break the export.
place_nameThe business the review belongs to.
place_urlThe profile the export was taken from.
captured_atAn ISO timestamp of when the export ran. Reputation records should be honest about their as-of date, and this is that date.

The three fields that surprise people

Dates are relative, not absolute

This is the one that catches everybody. The Google review page does not publish a calendar date; it publishes "3 months ago". There is no hidden exact date to read — the precise timestamp is simply not on the page, so no tool reading that page can give you one without inventing it.

What you get is the string as displayed, in relative_date. Combined with captured_at you can bound it: a review marked "3 months ago" in an export captured on 15 March fell in roughly the second half of December. That is an honest range rather than a false precision, which matters if the export is going anywhere near a dispute.

If you need sortable dates: derive them from captured_at minus the relative string, and keep both columns so the derivation stays auditable. Do not overwrite relative_date — it is the only primary evidence in the row.

Long reviews can be truncated

The page collapses long reviews behind a "More" link, and a collapsed review's full text is not in the page until it is expanded. Where that happened, text holds what was visible and text_truncated is true.

The flag exists so truncation is visible in the data rather than silent. Filtering on text_truncated = true tells you exactly which rows to look at by hand, instead of leaving you to notice later that a review ends mid-sentence.

Rating can be empty

The star rating is read from the accessible label on the rating element. If that label yields no digit at all, the count of filled star glyphs is used instead. If what comes out is not a number between 1 and 5, rating is written empty rather than guessed, and the run records that the row had a problem.

An empty rating is a row worth a manual look, not a broken export. Writing a plausible-looking number there would be worse: a wrong 4 is indistinguishable from a right one once it is in a spreadsheet.

CSV specifics

The CSV is written so a spreadsheet opens it correctly on the first try:

review_id,reviewer,rating,relative_date,text,text_truncated,owner_reply,...
r-8f2a,Dana Rivera,5,3 months ago,"Booked for a party of six, and they moved us
without being asked.",false,"Thank you Dana — glad it worked out.",...

Note the second row: the review text contains a line break and a comma, so it is quoted, and the break is kept inside the cell rather than spilling into the row below.

JSON specifics

The JSON is an array of objects, indented two spaces. It holds the same reviews, but it is a direct serialisation of the tool's internal record rather than a re-shaped copy of the CSV, and that has three consequences worth knowing before you script against it.

The keys are camelCase, not the CSV's snake_case

Only reviewer, rating and text are spelled the same in both. The other ten differ:

CSV columnJSON key
review_idreviewId
relative_daterelativeDate
text_truncatedtextTruncated
owner_replyownerReply
owner_reply_relative_dateownerReplyRelativeDate
photo_countphotoCount
reviewer_profile_urlreviewerProfileUrl
place_nameplaceName
place_urlplaceUrl
captured_atcapturedAt

Key order differs too: reviewerProfileUrl is third in the JSON and tenth in the CSV. Read by name, not by position.

There is a fourteenth key the CSV does not have

Every JSON row carries an issues array. It is the per-row diagnostic log — anything the parser noticed while reading that review, such as a rating it could not resolve. Usually empty. It is genuinely useful if you are auditing a capture, and it is the reason a JSON row is not simply a CSV row with different punctuation.

Types are real, not stringified

Picking one: take the CSV if it is going to a person in a spreadsheet, and the JSON if it is going to code — the real types and the issues log are worth having there. Just do not assume a field name carries across; it mostly does not.

Export your own Google reviews. Reviewer, rating, date, full text, your replies and photo counts — CSV or JSON, taken in your browser, nothing uploaded anywhere.

See how it works