# Google Sheets

> Google Sheets automation via the Sheets API. Use when the user needs to read data from a Google Sheet, read rows from a spreadsheet, get all records from a worksheet, update a cell in a Google Sheet, update a range of cells, batch update multiple cells, append a row to a spreadsheet, list worksheets in a spreadsheet, delete rows from a worksheet, create or delete a worksheet, find and replace text in a range, or perform any programmatic Google Sheets operation. Triggers include "read from Google Sheet", "baca Google Sheet", "update cell", "update sel", "append row", "tambah baris", "batch update", "list worksheets", "daftar worksheet", "delete row", "hapus baris", "create worksheet", "buat worksheet", "delete worksheet", "hapus worksheet", "find replace", "cari ganti", "get spreadsheet data", "ambil data spreadsheet", "write to sheet", "tulis ke sheet", or any task requiring Google Sheets read/write access.

- Skill: `lazuardytech/google-sheets` (Agent Skill, multi-file: 22 files)
- Install (CLI): `npx skillmds@latest add lazuardytech/google-sheets`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lazuardytech/google-sheets/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- License: MIT
- Author: lazuardytech (https://skillmd.com/u/lazuardytech)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lazuardytech/google-sheets

---


## Setup Check

Before running any script, verify the skill is ready:
1. `credentials.json` exists in the working directory (or `--credentials <path>` is passed)
2. The target spreadsheet is shared with the service account `client_email`

If `credentials.json` is missing, tell the user:
> "This skill needs a Google service account key. Follow the setup guide in `SETUP.md` to create one from Google Cloud Console."

If a script returns `403 PERMISSION_DENIED`, tell the user:
> "The service account doesn't have access to this spreadsheet. Share it with the `client_email` in your `credentials.json`."

If a script returns `SpreadsheetNotFound`, tell the user:
> "Spreadsheet not found or not shared. Make sure the URL is correct and the sheet is shared with the service account."

## Credentials Safety

`credentials.json` contains a private key — treat it like a password.

When the user places `credentials.json` in a project directory, ALWAYS:
1. Check if `.gitignore` exists in that directory
2. Check if `credentials.json` is already listed — if not, add it immediately:
   ```
   # Google service account key
   credentials.json
   ```
3. Tell the user: "I've added `credentials.json` to `.gitignore` so it won't be committed."

If there is no `.gitignore`, create one with `credentials.json` in it.

If the user wants to store it outside the project, suggest:
- `~/.config/google/credentials.json` and pass `--credentials ~/.config/google/credentials.json`
- Or an environment variable path: `--credentials $GOOGLE_CREDENTIALS`

Never echo or print the contents of `credentials.json`. Reference it by path only.

# google-sheets

Google Sheets read/write automation via the Sheets API and `gspread`.
Run CLIs from this skill's directory: `python3 scripts/<name>.py`.

## Setup

1. Go to [Google Cloud Console](https://console.cloud.google.com/) → APIs & Services → Enable **Google Sheets API** and **Google Drive API**
2. Create a Service Account → Keys → Add Key → JSON → download as `credentials.json`
3. Share the target spreadsheet with the service account email (found in `credentials.json` as `client_email`)
4. Place `credentials.json` in the working directory, or pass `--credentials <path>`

## Available Scripts

### Read all rows

```bash
python3 scripts/read_rows.py --url <spreadsheet_url>
python3 scripts/read_rows.py --url <spreadsheet_url> --worksheet 1
```

Output: `{"status": "ok", "worksheet": "Sheet1", "count": 42, "rows": [{"col": "val", ...}, ...]}`

### List worksheets

```bash
python3 scripts/list_worksheets.py --url <spreadsheet_url>
```

Output: `{"status": "ok", "spreadsheet": "My Sheet", "worksheets": [{"index": 0, "title": "Sheet1", "rows": 1000, "cols": 26}]}`

### Update a single cell

```bash
python3 scripts/update_cell.py --url <url> --row 2 --col 3 --value "Hello"
```

Output: `{"status": "updated", "worksheet": "Sheet1", "row": 2, "col": 3, "value": "Hello"}`

### Update a range

```bash
python3 scripts/update_range.py --url <url> --range A2:C3 --values "[[1,2,3],[4,5,6]]"
```

Output: `{"status": "updated", "worksheet": "Sheet1", "range": "A2:C3", "rows": 2}`

### Batch update multiple cells

```bash
python3 scripts/batch_update.py --url <url> --cell 2,1,Alice --cell 2,2,30
```

Output: `{"status": "updated", "worksheet": "Sheet1", "count": 2, "cells": [...]}`

### Append a row

```bash
python3 scripts/append_row.py --url <url> --values "Alice,30,Engineer"
```

Output: `{"status": "appended", "worksheet": "Sheet1", "values": ["Alice", "30", "Engineer"]}`

### Delete rows

```bash
python3 scripts/delete_row.py --url <url> --row 5 --confirm
python3 scripts/delete_row.py --url <url> --start 2 --end 4 --confirm
```

Output: `{"deleted": true, "worksheet": "Sheet1", "rows": [5]}` (or `[2, 3, 4]` for a batch).
Destructive — requires `--confirm`. Rows are 1-indexed; batch delete spans `--start`..`--end` inclusive.

### Create worksheet

```bash
python3 scripts/create_worksheet.py --url <url> --title "Sheet2" --rows 100 --cols 20
```

Output: `{"created": true, "title": "Sheet2", "id": 1, "rows": 100, "cols": 20}`

### Delete worksheet

```bash
python3 scripts/delete_worksheet.py --url <url> --title "Sheet2" --confirm
python3 scripts/delete_worksheet.py --url <url> --index 1 --confirm
```

Output: `{"deleted": true, "title": "Sheet2"}`. Destructive — requires `--confirm`.
Target by `--title` or `--index`.

### Find & replace

```bash
python3 scripts/find_replace.py --url <url> --range A1:C10 --find "old" --replace "new" --confirm
python3 scripts/find_replace.py --url <url> --range A1:C10 --find "TODO" --match-case --confirm
python3 scripts/find_replace.py --url <url> --range A1:A1 --find "x" --entire-cell --replace "y" --confirm
python3 scripts/find_replace.py --url <url> --range A1:C10 --find "\d+" --replace "#" --regex --confirm
```

Output: `{"replaced": 3, "cells": ["A1", "B2", "C3"]}`. Destructive — requires `--confirm`.
Options: `--match-case` (case-sensitive), `--entire-cell` (match only when the whole cell equals `--find`), `--regex` (treat `--find` as a regex). Range uses A1 notation; target a specific sheet with `--worksheet <index>`.

## Script Conventions

- Output: JSON to stdout, diagnostics to stderr
- Exit codes: `0` success · `1` error (credentials missing, API error, bad input) · `2` not found · `3` missing `--confirm`
- All scripts accept `--credentials <path>` to override the default `credentials.json` location
- `--worksheet` is always a zero-based index (default: `0`)
- `--values` for `update_range` expects a JSON 2D array string
- `--values` for `append_row` expects a comma-separated string

## Important Notes

- The service account must have edit access to the spreadsheet (share via the `client_email` in `credentials.json`)
- `credentials.json` is sensitive — never commit it to version control
- Scripts use PEP 723 inline dependencies — run with `uv run` or install `gspread` and `google-auth` manually

