Sheets¶
Sheets ¶
Sheets(auth: GoogleAuth, *, cache: bool = False, cache_ttl: float | None = None, cache_max_entries: int | None = None, dataframe_backend: Literal['pandas', 'polars'] = 'pandas', batch_cell_limit: int | None = 50000)
High-level Google Sheets client.
Inspired by gspread's simple API.
Example
auth = GoogleAuth() auth.authenticate()
sheets = Sheets(auth)
Open by title (like gspread!)¶
doc = sheets.open("My Spreadsheet")
Or by key/url¶
doc = sheets.open_by_key("abc123...") doc = sheets.open_by_url("https://docs.google.com/spreadsheets/...")
Work with worksheets¶
ws = doc.sheet1 data = ws.get_all_values() ws.update("A1", [["Hello", "World"]])
Initialize Sheets client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
auth
|
GoogleAuth
|
GoogleAuth instance with valid credentials |
required |
cache
|
bool
|
Cache reads made through the engine features (upsert, iter_rows, read_as, ...); invalidated by this client's own writes, not by other processes. See clear_cache(). |
False
|
cache_ttl
|
float | None
|
Seconds before a cached read expires (enables the cache) |
None
|
cache_max_entries
|
int | None
|
LRU size bound (enables the cache) |
None
|
dataframe_backend
|
Literal['pandas', 'polars']
|
"pandas" or "polars" for read/write_dataframe |
'pandas'
|
batch_cell_limit
|
int | None
|
Split big writes into requests of at most this many cells (None disables chunking) |
50000
|
open ¶
open(title: str) -> Spreadsheet
Open a spreadsheet by title.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
Spreadsheet title |
required |
Returns:
| Type | Description |
|---|---|
Spreadsheet
|
Spreadsheet object |
Raises:
| Type | Description |
|---|---|
ValueError
|
If not found |
open_by_key ¶
open_by_key(key: str) -> Spreadsheet
Open a spreadsheet by key (ID).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Spreadsheet ID |
required |
Returns:
| Type | Description |
|---|---|
Spreadsheet
|
Spreadsheet object |
open_by_url ¶
open_by_url(url: str) -> Spreadsheet
Open a spreadsheet by URL.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
Full Google Sheets URL |
required |
Returns:
| Type | Description |
|---|---|
Spreadsheet
|
Spreadsheet object |
create ¶
create(title: str) -> Spreadsheet
Create a new spreadsheet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
Spreadsheet title |
required |
Returns:
| Type | Description |
|---|---|
Spreadsheet
|
Created Spreadsheet |
copy ¶
copy(spreadsheet_id: str, title: str | None = None, copy_permissions: bool = False, folder_id: str | None = None) -> Spreadsheet
Copy a spreadsheet (optionally with its sharing, except ownership).
list_spreadsheets ¶
list_spreadsheets(max_results: int | None = 100) -> list[dict]
List all spreadsheets accessible to the user.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_results
|
int | None
|
Maximum spreadsheets to return (None = all) |
100
|
Returns:
| Type | Description |
|---|---|
list[dict]
|
List of {id, name} dicts |
get_values ¶
get_values(spreadsheet_id: str, range: str, value_render: ValueRender = 'FORMATTED_VALUE') -> list[list[Any]]
Get values from a range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spreadsheet_id
|
str
|
Spreadsheet ID |
required |
range
|
str
|
A1 range including the sheet, e.g. "'Sheet 1'!A1:C10" |
required |
value_render
|
ValueRender
|
FORMATTED_VALUE (as displayed), UNFORMATTED_VALUE (numbers as numbers) or FORMULA |
'FORMATTED_VALUE'
|
batch_get_values ¶
batch_get_values(spreadsheet_id: str, ranges: list[str], value_render: ValueRender = 'FORMATTED_VALUE') -> list[list[list[Any]]]
Get several ranges in one request; results follow the order of ranges.
update_values ¶
update_values(spreadsheet_id: str, range: str, values: list[list[Any]], value_input: ValueInput = 'USER_ENTERED') -> dict
Update values in a range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value_input
|
ValueInput
|
USER_ENTERED parses values as if typed into the UI ("=SUM(A1:A3)" becomes a formula, "2026-01-01" a date). Use RAW for untrusted data so text starting with "=" stays text. |
'USER_ENTERED'
|
append_values ¶
append_values(spreadsheet_id: str, range: str, values: list[list[Any]], value_input: ValueInput = 'USER_ENTERED') -> dict
Append rows after the table found in range (see update_values for value_input).
clear_values ¶
clear_values(spreadsheet_id: str, range: str) -> dict
Clear values from a range (formatting is kept).
batch_update ¶
batch_update(spreadsheet_id: str, data: list[dict], value_input: ValueInput = 'USER_ENTERED') -> dict
Batch update multiple ranges.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spreadsheet_id
|
str
|
Spreadsheet ID |
required |
data
|
list[dict]
|
List of {range, values} dicts |
required |
value_input
|
ValueInput
|
See update_values |
'USER_ENTERED'
|
batch_requests ¶
batch_requests(spreadsheet_id: str, requests: list[dict]) -> list[dict]
Run raw spreadsheets.batchUpdate requests (formatting, structure, ...).
Use for anything this client doesn't wrap; see the Sheets API reference for request types. Returns one reply per request.
format_range ¶
format_range(spreadsheet_id: str, sheet_id: int, range: str, cell_format: dict) -> None
Apply a cell format to a range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range
|
str
|
A1 range without the sheet, e.g. "A1:Z1" |
required |
cell_format
|
dict
|
A Sheets CellFormat, e.g. {"textFormat": {"bold": True}, "numberFormat": {"type": "CURRENCY", "pattern": "$#,##0.00"}}. Only the top-level keys given are changed. |
required |
freeze ¶
freeze(spreadsheet_id: str, sheet_id: int, rows: int | None = None, cols: int | None = None) -> None
Freeze the first rows rows and/or cols columns (0 unfreezes).
protect_range ¶
protect_range(spreadsheet_id: str, sheet_id: int, range: str | None = None, description: str | None = None, editors: list[str] | None = None, warning_only: bool = False) -> int
Protect a range (or the whole sheet when range is None).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
editors
|
list[str] | None
|
Emails that can still edit (default: only the owner) |
None
|
warning_only
|
bool
|
Let everyone edit but show a warning |
False
|
Returns:
| Type | Description |
|---|---|
int
|
The protected range ID |
find_replace ¶
find_replace(spreadsheet_id: str, find: str, replacement: str, sheet_id: int | None = None, match_case: bool = False, match_entire_cell: bool = False, regex: bool = False) -> int
Find and replace text, in one sheet or all of them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
regex
|
bool
|
Treat |
False
|
Returns:
| Type | Description |
|---|---|
int
|
Number of occurrences changed |
add_worksheet ¶
add_worksheet(spreadsheet_id: str, title: str, rows: int = 1000, cols: int = 26) -> Worksheet
Add a worksheet to a spreadsheet.
rename_worksheet ¶
rename_worksheet(spreadsheet_id: str, sheet_id: int, title: str) -> None
Rename a worksheet.
duplicate_worksheet ¶
duplicate_worksheet(spreadsheet_id: str, sheet_id: int, new_title: str | None = None, index: int | None = None) -> Worksheet
Copy a worksheet within the spreadsheet (default name: "Copy of ...").
delete_worksheet ¶
delete_worksheet(spreadsheet_id: str, sheet_id: int) -> bool
Delete a worksheet.
Returns:
| Type | Description |
|---|---|
bool
|
True if deleted, False if the spreadsheet doesn't exist. Other |
bool
|
failures raise, e.g. ValidationError-like 400s when deleting the |
bool
|
last remaining sheet. |
share ¶
share(spreadsheet_id: str, email: str, role: str = 'reader', notify: bool = True) -> bool
Share a spreadsheet.
Returns:
| Type | Description |
|---|---|
bool
|
True if shared, False if the spreadsheet doesn't exist. Other |
bool
|
failures raise. |
Spreadsheet
dataclass
¶
Spreadsheet(id: str, title: str, url: str, locale: str = 'en_US', time_zone: str = 'America/New_York', worksheets: list[Worksheet] = list(), _sheets: Optional[Sheets] = None)
A Google Spreadsheet.
Contains multiple worksheets (tabs).
sheet1
property
¶
sheet1: Worksheet | None
Get the first worksheet (convenience property like gspread).
add_worksheet ¶
add_worksheet(title: str, rows: int = 1000, cols: int = 26) -> Worksheet
Add a new worksheet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
Worksheet title |
required |
rows
|
int
|
Number of rows |
1000
|
cols
|
int
|
Number of columns |
26
|
Returns:
| Type | Description |
|---|---|
Worksheet
|
Created Worksheet |
duplicate_worksheet ¶
duplicate_worksheet(worksheet: Worksheet, new_title: str | None = None) -> Worksheet
Copy a worksheet (default name: "Copy of ...").
share ¶
share(email: str, role: str = 'reader', notify: bool = True) -> bool
Share spreadsheet with someone.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
email
|
str
|
Email to share with |
required |
role
|
str
|
Permission role (reader, writer) |
'reader'
|
notify
|
bool
|
Send notification email |
True
|
worksheet_or_create ¶
worksheet_or_create(title: str, rows: int = 100, cols: int = 26) -> Worksheet
The tab titled title, created if missing.
update_locale ¶
update_locale(locale: str) -> None
Locale used for formats and functions, e.g. "es_AR".
update_timezone ¶
update_timezone(timezone: str) -> None
Time zone, e.g. "America/Argentina/Buenos_Aires".
export ¶
export(format: str = 'pdf') -> bytes
The whole spreadsheet as bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
format
|
str
|
"pdf", "xlsx", "ods", "csv", "tsv" (first sheet only) or "html" (zip) |
'pdf'
|
list_named_ranges ¶
list_named_ranges() -> list[dict[str, Any]]
Named ranges (define them with Worksheet.define_named_range).
set_developer_metadata ¶
set_developer_metadata(key: str, value: str, visibility: str = 'DOCUMENT') -> None
Attach a hidden key/value to the spreadsheet.
list_developer_metadata ¶
list_developer_metadata() -> list[dict[str, Any]]
Developer metadata of the spreadsheet and its tabs.
delete_developer_metadata ¶
delete_developer_metadata(key: str) -> None
Remove developer metadata by key.
remove_permission ¶
remove_permission(value: str, role: str = 'any') -> list[str]
Revoke access of an email or domain (optionally only for role); returns removed IDs.
Worksheet
dataclass
¶
Worksheet(id: int, title: str, index: int, row_count: int = 1000, column_count: int = 26, _spreadsheet: Optional[Spreadsheet] = None)
A worksheet (tab) within a spreadsheet.
Provides methods for reading and writing cell data. Ranges are A1 notation without the sheet title ("A1:C10"); the title is added and quoted for you, so tabs named "2024" or "Pablo's" work.
get ¶
get(range: str = 'A1', value_render: ValueRender = 'FORMATTED_VALUE') -> list[list[Any]]
Get values from a range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range
|
str
|
A1 notation (e.g., "A1:B10", "A:C", "1:5") |
'A1'
|
value_render
|
ValueRender
|
FORMATTED_VALUE, UNFORMATTED_VALUE or FORMULA |
'FORMATTED_VALUE'
|
Returns:
| Type | Description |
|---|---|
list[list[Any]]
|
2D list of values |
batch_get ¶
batch_get(ranges: list[str]) -> list[list[list[Any]]]
Get several ranges in one request, in the order given.
get_all_records ¶
get_all_records(head: int = 1) -> list[dict]
Get all rows as list of dicts using row as headers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
head
|
int
|
Row number to use as headers (1-indexed) |
1
|
Returns:
| Type | Description |
|---|---|
list[dict]
|
List of dicts with header keys |
update ¶
update(range: str, values: list[list[Any]], value_input: ValueInput = 'USER_ENTERED') -> dict
Update a range with values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range
|
str
|
A1 notation (a single cell writes from there down/right) |
required |
values
|
list[list[Any]]
|
2D list of values |
required |
value_input
|
ValueInput
|
USER_ENTERED (parse like the UI: formulas, dates) or RAW (store as-is; use for untrusted text) |
'USER_ENTERED'
|
Returns:
| Type | Description |
|---|---|
dict
|
Update response |
batch_update ¶
batch_update(data: list[dict[str, Any]], value_input: ValueInput = 'USER_ENTERED') -> dict
Write several ranges of this sheet in one request: [{"range": "A1:B2", "values": [...]}].
append_row ¶
append_row(values: list[Any], value_input: ValueInput = 'USER_ENTERED') -> dict
Append a row to the end of the worksheet.
append_rows ¶
append_rows(rows: list[list[Any]], value_input: ValueInput = 'USER_ENTERED') -> dict
Append multiple rows.
clear ¶
clear(range: str | None = None) -> dict
Clear values from a range or entire worksheet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range
|
str | None
|
A1 notation (default: entire sheet) |
None
|
format ¶
format(range: str | list[str], cell_format: dict | CellFormat) -> None
Format one or more ranges with a CellFormat object or a raw API dict.
Example
ws.format("A1:Z1", CellFormat(text_format=TextFormat(bold=True))) ws.format("B2:B100", {"numberFormat": {"type": "CURRENCY", "pattern": "$#,##0.00"}})
freeze ¶
freeze(rows: int | None = None, cols: int | None = None) -> None
Freeze header rows and/or columns (0 unfreezes).
protect ¶
protect(range: str | None = None, description: str | None = None, editors: list[str] | None = None, warning_only: bool = False) -> int
Protect a range, or the whole sheet. Returns the protected range ID.
replace ¶
replace(find: str, replacement: str, match_case: bool = False, match_entire_cell: bool = False, regex: bool = False) -> int
Find and replace in this worksheet (server-side).
Returns:
| Type | Description |
|---|---|
int
|
Number of occurrences changed |
find ¶
find(query: str | Pattern[str]) -> tuple[int, int] | None
Find the first cell equal to query, or matching it if it's a regex.
Example
ws.find("Total") ws.find(re.compile(r"\d{3}-\d{4}"))
Returns:
| Type | Description |
|---|---|
tuple[int, int] | None
|
(row, col) tuple (1-indexed) or None |
findall ¶
findall(query: str | Pattern[str]) -> list[tuple[int, int]]
Find all cells equal to query, or matching it if it's a regex.
iter_rows ¶
iter_rows(page_size: int = 1000, skiprows: int = 0) -> Iterator[list[str]]
Yield rows lazily, reading page_size rows per request (big sheets).
iter_records ¶
iter_records(page_size: int = 1000) -> Iterator[dict[str, str]]
Yield rows as {header: value} dicts, lazily (header in row 1).
update_row ¶
update_row(row: int, values: list[Any], start_column: int | None = None) -> None
Write values into row starting at start_column (1-based, default A).
insert ¶
insert(values: list[list[Any]], row: int | None = None, value_input: ValueInput = 'USER_ENTERED') -> Any
Insert rows at row (1-based), shifting the existing ones down; append when None.
GSpreadManager's insert(fila=...) appended after the table instead: it sent values.append with a range starting at that row, which the API treats as "find the table there and add after it".
import_csv ¶
import_csv(source: Any, *, clear: bool = True, delimiter: str = ',') -> Any
Load a CSV (path or file object) from A1, clearing the sheet first unless clear=False.
upsert ¶
upsert(rows: list[dict[str, Any]] | list[list[Any]], key: str) -> dict[str, int]
Update rows whose key column matches and append the rest.
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
{"updated": n, "appended": m} |
update_where ¶
update_where(where: Where, updates: dict[str, Any]) -> int
Set updates on rows matching where ({column: value} or a predicate).
delete_where ¶
delete_where(where: Where) -> int
Delete rows matching where; returns how many were deleted.
rows_where_column_equals ¶
rows_where_column_equals(column: int, value: Any) -> list[tuple[int, list[str]]]
(row number, row) for rows whose column (0-based, as in GSpreadManager) equals value.
row_with_empty_in_column ¶
row_with_empty_in_column(column_letter: str) -> tuple[list[Any] | None, int | None]
First row with an empty cell in column_letter: (row values, number) or (None, None).
ensure_schema ¶
ensure_schema(model: type, *, create: bool = True, strict: bool = False) -> dict[str, Any]
Check (or write, if the sheet is empty) the header row against model's fields.
read_as ¶
read_as(model: type, skiprows: int = 0) -> list[Any]
Rows as model instances (dataclass or Pydantic), with values converted to field types.
write_models ¶
write_models(models: list[Any], include_header: bool = True, clear: bool = True) -> Any
Write model instances from A1 (header from the fields).
upsert_models ¶
upsert_models(models: list[Any], key: str) -> dict[str, int]
upsert() for model instances.
format_header ¶
format_header(range: str = '1:1', background_hex: str | None = '#D9EAD3') -> None
Bold header with a background color.
set_background ¶
set_background(range: str | list[str], color: Color) -> None
Background color for one or more ranges.
set_text_format ¶
set_text_format(range: str | list[str], *, bold: bool | None = None, italic: bool | None = None, font_size: int | None = None, color: Color | None = None) -> None
Bold, italic, size and/or color of the text.
set_number_format ¶
set_number_format(range: str | list[str], pattern: str, number_type: str = 'NUMBER') -> None
Number format, e.g. ("C2:C100", "#,##0.00", "CURRENCY").
merge ¶
merge(range: str, merge_type: str = 'MERGE_ALL') -> None
Merge cells (MERGE_ALL, MERGE_ROWS or MERGE_COLUMNS).
add_dropdown ¶
add_dropdown(range: str, values: list[Any], strict: bool = True) -> None
Dropdown with values; strict rejects anything else.
set_data_validation ¶
set_data_validation(range: str, condition_type: str, values: list[Any] | None = None, strict: bool = True, show_custom_ui: bool = True) -> None
Any validation rule, e.g. ("B2:B100", "NUMBER_GREATER", [0]).
add_conditional_format ¶
add_conditional_format(range: str, condition_type: str, values: list[Any], cell_format: CellFormat, index: int = 0) -> None
Format cells meeting a condition, e.g. ("C2:C100", "NUMBER_LESS", [0], red).
insert_rows ¶
insert_rows(at: int, number: int = 1, inherit_from_before: bool = False) -> None
Insert blank rows before row at (1-based).
insert_cols ¶
insert_cols(at: int, number: int = 1, inherit_from_before: bool = False) -> None
Insert blank columns before column at (1-based).
delete_rows ¶
delete_rows(start: int, end: int | None = None) -> None
Delete rows start..end (1-based, inclusive).
delete_cols ¶
delete_cols(start: int, end: int | None = None) -> None
Delete columns start..end (1-based, inclusive).
resize_rows ¶
resize_rows(start: int, end: int, pixels: int) -> None
Row height for rows start..end.
resize_cols ¶
resize_cols(start: int, end: int, pixels: int) -> None
Column width for columns start..end.
sort_range ¶
sort_range(range: str, *specs: tuple[int, str]) -> None
Sort by columns: sort_range("A2:C100", (1, "asc"), (3, "desc")).
set_basic_filter ¶
set_basic_filter(range: str | None = None) -> None
Turn on the filter (whole sheet by default).
define_named_range ¶
define_named_range(name: str, range: str) -> None
Name a range of this sheet (see Spreadsheet.list_named_ranges).
list_protected_ranges ¶
list_protected_ranges() -> list[dict[str, Any]]
Protected ranges of this sheet.
delete_protected_range ¶
delete_protected_range(protected_range_id: str | int) -> None
Remove a protection (ID from protect() or list_protected_ranges()).
set_developer_metadata ¶
set_developer_metadata(key: str, value: str, visibility: str = 'DOCUMENT') -> None
Attach a key/value to this sheet (hidden from users).
add_chart ¶
add_chart(chart_type: str, domain: str, series: list[str], *, title: str | None = None, anchor_cell: str = 'A1', legend: str = 'BOTTOM_LEGEND') -> int | None
Embedded chart (COLUMN, BAR, LINE, AREA, PIE, SCATTER, ...); returns its ID.
add_pivot_table ¶
add_pivot_table(source: str, anchor_cell: str, *, rows: list[int], values: list[tuple[int, str]], columns: list[int] | None = None) -> None
Pivot table at anchor_cell over source; values are (column, "SUM"|"COUNTA"|...).
set_banding ¶
set_banding(range: str, *, first_color: Color, second_color: Color, header_color: Color | None = None) -> int | None
Alternating row colors; returns the banding ID.
read_dataframe ¶
read_dataframe(skiprows: int = 0, *, drop_empty_rows: bool = False, drop_empty_cols: bool = False, index_col: str | None = None) -> Any
The sheet as a DataFrame of the client's backend (Sheets(dataframe_backend=...)).
Needs gsuite-sdk[pandas] or gsuite-sdk[polars].
write_dataframe ¶
write_dataframe(df: Any, include_header: bool = True, clear: bool = True, *, start_cell: str | None = None, include_index: bool = False) -> Any
Write a pandas or polars DataFrame (from A1 or start_cell).
to_dataframe ¶
to_dataframe(header_row: int = 1) -> pd.DataFrame
Read the worksheet into a pandas DataFrame (needs gsuite-sdk[pandas]).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
header_row
|
int
|
Row with column names (1-indexed); rows above it are skipped |
1
|
Values come back as displayed text; convert types in pandas as needed.
from_dataframe ¶
from_dataframe(df: DataFrame, start: str = 'A1', include_header: bool = True, include_index: bool = False, clear: bool = False, value_input: ValueInput = 'USER_ENTERED') -> dict
Write a pandas DataFrame starting at start.
NaN/None become empty cells and dates are written as ISO strings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
clear
|
bool
|
Clear the worksheet first (otherwise old cells outside the new data remain) |
False
|
AsyncSheets ¶
AsyncSheets(auth: Any, *, timeout: float | None = None, batch_cell_limit: int | None = 50000, transport: Any = None)
Entry point: open, create, copy and delete spreadsheets.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
auth
|
Any
|
GoogleAuth (OAuth or service account) or google-auth credentials |
required |
timeout
|
float | None
|
Seconds per request (default: GSUITE_REQUEST_TIMEOUT) |
None
|
batch_cell_limit
|
int | None
|
Split big writes into requests of at most this many cells |
50000
|
transport
|
Any
|
httpx transport (tests) |
None
|
create
async
¶
create(title: str, folder_id: str | None = None) -> AsyncSpreadsheet
Create a spreadsheet.
copy
async
¶
copy(spreadsheet_id: str, title: str | None = None, copy_permissions: bool = False, folder_id: str | None = None) -> AsyncSpreadsheet
Copy a spreadsheet (optionally with its sharing, except ownership).
list_spreadsheets
async
¶
list_spreadsheets(title: str | None = None) -> list[dict[str, Any]]
Spreadsheets visible to the user ({id, name}).
AsyncWorksheet ¶
AsyncWorksheet(spreadsheet: AsyncSpreadsheet, title: str, sheet_id: int)
A tab. Values, streaming, tables and typed rows, async.