Ai knowledge and logicHelpers

list_google_sheets_rows

List or find Google Sheet rows with bounded cursor pagination.

list_google_sheets_rows(
    sheet_id: str,
    sheet_name: str = None,
    limit: int = 50,
    cursor: str = None,
    where: dict = None,
    include_cell_details: bool = False,
) -> dict

Returns a bounded page of rows from a Google Sheet. Pass where to match exact values in one or more columns. Multiple conditions are combined with AND and matches are case-sensitive.

Each row has this shape:

{
    "rowNumber": 42,
    "values": {
        "Order ID": "1000123",
        "Status": "Pending",
    },
}

Parameters

NameTypeDescription
sheet_idstrSpreadsheet ID from the Google Sheets URL
sheet_namestrOptional tab name; defaults to the first tab
limitintMaximum rows to return, from 1 to 100; defaults to 50
cursorstrOpaque nextCursor from the previous response
wheredictOptional exact column-value matches, such as {"Order ID": "1000123", "Status": "Pending"}
include_cell_detailsboolOptional; defaults to False. Add displayed text, formulas, and hyperlink URLs in each row's cells map.

Returns

{
    "rows": [...],
    "nextCursor": "opaque cursor or None",
}

When nextCursor is not None, pass it back unchanged to continue. Do not conclude that a filtered search has no more matches until nextCursor is None.

Why filtered results are paginated by scan position

Google Sheets has no indexed structured row search. A filtered call scans at most 5,000 rows so a large or sparsely matching sheet cannot consume an unbounded action run. The cursor continues from the last scanned position.

Cursors and row numbers are not permanent IDs

Inserting, deleting, or sorting rows can make a cursor or row number stale. When updating by row number, pass expected values so the write fails closed if the row has moved.

Examples

Find pending requests for an order

page = list_google_sheets_rows(
    "YOUR_SHEET_ID",
    where={"Order ID": "1000123", "Status": "Pending"},
    limit=10,
)

for row in page["rows"]:
    print(row["rowNumber"], row["values"])
page = list_google_sheets_rows(
    "YOUR_SHEET_ID",
    sheet_name="Resources",
    include_cell_details=True,
)

for row in page["rows"]:
    resource = row["cells"]["Resource"]
    print(resource["displayText"], resource["hyperlinks"])

For example, a cell displaying "Installation guide" with a hyperlink formula returns:

{
    "rowNumber": 12,
    "values": {"Resource": "Installation guide"},
    "cells": {
        "Resource": {
            "displayText": "Installation guide",
            "formula": '=HYPERLINK("https://example.com/guide", "Installation guide")',
            "hyperlinks": ["https://example.com/guide"],
        },
    },
}

The existing values map, exact where filtering, and cursor pagination are unchanged. When include_cell_details is omitted or False, cells is absent and no additional metadata request is made. The REST API option is includeCellDetails: true on POST /rest/v1/sheets/rows/search.

Each named column has displayText (the formatted text shown in Sheets) and hyperlinks (distinct URLs, or an empty list). formula is present only for formula cells. Links can come from a HYPERLINK formula, a cell-level link, or rich-text links on parts of the text; manually attached links need no formula. This option returns these cell details, not all formatting or smart-chip data.

Metadata is fetched only for returned rows. Values and metadata are separate reads, so editing or sorting a sheet during a request can cause them to differ; the response is not a transactional snapshot.

Process every page

cursor = None

while True:
    page = list_google_sheets_rows(
        "YOUR_SHEET_ID",
        sheet_name="Refunds",
        limit=100,
        cursor=cursor,
    )

    for row in page["rows"]:
        process(row)

    cursor = page["nextCursor"]
    if cursor is None:
        break

Prerequisites

Share the spreadsheet with [email protected]. Viewer access is enough for listing; use Editor access if the same workflow also appends or updates.

On this page