> For the complete documentation index, see [llms.txt](https://docs.pullbay.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.pullbay.com/documentation/integrations/pullbay-x-google-sheets-integration.md).

# Pullbay X Google Sheets Integration

Google Sheets is the perfect home for App Store review data. It's free, shareable, and lets you analyze reviews with charts, pivot tables, and filters — no coding skills required. This guide shows you how to automatically sync Pullbay reviews to a Google Sheet using Google Apps Script.

## Why Log Reviews to Google Sheets?

* **Shareable**: Share the sheet with your team, stakeholders, or investors in seconds
* **Searchable**: Filter reviews by rating, date, country, or keywords
* **Visual**: Create charts showing review trends over time
* **Collaborative**: Add comments, assign follow-ups, and tag team members
* **No-code analysis**: Use built-in functions (AVERAGE, COUNTIF, etc.) to calculate metrics
* **Backup**: Keep a permanent record of all reviews with timestamps

## Two Approaches

1. **Google Apps Script** (recommended) — write a simple script that calls the Pullbay API and logs reviews directly
2. **Via automation platforms** — use Zapier or Make.com as middleware if you prefer not to write code

This guide focuses on the Google Apps Script approach, which is free and requires minimal setup.

## Approach 1: Google Apps Script (Direct API Call)

### Prerequisites

* A Google account and access to Google Sheets
* Your Pullbay API key (from the [dashboard](https://dashboard.pullbay.com/))
* Your app's numeric App Store ID (8–12 digits) or bundle ID (e.g. `com.burbn.instagram`)

{% stepper %}
{% step %}

## Create a Google Sheet

1. Go to [sheets.google.com](https://sheets.google.com/)
2. Click **Create** and select **Blank spreadsheet**
3. Name it (e.g. "App Store Reviews - 2026")
4. Click **OK**
   {% endstep %}

{% step %}

## Set Up Sheet Columns

In row 1, create these column headers:

| Column | Header    | Source field         |
| ------ | --------- | -------------------- |
| A      | Date      | `review.date`        |
| B      | Score     | `review.score` (1–5) |
| C      | Title     | `review.title`       |
| D      | Review    | `review.text`        |
| E      | Author    | `review.userName`    |
| F      | Version   | `review.version`     |
| G      | Country   | `review.country`     |
| H      | Review ID | `review.id`          |

Starting in row 2, the script will fill in review data automatically.
{% endstep %}

{% step %}

## Open the Apps Script Editor

1. In your Google Sheet, click **Extensions** (top menu)
2. Click **Apps Script**
3. A new tab opens with the Apps Script editor
4. Delete the default `myFunction()` that appears
   {% endstep %}

{% step %}

## Write the Review-Fetching Script

Copy and paste this complete script into the editor:

```javascript
function fetchAndLogReviews() {
  // ── Configuration ────────────────────────────────────────────────────────
  const API_KEY = "YOUR_API_KEY";  // Replace with your Pullbay API key
  const APP_ID  = "389801252";     // Numeric App Store ID (or use BUNDLE_ID below)
  // const BUNDLE_ID = "com.burbn.instagram"; // Alternative: use bundle ID instead of APP_ID
  const MAX_ITEMS = 500;           // Maximum reviews per run (1–500)

  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();

  // ── Build request URL ────────────────────────────────────────────────────
  const params = new URLSearchParams({
    appId:    APP_ID,      // Use bundleId: BUNDLE_ID if using bundle ID
    country:  "us",
    sort:     "recent",
    maxItems: MAX_ITEMS,
  });

  const url = `https://api.pullbay.com/appstore/reviews?${params}`;

  const options = {
    method:            "get",
    headers:           { "Authorization": `Bearer ${API_KEY}` },
    muteHttpExceptions: true,
  };

  // ── Fetch reviews ─────────────────────────────────────────────────────────
  try {
    const response     = UrlFetchApp.fetch(url, options);
    const responseCode = response.getResponseCode();

    if (responseCode !== 200) {
      Logger.log(`Error: API returned ${responseCode}`);
      Logger.log(response.getContentText());
      return;
    }

    const body = JSON.parse(response.getContentText());

    if (!body.success || !body.data) {
      Logger.log("Error: unexpected response from Pullbay");
      Logger.log(JSON.stringify(body));
      return;
    }

    const reviews       = body.data;
    const creditsCharged = body.pricing.creditsCharged;
    Logger.log(`Fetched ${reviews.length} reviews — requestId: ${body.requestId}, credits: ${creditsCharged}`);

    // ── Clear existing data (comment out to append instead) ───────────────
    const lastRow = sheet.getLastRow();
    if (lastRow > 1) {
      sheet.deleteRows(2, lastRow - 1);
    }

    // ── Write reviews to sheet ─────────────────────────────────────────────
    reviews.forEach((review, index) => {
      const row  = index + 2; // Row 1 is headers
      const date = new Date(review.date).toLocaleDateString();

      sheet.getRange(row, 1).setValue(date);             // Date
      sheet.getRange(row, 2).setValue(review.score);     // Score (1–5)
      sheet.getRange(row, 3).setValue(review.title);     // Title
      sheet.getRange(row, 4).setValue(review.text);      // Review text
      sheet.getRange(row, 5).setValue(review.userName);  // Author
      sheet.getRange(row, 6).setValue(review.version);   // App version
      sheet.getRange(row, 7).setValue(review.country);   // Country
      sheet.getRange(row, 8).setValue(review.id);        // Review ID
    });

    Logger.log(`Successfully logged ${reviews.length} reviews`);

  } catch (error) {
    Logger.log(`Error: ${error.toString()}`);
  }
}
```

{% endstep %}

{% step %}

## Add Your API Key and App ID

In the script, replace:

* `YOUR_API_KEY` with your actual Pullbay API key
* `389801252` with your App Store app ID

If you prefer to use the bundle ID (e.g. `com.burbn.instagram`), comment out the `appId` line and uncomment `bundleId`.
{% endstep %}

{% step %}

## Test the Script

1. In the Apps Script editor, click the **Run** button (play icon)
2. If prompted for permissions, click **Review permissions**, select your Google account, and click **Allow**
3. Check the **Execution log** (bottom panel) for output messages

Go back to your Google Sheet. If successful, you'll see review data from row 2 onward.
{% endstep %}

{% step %}

## Set Up Automatic Daily Runs

To run the script automatically every day:

1. In the Apps Script editor, click the **Triggers** icon (clock) on the left sidebar
2. Click **Create a trigger**
3. Configure the trigger:
   * **Function to run**: `fetchAndLogReviews`
   * **Deployment**: Head
   * **Event source**: Time-driven
   * **Type**: Day timer
   * **Time of day**: e.g. 9 AM
4. Click **Save**

The script now runs automatically at your chosen time every day. Check the **Executions** log in Apps Script to verify runs succeeded.
{% endstep %}
{% endstepper %}

## Understanding the Script

The script does four things:

1. **Builds the request URL** with `appId`, `country`, `sort`, and `maxItems` as query parameters
2. **Calls the Pullbay API** with your Bearer token and parses the response envelope (`body.data`, `body.pricing.creditsCharged`, `body.requestId`)
3. **Clears previous rows** starting from row 2 (optional — comment out to append instead)
4. **Writes each review** to a new row using the actual field names from the API: `score`, `text`, `userName`, `version`, `country`, `id`

To **append** new reviews on every run instead of replacing, comment out the delete block:

```javascript
// const lastRow = sheet.getLastRow();
// if (lastRow > 1) {
//   sheet.deleteRows(2, lastRow - 1);
// }
```

And change the row assignment in the forEach loop:

```javascript
reviews.forEach((review) => {
  const row = sheet.getLastRow() + 1; // Append to next empty row
  // ... rest unchanged
});
```

## Approach 2: Using Zapier or Make as Middleware

If you prefer not to write scripts, you can use Zapier or Make.com:

1. Follow the Zapier or Make.com integration guides
2. In the final action, select **Add row to Google Sheets**
3. Map the Pullbay fields to your sheet columns using the field names above (`score`, `text`, `userName`, etc.)
4. Reviews are logged automatically on your chosen schedule

## Tips for Analysis and Organization

### Color-Code Reviews by Rating

1. Select the **Score** column (column B)
2. Click **Format** → **Conditional formatting**
3. Add rules:
   * Score = 5: Green background
   * Score = 4: Light green background
   * Score ≤ 2: Red background
4. Click **Done**

High-priority reviews stand out at a glance.

### Create a Pivot Table for Review Trends

1. Select all your data (including headers)
2. Click **Insert** → **Pivot table** → **Create**
3. In the pivot table editor:
   * **Rows**: Country
   * **Columns**: Score
   * **Values**: COUNTA of Review ID
4. Click **Insert**

Example output:

| Country | 1 | 2 | 3  | 4  | 5  |
| ------- | - | - | -- | -- | -- |
| US      | 3 | 5 | 12 | 24 | 45 |
| GB      | 1 | 2 | 8  | 18 | 32 |

### Calculate Key Metrics

Add formulas in a dedicated "Metrics" sheet or row:

* **Average score**: `=AVERAGE(B2:B)`
* **Total reviews**: `=COUNTA(H2:H)` (counts non-empty Review ID cells)
* **Low-rating count**: `=COUNTIF(B2:B,"<=2")`
* **5-star count**: `=COUNTIF(B2:B,5)`
* **Latest review date**: `=MAX(A2:A)`

### Set Up Filters

1. Click any cell in your data
2. Click **Data** → **Create a filter**
3. Use the filter icons on the header row to show only 1–2 star reviews, a specific country, or a date range

Filters let you focus without changing the underlying data.

### Use Multiple Sheets Per App

If you manage multiple apps:

1. Create a separate sheet tab for each app (rename tabs at the bottom)
2. Modify the script to write to a named sheet instead of the active one:

```javascript
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Instagram");
```

3. Create one trigger per app or pass the app ID as a parameter

## Modifying the Script for Advanced Use

### Filter by Date (Last 7 Days Only)

```javascript
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);

const recentReviews = reviews.filter(review => new Date(review.date) >= sevenDaysAgo);
// Use recentReviews in the forEach loop below
```

### Send a Summary Email When Complete

Add this at the end of the script to receive an email after each run:

```javascript
GmailApp.sendEmail(
  Session.getEffectiveUser().getEmail(),
  "Reviews Synced",
  `${reviews.length} reviews logged — ${creditsCharged} credits used — ${new Date().toLocaleString()}`
);
```

### Paginate Manually (for Very Large Datasets)

`maxItems=500` fetches up to 500 reviews in one call (the endpoint maximum). If you want to paginate manually across multiple runs using cursors, store the cursor value in a script property:

```javascript
const props  = PropertiesService.getScriptProperties();
const cursor = props.getProperty("reviewCursor");

const params = new URLSearchParams({ appId: APP_ID, country: "us", sort: "recent" });
if (cursor) params.set("cursor", cursor);

// After fetching...
const pagination = body.pagination;
if (pagination.hasNextPage && pagination.cursor) {
  props.setProperty("reviewCursor", pagination.cursor);
} else {
  props.deleteProperty("reviewCursor"); // No more pages; reset
}
```

## Troubleshooting

<details>

<summary>Script runs but no data appears in the sheet</summary>

* Check the Execution log (**View → Execution log**) for error messages
* Verify your API key is correct and active in the Pullbay dashboard
* Confirm your app ID is a valid 8–12 digit number (or that the bundle ID matches exactly)
* Check that your app has reviews in the selected country

</details>

<details>

<summary>Authentication error / 401 response</summary>

* Your API key may have been revoked or expired
* Generate a new key in the [Pullbay dashboard](https://dashboard.pullbay.com/) and update the script

</details>

<details>

<summary>Script times out</summary>

* Apps Script has a 6-minute execution limit
* Reduce `MAX_ITEMS` (e.g. to 100) or add a date filter so fewer rows are written per run
* For very large review sets, paginate across multiple scheduled runs using script properties (see above)

</details>

<details>

<summary>Need to change the API key or app ID later</summary>

* Click **Extensions** → **Apps Script**
* Update the values at the top of the script
* Save and re-run to verify

</details>

## Rate Limits and Credits

Each time the script runs, it makes one API request. Credits consumed depend on the number of reviews returned:

```
creditsCharged = 1 (base) + returned_reviews × 1
```

For example: 200 reviews returned → 201 credits charged.

Rate limits are enforced per API key based on your plan (per-second and per-minute). Running once per day is well within all plan limits. Check the `X-RateLimit-Remaining` response header or your Pullbay dashboard to monitor usage.

## Next Steps

1. Create your Google Sheet and add the column headers from Step 2
2. Copy the Apps Script code and set your API key and app ID
3. Test the script with the **Run** button and verify the Execution log
4. Set up a time-based trigger for automatic daily runs
5. Add conditional formatting, pivot tables, and metric formulas
6. Share the sheet with your team for collaborative review management
