• Google Play
  • Reviews
  • Node.js
  • Mobile apps

Export Google Play reviews to CSV: reports, API and code

Export Google Play reviews to CSV from Play Console's monthly reports, Cloud Storage or the Reviews API, and merge every month into one clean UTF-8 file.

Author
Extenify Team
Published
Reading time
12 min read
A wall of numbered brass post office boxes around a closed service window in a post office lobby

To export Google Play reviews, most developers start on the Ratings and reviews > Reviews page of Play Console, filter by date or star rating, and look for a download button. There is not one for individual reviews. Google's own help page on reviews describes filtering, sorting, searching and replying, and points to "reports" you can "download as CSV files from Google Cloud Storage" for the raw data. The Reviews API is no shortcut either: it only returns reviews written or edited in the last week.

This guide covers every official way to get your Google Play reviews into a CSV file, what the file contains, why it often opens as garbage in Excel, and a tested Node.js script that merges every monthly report into one clean UTF-8 file you can analyze, chart or feed to an LLM.

Key takeaways

  • The full history of your reviews lives in monthly reviews reports: reviews_<package>_YYYYMM.csv, downloadable from Play apps reports in Play Console or from your private Cloud Storage bucket.
  • The files are UTF-16, not UTF-8. Convert them before importing into BigQuery or a spreadsheet, or columns and accents break.
  • Bulk access needs the View app information and download bulk reports permission set at the account level ("Global").
  • The Reviews API is for recent reviews and replies only: written reviews from the last 7 days, production versions only, 200 reads per hour per app.
  • None of these routes work for a competitor's app. For that you need public store listing data.

Four ways to export Google Play reviews

Route History Format Best for
Play Console, Play apps reports Monthly files UTF-16 CSV A one-off export of a few months
Cloud Storage bucket Monthly files UTF-16 CSV Scripts, scheduled backups, BigQuery
Reviews API (reviews.list) Last 7 days JSON Fresh reviews, alerts and replies
Public store listing data Depends on the tracker Varies Apps you do not own

The first two give you the same files through different doors. The third is the only real-time option, and the fourth is the only one that covers competitors.

Option 1: Download the reviews CSV in Play Console

Google documents the manual route in its article on downloading and exporting monthly reports:

  1. Open Play Console.
  2. Go to Manage account > Play apps reports and select Reviews.
  3. Under "Select an application", type and select your app's name.
  4. Select the year and month of the report you want to download.

Reviews reports are "detailed" reports, which means one row per individual review rather than daily totals. Google says data in these reports "is captured daily and posted within 3 to 7 days in monthly CSV files", and recommends "building systems that don't depend on the exports being updated at a specific time." In practice, the current month's file grows over the month and lags a few days behind the Reviews page.

What the reviews CSV contains

Each file has these columns, in this order, according to the same help article:

Column What it holds
Package Name The app's package name
App Version Code, App Version Name The version the reviewer had installed
Reviewer Language Language of the review
Device Device code name
Review Submit Date and Time, Review Submit Millis Since Epoch When the review was first written
Review Last Update Date and Time, Review Last Update Millis Since Epoch When it was last edited
Star Rating 1 to 5
Review Title, Review Text The written review, which can be empty
Developer Reply Date and Time, Developer Reply Millis Since Epoch, Developer Reply Text Your public reply, if any
Review Link A link to the review in Play Console

Two details matter when you analyze the file. Use the millisecond columns rather than the formatted dates, because they sort and compare without parsing. And treat Review Title and Review Text as optional: Google marks them optional, so count rows with empty text separately instead of dropping them silently.

Why the file looks broken in Excel

The most common complaint about these reports is that they open as one squashed column, show odd characters, or come out blank in another tool. One developer copying the files with Azure Data Factory found the copies were blank while the same files downloaded on a Mac were about 500 KB, in a thread on the Google developer forum. The cause is the encoding. Google's help page says that to import these reports into BigQuery "you need to convert the CSV files from UTF-16 to UTF-8."

On macOS or Linux, iconv does it in one line and reads the byte order mark to pick the right byte order:

iconv -f UTF-16 -t UTF-8 reviews_com.example.app_202610.csv > reviews-202610.csv

On Windows, open a blank workbook in Excel and use Data > From Text/CSV, then set the file origin to UTF-16 (Excel lists it as "1200: Unicode") before loading. Double-clicking the file is what produces the single squashed column.

Option 2: Copy every month from Cloud Storage

Downloading months one by one gets old quickly. The same files sit in a private Cloud Storage bucket that belongs to your developer account. On the Play apps reports page, click Copy Cloud Storage URI next to the Reviews section. Google says the URI "begins with pubsite_prod_rev", for example pubsite_prod_rev_01234567890987654321, and that review files follow this pattern:

gs://[developer_bucket_id]/reviews/reviews_[package_name]_YYYYMM.csv

The account you use needs bulk report access. Google's help page states: "To access bulk reports, your 'View app information' permission must be set to 'Global'." In the permission list on Users and permissions that is View app information and download bulk reports (read-only), granted at the account level, not per app.

Google's own example still uses gsutil, but Google Cloud now calls gsutil "a legacy Cloud Storage CLI" that is "minimally maintained", and its gsutil page says it will stop shipping with the Google Cloud CLI after March 2027. Use gcloud storage instead:

gcloud auth login                       # an account with access to Play Console
gcloud storage ls gs://pubsite_prod_rev_01234567890987654321/reviews/
gcloud storage cp "gs://pubsite_prod_rev_01234567890987654321/reviews/reviews_com.example.app_2026*" ./reviews/

The bucket is not visible in your Google Cloud projects. It belongs to the Play developer account, so access is controlled entirely by Play Console permissions, not by Cloud IAM roles.

Option 3: Merge every month into one file with Node.js

For a scheduled export, a service account is better than a personal login. Create one in Google Cloud, invite its email address on Play Console's Users and permissions page, and grant the bulk reports permission above. If anything in that setup fails, the guide to the Google Play service account walks through each error and includes a script that tests access to the bucket.

Then install two packages:

npm install @google-cloud/storage csv-parse csv-stringify

The script below lists every reviews file for one package, decodes UTF-16 from the byte order mark, keeps one row per review, and writes a single UTF-8 CSV:

// export-play-reviews.mjs: merge Google Play's monthly review reports into one UTF-8 CSV.
// Usage: node export-play-reviews.mjs pubsite_prod_rev_01234567890987654321 com.example.app
import { writeFile } from 'node:fs/promises';
import { Storage } from '@google-cloud/storage';
import { parse } from 'csv-parse/sync';
import { stringify } from 'csv-stringify/sync';

// Play writes these reports as UTF-16. Detect the byte order mark instead of assuming one.
function decodeReport(buf) {
  if (buf[0] === 0xff && buf[1] === 0xfe) return buf.subarray(2).toString('utf16le');
  if (buf[0] === 0xfe && buf[1] === 0xff) return new TextDecoder('utf-16be').decode(buf.subarray(2));
  if (buf[0] === 0xef && buf[1] === 0xbb && buf[2] === 0xbf) return buf.subarray(3).toString('utf8');
  if (buf.length > 1 && buf[1] === 0x00) return buf.toString('utf16le'); // UTF-16LE without a BOM
  return buf.toString('utf8');
}

const updatedAt = (row) => Number(row['Review Last Update Millis Since Epoch'] || 0);

const [bucketName, packageName] = process.argv.slice(2);
if (!bucketName || !packageName) throw new Error('Usage: node export-play-reviews.mjs <bucket> <package>');

const storage = new Storage(); // reads the key file named in GOOGLE_APPLICATION_CREDENTIALS
const [files] = await storage.bucket(bucketName).getFiles({ prefix: `reviews/reviews_${packageName}_` });
if (files.length === 0) throw new Error('No review reports found. Check the bucket ID, package name and permissions.');

// One row per review: if a review appears more than once, keep its latest edit.
const byKey = new Map();
for (const file of files.sort((a, b) => a.name.localeCompare(b.name))) {
  const [buf] = await file.download();
  const rows = parse(decodeReport(buf), { columns: true, skip_empty_lines: true, relax_column_count: true });
  console.log(`${file.name}: ${rows.length} rows`);
  for (const row of rows) {
    const key = row['Review Link'] || `${row['Review Submit Millis Since Epoch']}|${row['Device']}`;
    const previous = byKey.get(key);
    if (!previous || updatedAt(row) >= updatedAt(previous)) byKey.set(key, row);
  }
}

const reviews = [...byKey.values()].sort(
  (a, b) => Number(a['Review Submit Millis Since Epoch']) - Number(b['Review Submit Millis Since Epoch']),
);
await writeFile(`reviews-${packageName}.csv`, stringify(reviews, { header: true }));
console.log(`Wrote ${reviews.length} reviews to reviews-${packageName}.csv`);

Run it with the service account key:

GOOGLE_APPLICATION_CREDENTIALS=./play-reports-sa.json \
  node export-play-reviews.mjs pubsite_prod_rev_01234567890987654321 com.example.app

We tested the decoding and merging logic on synthetic UTF-16 files with a byte order mark, quoted commas, accented text, empty review text and an edited review that appears in two months. The deduplication is defensive: it costs nothing when every review appears once, and it stops an edited review from being counted twice if it ever shows up again in a later file.

Schedule the script once a day or once a week and you have a backup that does not depend on anyone remembering to click download. The guide to a Node.js cron job shows how to run it on time and catch up after downtime.

Option 4: The Reviews API for the last seven days

The Reply to Reviews API is the right tool for fresh reviews, not for history. Google's limits are explicit:

  • "The API shows only the reviews that include comments." Ratings without text never appear.
  • "You can retrieve only the reviews that users have created or modified within the last week."
  • It covers production versions only, not alpha or beta.
  • Quotas are 200 GET requests per hour and 2,000 replies per day, per app.
  • Pages hold 10 reviews by default and up to 100 with maxResults; pass nextPageToken as token for the next page.
  • Add translationLanguage to get a translation in text and the original in originalText.
  • Replies can be at most 350 characters.

That makes the API a good feed for alerts and reply workflows, which the guide to App Store review alerts builds for both Google Play and the App Store. If your poller stops for more than a week, the monthly reports are how you fill the gap.

What to do with the export

A clean file of every review answers questions the Reviews page makes slow. Here is a short script that reports average stars, written reviews and reply rate per app version:

// review-stats.mjs: average stars, written reviews and reply rate per app version.
import { readFile } from 'node:fs/promises';
import { parse } from 'csv-parse/sync';

const rows = parse(await readFile(process.argv[2], 'utf8'), { columns: true });
const byVersion = new Map();
for (const r of rows) {
  const v = r['App Version Name'] || '(unknown)';
  const s = byVersion.get(v) ?? { reviews: 0, stars: 0, withText: 0, replied: 0 };
  s.reviews += 1;
  s.stars += Number(r['Star Rating']);
  if (r['Review Text'].trim()) s.withText += 1;
  if (r['Developer Reply Text'].trim()) s.replied += 1;
  byVersion.set(v, s);
}
console.table(
  [...byVersion].map(([version, s]) => ({
    version,
    reviews: s.reviews,
    avgStars: (s.stars / s.reviews).toFixed(2),
    withText: s.withText,
    replyRate: s.withText ? `${Math.round((100 * s.replied) / s.withText)}%` : '-',
  })),
);

Run node review-stats.mjs reviews-com.example.app.csv. A few ways to use the result:

  • Spot bad releases. A version whose average drops well below its neighbors usually shipped a bug. Compare it with crash and ANR rates from the Google Play Developer Reporting API.
  • Do not confuse it with your store rating. The average of exported reviews is not the rating Google shows on your listing, which is weighted toward recent ratings and varies by country and device. The guide to Google Play rating calculation explains the difference.
  • Find recurring complaints. Feed the text into a classifier, as in app review analysis with an LLM, or group similar reviews with pgvector in Node.js.
  • Track reply coverage. Low-star reviews without a reply are the easiest wins, since users get a notification and email when you answer.

Exporting a competitor's Google Play reviews

Everything above works only for apps in your own developer account. There is no official export or API for another developer's reviews; the Play Developer API and the reports bucket are scoped to apps you manage. Scraping the store yourself means maintaining code against pages that change without notice.

If you need to watch rivals, use public store listing data instead. Extenify tracks ratings, review counts, new reviews, installs and versions for any public Google Play or App Store listing, and for Chrome, Edge and Firefox extensions, with daily alerts when something changes. That makes it a practical tool for competitor app monitoring next to your own exports. Every plan starts with a 7-day free trial, no credit card required; see pricing.

Google Play review export checklist

  • Your account or service account has View app information and download bulk reports at the account level.
  • You copied the pubsite_prod_rev_... URI from Play apps reports.
  • Scripts use gcloud storage or a client library, not gsutil.
  • Every file is converted from UTF-16 to UTF-8 before it reaches a spreadsheet or BigQuery.
  • Reviews are keyed on Review Link and the latest Review Last Update wins.
  • Empty Review Text rows are counted, not dropped.
  • The Reviews API covers the last 7 days, and the monthly reports cover everything older.

Frequently asked questions

Can you export Google Play reviews from Play Console?

Yes, but not from the Reviews page. Go to Manage account > Play apps reports, choose Reviews, select the app and the month, and download the CSV. Each file covers one month, so a full history means one file per month.

Why does my Google Play reviews CSV look garbled in Excel?

The reports are encoded as UTF-16. Convert them to UTF-8 with iconv -f UTF-16 -t UTF-8, or import them through Excel's Data > From Text/CSV with the file origin set to UTF-16, instead of double-clicking the file.

How far back can I export Google Play reviews?

The reports are organized as one file per month, and the Reviews API only reaches back 7 days. Google's help page does not state a retention limit for the monthly files, so the easiest way to know is to list your bucket with gcloud storage ls and see which months are there. Keep your own copy either way.

Is there an API to export all Google Play reviews?

Not as a single API call. reviews.list returns only written reviews from the last week. For older reviews, read the monthly files from Cloud Storage with the Cloud Storage API or the @google-cloud/storage client, as in the script above.

How do I export Google Play reviews for an app I don't own?

Google offers no official route. Play Console reports and the Developer API only cover apps in your developer account. For competitors, use a tool that records public store listing data over time.

Summary

Google Play keeps your full review history in monthly reviews reports, not on the Reviews page. Download them from Play apps reports for a quick export, or copy them from the pubsite_prod_rev_ Cloud Storage bucket with gcloud storage or a script for anything regular. Convert from UTF-16 before analysis, key each review on its link, and keep the latest edit. Use the Reviews API for the last week and for replies, and public store listing data for apps you do not own.

Image credits

Put every store in one dashboard tonight.

Sign in with Google, paste one store link, and your unified view is live in under a minute. Then 7 days of Pro free, no credit card required, and you never touch your app's code.

Start free trial