Your read_range reads the MRR tracker. Now the other side — when a new customer signs up, you add a row. What does that row look like?
After read_range I can read the existing data. But every new customer means I open Sheets, scroll to the bottom, type date, plan, MRR, status. Four fields, every time. ['2026-04-14', 'Pro', 149.0, 'active'].
That four-field list is exactly what you pass to GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND. The action appends it after the last row in your range automatically — no scrolling, no finding the next empty row:
result = toolset.execute_action(
Action.GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND,
{"spreadsheet_id": sheet_id, "range": range_a1, "values": [values]}
)
print(result.get("updates", {}).get("updatedRows", 0))Why [values] with the extra brackets? The values are already a list.
The API expects a list of rows — each row is a list. [values] wraps your single row in the outer list. If you're appending one row: [["date", "plan", 149.0, "active"]]. If you're appending multiple rows: [[row1], [row2]]. Here's the full function:
def append_row(sheet_id: str, range_a1: str, values: list) -> dict:
result = toolset.execute_action(
Action.GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND,
{"spreadsheet_id": sheet_id, "range": range_a1, "values": [values]}
)
print(f"Appended row to {range_a1}")
return resultSo every new paying customer triggers append_row — date, plan, MRR, status — and the tracker updates itself. That's my Monday log entry gone.
Your MRR tracker just got its first automated row. Every paying customer from now on writes themselves in.
Read then append — the full Sheets read-write cycle. My MRR tracker is now a live Python data store.
Always call read_range before appending to confirm the sheet has data and your range is correct. Appending to the wrong range creates rows in the wrong place — harder to undo than a draft email.
GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND adds rows after the last existing row in the range.
toolset.execute_action(
Action.GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND,
{"spreadsheet_id": "1abc...", "range": "Sheet1!A:D", "values": [["2026-04-14", "Pro", 149.0, "active"]]}
)values is a list of rows. Each row is a list of cell values. One row: [[v1, v2, v3]]. Multiple rows: [[r1v1, r1v2], [r2v1, r2v2]].
values must be [[row]] — a list of lists. Passing [row] (a flat list) inserts each value as a separate column header.
Your read_range reads the MRR tracker. Now the other side — when a new customer signs up, you add a row. What does that row look like?
After read_range I can read the existing data. But every new customer means I open Sheets, scroll to the bottom, type date, plan, MRR, status. Four fields, every time. ['2026-04-14', 'Pro', 149.0, 'active'].
That four-field list is exactly what you pass to GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND. The action appends it after the last row in your range automatically — no scrolling, no finding the next empty row:
result = toolset.execute_action(
Action.GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND,
{"spreadsheet_id": sheet_id, "range": range_a1, "values": [values]}
)
print(result.get("updates", {}).get("updatedRows", 0))Why [values] with the extra brackets? The values are already a list.
The API expects a list of rows — each row is a list. [values] wraps your single row in the outer list. If you're appending one row: [["date", "plan", 149.0, "active"]]. If you're appending multiple rows: [[row1], [row2]]. Here's the full function:
def append_row(sheet_id: str, range_a1: str, values: list) -> dict:
result = toolset.execute_action(
Action.GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND,
{"spreadsheet_id": sheet_id, "range": range_a1, "values": [values]}
)
print(f"Appended row to {range_a1}")
return resultSo every new paying customer triggers append_row — date, plan, MRR, status — and the tracker updates itself. That's my Monday log entry gone.
Your MRR tracker just got its first automated row. Every paying customer from now on writes themselves in.
Read then append — the full Sheets read-write cycle. My MRR tracker is now a live Python data store.
Always call read_range before appending to confirm the sheet has data and your range is correct. Appending to the wrong range creates rows in the wrong place — harder to undo than a draft email.
GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND adds rows after the last existing row in the range.
toolset.execute_action(
Action.GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND,
{"spreadsheet_id": "1abc...", "range": "Sheet1!A:D", "values": [["2026-04-14", "Pro", 149.0, "active"]]}
)values is a list of rows. Each row is a list of cell values. One row: [[v1, v2, v3]]. Multiple rows: [[r1v1, r1v2], [r2v1, r2v2]].
values must be [[row]] — a list of lists. Passing [row] (a flat list) inserts each value as a separate column header.
Create a free account to get started. Paid plans unlock all tracks.