KhueApps
Home/Python/Programmatically Edit Google Sheets with Python Using gspread

Programmatically Edit Google Sheets with Python Using gspread

Last updated: October 06, 2025

Overview

Automate boring data updates, reporting, and simple ETL by editing Google Sheets from Python. This guide shows a concise, practical flow using gspread (a thin wrapper over the Google Sheets API) and the official client for advanced batch writes.

What you’ll learn:

  • Authenticate with a service account
  • Read ranges and write cells
  • Append rows reliably
  • Batch update values efficiently

Requirements

  • Python 3.8+
  • A Google account and a Google Sheet you can edit
  • Basic terminal usage

Quickstart

  1. Create a Google Cloud project and enable “Google Sheets API”.
  2. Create a Service Account and generate a JSON key.
  3. Share your target spreadsheet with the service account’s email (Editor).
  4. Install packages:
pip install gspread google-auth google-api-python-client
  1. Set environment variables:
# Absolute path to the downloaded service account JSON
export GOOGLE_APPLICATION_CREDENTIALS=/abs/path/service_account.json
# The spreadsheet ID (string between /d/ and /edit in the sheet URL)
export SHEET_ID=your_spreadsheet_id
  1. Run the minimal working example below.

Minimal Working Example (gspread)

This script writes a header, appends a row, then reads it back.

import os
import gspread
from google.oauth2.service_account import Credentials

# Scopes: spreadsheets read/write
SCOPE = ["https://www.googleapis.com/auth/spreadsheets"]

creds_path = os.environ["GOOGLE_APPLICATION_CREDENTIALS"]
spreadsheet_id = os.environ["SHEET_ID"]

creds = Credentials.from_service_account_file(creds_path, scopes=SCOPE)
client = gspread.authorize(creds)

# Open the spreadsheet and select the first worksheet
sh = client.open_by_key(spreadsheet_id)
ws = sh.sheet1  # or sh.worksheet("Sheet1")

# Write a header row (A1:B1) in one call
ws.update("A1:B1", [["Timestamp", "Value"]])

# Append a row; USER_ENTERED lets formulas and types be interpreted
ws.append_row(["=NOW()", 42], value_input_option="USER_ENTERED")

# Read a small range back into Python (list of lists)
rows = ws.get("A1:B5")
for r in rows:
    print(r)

Expected behavior:

  • A header is set in A1:B1.
  • A timestamp and value are appended in the next row.
  • The script prints the first few rows in A and B.

Setup Steps (detailed)

  1. In your Google Cloud project, enable the Google Sheets API.
  2. Create a Service Account and download its JSON key.
  3. In Google Sheets, share the spreadsheet with the service account email (Editor role).
  4. Install Python packages with pip.
  5. Set environment variables for the credentials path and spreadsheet ID.
  6. Run the example; verify new data appears in the sheet.

Common Tasks (snippets)

  • Update a rectangular range in one request:
ws.update("A2:C4", [[1, 2, 3], [4, 5, 6], [7, 8, 9]])
  • Read all values (avoid for very large sheets; prefer precise ranges):
all_values = ws.get_all_values()
  • Find a cell and update it:
cell = ws.find("Value")  # first match
ws.update_cell(cell.row, cell.col + 1, "updated")
  • Create a new worksheet if missing:
try:
    ws = sh.worksheet("Data")
except gspread.WorksheetNotFound:
    ws = sh.add_worksheet(title="Data", rows=1000, cols=26)
  • Append multiple rows efficiently (build a block and update once):
rows_to_add = [["=NOW()", i] for i in range(10)]
start_row = ws.row_count + 1  # current size; may not reflect used range
# Prefer calculating the next empty row by scanning a column:
next_row = len(list(filter(None, ws.col_values(1)))) + 1
end_row = next_row + len(rows_to_add) - 1
ws.update(f"A{next_row}:B{end_row}", rows_to_add, value_input_option="USER_ENTERED")

Advanced: Batch Value Updates with the Official Client

For many ranges at once, call the Sheets API directly.

from googleapiclient.discovery import build
service = build("sheets", "v4", credentials=creds)

body = {
    "valueInputOption": "USER_ENTERED",
    "data": [
        {
            "range": "Sheet1!C1:D2",
            "majorDimension": "ROWS",
            "values": [["Sum", "=SUM(B:B)"], ["Note", "ok"]],
        },
        {
            "range": "Sheet1!F1:F3",
            "values": [["A"], ["B"], ["C"]],
        },
    ],
}
service.spreadsheets().values().batchUpdate(
    spreadsheetId=spreadsheet_id, body=body
).execute()

Choosing an Auth Method

MethodUse whenNotes
Service AccountAutomation, CI, serversShare the sheet with the service account email. No interactive prompts.
OAuth user flowDesktop apps, personal useGrants access to your user Drive; use google-auth-oauthlib InstalledAppFlow.

Pitfalls and How to Avoid Them

  • Permission errors (403): share the sheet with the service account’s email, not your personal email.
  • Wrong scope: include the spreadsheets scope for read/write; otherwise updates fail.
  • Formula handling: use value_input_option="USER_ENTERED" so formulas like =NOW() compute.
  • Worksheet naming: ranges like Sheet 1!A1 need quoting if using the raw API; with gspread, use worksheet objects to avoid A1 quoting issues.
  • Row detection: row_count is worksheet size, not the last used row. To find the next data row, scan a key column.
  • Large reads: get_all_values can be slow; prefer precise ranges or batch reads.
  • Concurrency: concurrent writers may overwrite cells; coordinate via row-level appends or separate sheets.

Performance Notes

  • Prefer rectangular range updates (ws.update with a 2D list) instead of many single-cell updates.
  • Batch multiple ranges with spreadsheets().values().batchUpdate to reduce HTTP calls.
  • Read only what you need: narrow A1 ranges or batchGet multiple ranges.
  • Backoff and retry on 429/5xx; exponential backoff improves reliability under quota pressure.
  • Precompute data in Python, then send one write. Avoid read-modify-write loops for every cell.
  • Use USER_ENTERED for type inference and formulas; use RAW for exact values to avoid parsing overhead.

Tiny FAQ

  • How do I authenticate without a service account?
    • Use OAuth with google-auth-oauthlib’s InstalledAppFlow to obtain user credentials and store a token for subsequent runs.
  • How do I insert rows at a specific position?
    • With gspread: ws.insert_row(["a", "b"], index=2) to shift existing rows down.
  • Can I edit a protected range?
    • Only if your account (or service account) has permission to edit that protected range.
  • How do I get the spreadsheet ID?
    • It’s the long ID between /d/ and /edit in the spreadsheet URL.

Wrap-up

With gspread for simplicity and the official client for batch operations, you can reliably script Google Sheets edits from Python—ideal for scheduled reports, small ETL jobs, and automating everyday tasks.

Series: Automate boring tasks with Python

Python