<!-- BusyBot node reference — https://busybot.net/tools/google-sheets-trigger/ -->

> Node: Google Sheets Trigger (`google_sheets_trigger`) · Polling trigger · v1
> Category: Data & Storage · Credentials: Google Sheets OAuth2 (`googleSheetsOAuth2`)
> Updated: 2026-08-16

# Google Sheets Trigger

> Triggers on new or updated rows in Google Sheets

## Overview

Monitors a Google Sheets spreadsheet for changes by polling the Sheets API and Drive Revisions API. Detects new rows added (via row count tracking), row updates (via XLSX revision diffing), or both. Supports configurable data ranges, column watching, and version comparison output (new, old, or both). Requires Google OAuth2 credentials with Drive and Sheets API access.

**Category:** Data & Storage  
**Tool Name:** `google_sheets_trigger`  
**Version:** 1

**Appearance:** Icon: `si-googlesheets` | Color: `#4285f4`

## Node Type

**Trigger** — polling (checks for new data on a schedule)

## Input / Output

| Direction | Port(s) |
|-----------|--------|
| Input | None (trigger node) |
| Output | `Output` |

## Credentials

This tool requires **Google Sheets OAuth2** credentials.
See the [Credentials Guide](https://busybot.net/credentials/google-sheets-oauth2/) for setup instructions.

### Parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| Authentication | `options` | No | `oAuth2` | Authentication method to use. |
| | | | | Options: `oAuth2` (recommended), `serviceAccount` |
| Google Account | `credential` | No | — | Connect or select your Google account. _(shown when Authentication is `oAuth2`)_ |
| Service Account Email | `string` | Yes | — | The email address of the Google service account. _(shown when Authentication is `serviceAccount`)_ |
| Private Key | `string` | Yes | — | The private key from the service account JSON key file. _(shown when Authentication is `serviceAccount`)_ |
| Document ID | `string` | Yes | — | The ID of the Google Sheets document. Found in the spreadsheet URL: https://docs.google.com/spreadsheets/d/{documentId}/edit |
| Sheet Name | `string` | Yes | `Sheet1` | The name of the sheet tab to monitor (e.g. "Sheet1"). For update/anyUpdate events, must be 31 characters or fewer. |
| Trigger On | `options` | Yes | `anyUpdate` | Which event to trigger on. "Row Updated" and "Row Added or Updated" require edit access to the document and use the Drive Revisions API. |
| | | | | Options: `rowAdded` (Row Added), `rowUpdate` (Row Updated), `anyUpdate` (Row Added or Updated) |
| Include in Output | `options` | No | `new` | Whether to include the new version, old version, or both versions of changed rows in the output. _(shown when Trigger On is `rowUpdate` or `anyUpdate`)_ |
| | | | | Options: `new` (New Version), `old` (Old Version), `both` (Both Versions) |
| Options | `collection` | No | `{}` | Advanced configuration for the range and rendering of the data read from the sheet. |
| — Columns to Watch | `string` | No | — | Comma-separated list of column names to watch for changes. If empty, all columns are watched. Only applies to Row Updated and Row Added or Updated events. _(shown when Trigger On is `anyUpdate` or `rowUpdate`)_ |
| — Data Location on Sheet | `fixedCollection` | No | `{ "values": { "rangeDefinition": "specifyRangeA1" } }` | Which part of the sheet holds the headers and the data. |
| — — Range Definition | `options` | No | — | How the data range is expressed. |
| | | | | Options: `specifyRangeA1` (Specify Range (A1 Notation)), `specifyRange` (Specify Range (Rows)) |
| — — Header Row | `number` | No | `1` | Index of the row which contains the column headers. Starts at 1. _(shown when Range Definition is `specifyRange`)_ |
| — — First Data Row | `number` | No | `2` | Index of the first row which contains the actual data. Starts at 1. _(shown when Range Definition is `specifyRange`)_ |
| — — Range | `string` | No | — | The table range to read from in A1 notation (e.g. A:Z or C4:E7). _(shown when Range Definition is `specifyRangeA1`)_ |
| — Value Render | `options` | No | `UNFORMATTED_VALUE` | How values should be rendered in the output. _(shown when Trigger On is `rowAdded`)_ |
| | | | | Options: `UNFORMATTED_VALUE` (Unformatted), `FORMATTED_VALUE` (Formatted), `FORMULA` (returns the cell's formula rather than its result) |
| — DateTime Render | `options` | No | `SERIAL_NUMBER` | How dates should be rendered in the output. _(shown when Trigger On is `rowAdded`)_ |
| | | | | Options: `SERIAL_NUMBER` (the spreadsheet serial number), `FORMATTED_STRING` (Formatted String) |
| Poll Interval | `number` | No | `1` | How often to check for new data. |
| Poll Interval Unit | `options` | No | `minutes` | Unit for the poll interval. |
| | | | | Options: `seconds`, `minutes`, `hours` |

## Output Data

Each changed row becomes one output item, with the sheet's header row supplying the property names. Every item carries `_trigger` (always `google_sheets_polling`) and `_timestamp` (when the poll ran).

With **Trigger On** = `rowAdded`, each appended row is emitted using the column headers as keys:

```json
{
  "Name": "Acme Corp",
  "Email": "ops@acme.example",
  "Status": "Active",
  "_trigger": "google_sheets_polling",
  "_timestamp": "2026-08-15T09:00:00.000Z"
}
```

With **Trigger On** = `rowUpdate`, each item additionally carries `row_number` — the 1-based row index in the sheet. With `anyUpdate`, it also carries `change_type`, which is `added` for a row that did not exist in the previous revision and `updated` for one that changed:

```json
{
  "row_number": 14,
  "change_type": "updated",
  "Name": "Acme Corp",
  "Email": "ops@acme.example",
  "Status": "Churned",
  "_trigger": "google_sheets_polling",
  "_timestamp": "2026-08-15T09:00:00.000Z"
}
```

**Include in Output** selects which version of a changed row you get:

| Value | Item shape |
|-------|-----------|
| `new` | The row as it is now (the shape above) |
| `old` | The row as it was in the previous revision |
| `both` | `previous` and `current` objects for the whole row, plus `differences` — `row_number` and one entry per changed column holding `{ previous, current }` |

Reference row values downstream by expression, e.g. `{{ $json.Status }}` or `{{ $json.differences.Status.previous }}`.

## Usage Examples

- Trigger a workflow when a new row is added to a Google Sheet
- Detect updated cells in a spreadsheet and process the changes
- Monitor a Google Sheets document for any row additions or modifications

## Example Configuration

Watch a sheet tab for appended rows:

```json
{
  "type": "google_sheets_trigger",
  "parameters": {
    "documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
    "sheetName": "Sheet1",
    "event": "rowAdded",
    "pollInterval": 5,
    "pollIntervalUnit": "minutes"
  }
}
```

Track edits to existing rows and receive both versions:

```json
{
  "type": "google_sheets_trigger",
  "parameters": {
    "documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
    "sheetName": "CustomerData",
    "event": "rowUpdate",
    "includeInOutput": "both",
    "pollInterval": 2,
    "pollIntervalUnit": "minutes"
  }
}
```

Watch only certain columns inside a fixed A1 range:

```json
{
  "type": "google_sheets_trigger",
  "parameters": {
    "documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
    "sheetName": "Sales",
    "event": "anyUpdate",
    "includeInOutput": "new",
    "pollInterval": 30,
    "pollIntervalUnit": "seconds",
    "options": {
      "columnsToWatch": "Amount,Stage,Owner",
      "dataLocationOnSheet": {
        "values": {
          "rangeDefinition": "specifyRangeA1",
          "range": "A1:E100"
        }
      }
    }
  }
}
```

Read a sheet whose headers are not on row 1:

```json
{
  "type": "google_sheets_trigger",
  "parameters": {
    "documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
    "sheetName": "Inventory",
    "event": "rowAdded",
    "pollInterval": 1,
    "pollIntervalUnit": "minutes",
    "options": {
      "dataLocationOnSheet": {
        "values": {
          "rangeDefinition": "specifyRange",
          "headerRow": 3,
          "firstDataRow": 4
        }
      }
    }
  }
}
```

### Trigger Behavior

- **Activation:** Polling starts when the workflow is activated. There is no poll at the moment of activation — the first check runs one full interval later.
- **Schedule:** The trigger polls for new data based on the configured polling interval.
- **State:** Maintains internal state (the row count for `rowAdded`, or the last document revision seen for `rowUpdate` and `anyUpdate`) so each poll returns only what changed since the last check.
- **First Run:** The first poll records the current position and returns no items, so activating the workflow never replays the existing sheet.
- **Testing:** Running the node from the editor emits a single sample row so you can build the rest of the workflow; real changes arrive only while the workflow is activated.

## Tips

Configure the Document ID, Sheet name, and the event type (Row Added, Row Updated, or Row Added or Updated). The trigger will poll the sheet on a schedule and detect changes since the last check.

### Key Implementation Notes

1. **Event dependencies**: `includeInOutput` is only available when `event` is `"rowUpdate"` or `"anyUpdate"` — it is hidden for `"rowAdded"`. Conversely, `valueRender` and `dateTimeRenderOption` are only available when `event` is `"rowAdded"`.

2. **Sheet name limitations**: for update events, the sheet name must be 31 characters or fewer.

3. **Data location**: when specifying `dataLocationOnSheet`, always use the `"values"` group. Choose `"specifyRangeA1"` and provide a `range` field for A1 notation ranges, or choose `"specifyRange"` and provide `headerRow` and `firstDataRow` fields for row-based ranges. Do not mix fields across the two `rangeDefinition` modes.

4. **Polling frequency**: balance polling frequency with API rate limits — polling too often may exhaust your quota.

### Notes

- `rowAdded` detects appended rows by comparing the row count, so it will not notice a row inserted in the middle of the sheet; use `anyUpdate` for that.
- `rowUpdate` and `anyUpdate` compare the current sheet against the previous document revision, which requires edit access to the document.
- Changing the Document ID or Sheet Name resets the trigger's stored position, and the next poll re-establishes a baseline instead of reporting every existing row as new.