The real problem
Why this is harder than it looks
The Google Sheets API is well-documented and the calls themselves are clean. The problems don't show up until you're running this for real users — and several of them are specific to Google's OAuth model in ways that catch teams off guard.
Google's access tokens expire after one hour. That's a shorter window than most OAuth providers, which means your refresh logic has to be proactive — not reactive. If an access token expires mid-operation and you haven't refreshed it in time, you get a silent 401 that looks like a permissions problem, not an expiry problem. More critically, Google's OAuth requires that every user who connects your app must authorize it through a Google consent screen that names your specific Google Cloud project — not Scalekit's, not a generic library. This means you must register your own OAuth credentials in Google Cloud Console before users can connect at all. There's no managed app shortcut here.
The scope model adds more surface area. Google Sheets access is governed by fine-grained OAuth scopes: read-only access, full read/write access, and Drive-level scope for file discovery are all separate. Request the wrong scope set and your agent fails with a 403 at runtime, not at setup time — which means the failure surfaces in production, not in testing. And if you need to expand scopes later because your agent's capabilities grew, users must re-authorize from scratch, since Google does not allow incremental scope grants for existing tokens.
On top of all this, any Google Cloud project that requests scopes covering user data must pass Google's app verification process before it can be used by more than 100 test users. If you're building an OAuth flow from scratch, that verification process becomes a prerequisite before you can even ship. Scalekit handles the OAuth infrastructure — token storage, refresh scheduling, revocation detection, and the credential wiring — so you can focus on what your agent actually does with the spreadsheet data.
Capabilities
What your agent can do with Google Sheets
Once connected, your agent has 4 pre-built tools covering the core Google Sheets API surface:
- Create spreadsheets: spin up new sheets with optional titles, initial sheet tabs, locale, and timezone
- Read cell values from a range: fetch just the data from any A1-notation range, with control over row vs. column orientation and value rendering
- Read full spreadsheet metadata: retrieve everything — sheet properties, formatting, themes, pixel sizes, and cell data in one call
- Write cell values: update a single cell or a multi-row/column range atomically, with control over how input values are interpreted
Setup context
What we're building
This guide connects a data reporting agent to Google Sheets — letting it read pipeline data, write summaries back to shared sheets, and create new tracking spreadsheets on behalf of each user.
🤖
Example agent
Reporting assistant reading and writing Google Sheets on behalf of each team member
🔐
Auth model
B2B SaaS — each user connects their own Google account. identifier = your user ID
Setup
1 Setup: One SDK, One credential
Install the Scalekit SDK. The only credential your application manages is the Scalekit API key — no Google secrets, no user tokens, nothing belonging to your customers.
pip install scalekit-sdk-python
import scalekit.client
import os
from dotenv import load_dotenv
load_dotenv()
scalekit = scalekit.client.ScalekitClient(
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
env_url=os.getenv("SCALEKIT_ENV_URL"),
)
actions = scalekit.actions
Connected Accounts
2 Per-User Auth: Creating connected accounts
Each user gets their own Connected Account, giving them a dedicated auth context. The identifier is any unique string from your system — a UUID, email, whatever you use internally.
response = actions.get_or_create_connected_account(
connection_name="google_sheets",
identifier="user_gs_456" # your internal user ID
)
connected_account = response.connected_account
print(f"Status: {connected_account.status}")
# Status: PENDING — user hasn't authorized yet
This call is idempotent — safe to call on every session start. Returns the existing account if one already exists.
Authorization Flow
3 The authorization flow
The user authorizes your agent once through Google's native consent screen. Scalekit generates the OAuth URL with the correct scopes, PKCE challenge, and redirect handling pre-configured. After approval, you never see the token.
if connected_account.status != "ACTIVE":
link = actions.get_authorization_link(
connection_name="google_sheets",
identifier="user_gs_456"
)
# Redirect user → Google consent screen
# Scalekit captures the token on callback
return redirect(link.link)
Token management is automatic
After the user approves, Scalekit stores encrypted tokens and the connected account moves to ACTIVE. Google
access tokens expire every hour — Scalekit refreshes them proactively before expiry. If a user revokes
access from their Google account security settings, the account moves to REVOKED — no silent failures.
Check account.status before critical write operations.
Bring Your Own Credentials — required for production
Register your own Google Cloud OAuth client and supply your Client ID and Secret in the Scalekit dashboard.
This ensures users see your app name on the Google consent screen — Google does not allow a third party to
appear on the consent UI on your behalf. Token management stays fully handled.
Already have credentials?
Calling Google Sheets
4 Calling Google Sheets: What your agent writes
With the connected account active, your agent calls Sheets actions using execute_tool. Name the tool, pass parameters. Scalekit handles token retrieval, request construction, and response parsing.
Read values from a range
Fetch cell data from a specific range using A1 notation. Use googlesheets_get_values when you only need the data — not formatting or metadata.
result = actions.execute_tool(
identifier="user_gs_456",
tool_name="googlesheets_get_values",
tool_input={
"spreadsheet_id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"range": "Sheet1!A1:D20",
"major_dimension": "ROWS"
}
)
# { "range": "Sheet1!A1:D20", "values": [["Q1", "Revenue", ...], ...] }
Write values to a range
Update one or more cells by passing a 2D array of values. value_input_option controls whether strings are interpreted as formulas or stored as raw text.
result = actions.execute_tool(
identifier="user_gs_456",
tool_name="googlesheets_update_values",
tool_input={
"spreadsheet_id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"range": "Summary!B2:D4",
"value_input_option": "USER_ENTERED",
"values": [
["Q1 Closed Won", 142000, "=B2*0.15"],
["Q2 Closed Won", 187500, "=B3*0.15"],
["Q3 Forecast", 210000, "=B4*0.15"]
]
}
)
Create a new spreadsheet
Spin up a fresh spreadsheet with an optional title and initial sheet configuration. Returns the new spreadsheet ID for subsequent read/write calls.
result = actions.execute_tool(
identifier="user_gs_456",
tool_name="googlesheets_create_spreadsheet",
tool_input={
"title": "Q3 Pipeline Tracker — Auto-generated",
"locale": "en_US",
"time_zone": "America/New_York",
"sheets": [
{"properties": {"title": "Opportunities"}},
{"properties": {"title": "Summary"}}
]
}
)
# { "spreadsheetId": "1XkL...", "spreadsheetUrl": "https://docs.google.com/..." }
Read full spreadsheet metadata
Use googlesheets_read_spreadsheet when your agent needs to understand a sheet's structure — tabs, column types, formatting — before deciding how to write to it.
result = actions.execute_tool(
identifier="user_gs_456",
tool_name="googlesheets_read_spreadsheet",
tool_input={
"spreadsheet_id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"include_grid_data": False # metadata only, skip cell data for speed
}
)
# Returns sheet titles, column counts, frozen rows, tab colors, themes
Framework wiring
5 Wiring into your agent framework
Scalekit integrates directly with LangChain. The agent decides what to read or write; Scalekit handles auth on every invocation. No token plumbing in your agent logic.
from langchain_anthropic import ChatAnthropic
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from scalekit.langchain import get_tools
gs_tools = get_tools(
connection_name="google_sheets",
identifier="user_gs_456"
)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a data reporting assistant. Use the available tools to read and write Google Sheets data."),
MessagesPlaceholder("chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"),
])
agent = create_tool_calling_agent(ChatAnthropic(model="claude-sonnet-4-6"), gs_tools, prompt)
result = AgentExecutor(agent=agent, tools=gs_tools).invoke({
"input": "Read the Q2 pipeline data from the Opportunities sheet and write a summary to the Summary tab"
})
Other frameworks supported
Tool reference
Google Sheets tools
4 pre-built tools. Your agent calls tools by name — no API wrappers to write.
googlesheets_create_spreadsheet
Create a new spreadsheet with optional title, initial sheet tabs, locale, and timezone. Returns the new spreadsheet ID and URL.
googlesheets_read_spreadsheet
Return the full spreadsheet: metadata, sheet properties, formatting, themes, and optionally cell data. Use when your agent needs structural context before writing.
Return only cell values from a specific A1-notation range. Supports row vs. column dimension and value rendering options. Use this over googlesheets_read_spreadsheet when you only need the data.
googlesheets_update_values
Write a 2D array of values to a specific range. Supports USER_ENTERED (formulas interpreted) or RAW (stored as-is). Optionally returns updated values in the response.
Connector notes
Google Sheets-specific behavior
Google Cloud app verification gates production use
Google requires any app requesting scopes that access user data to pass a verification process before it
can be used by more than 100 accounts. During development, add individual test users to bypass this limit.
Before launching to real users, submit your app for verification in the Google Cloud Console under OAuth
consent screen settings. Verification typically takes several days — plan for this in your production
timeline.
Scope selection determines what your agent can do
Google Sheets uses two primary scopes: spreadsheets.readonly for read-only access and spreadsheets for
full read/write. If your agent creates spreadsheets or writes data, you need the full scope. Configure the
scope set in the Scalekit dashboard when setting up your connection — users will need to re-authorize if
you add scopes later, as Google does not support incremental scope grants.
Spreadsheet IDs must be obtained at runtime
Google Sheets file IDs are user-specific and cannot be hardcoded. For agents working across multiple
users' sheets, retrieve file IDs at runtime via the Google Drive API, or ask the user to provide the
spreadsheet ID to your application. The Sheets API itself has no "list all sheets" endpoint — Drive scope
is required for file discovery.
Infrastructure decision
Why not build this yourself
The Google OAuth flow is documented. Token storage isn't technically hard. But here's what you're actually signing up for with Sheets specifically:
PROBLEM 01
One-hour token expiry — the shortest window of any major OAuth provider — requires proactive refresh scheduling, not reactive 401 handling
PROBLEM 02
Mandatory Bring Your Own Credentials: Google requires your Cloud project on the consent screen, so there is no managed app to shortcut setup
PROBLEM 03
No incremental scope grants — adding a new capability to your agent forces every connected user to re-authorize from scratch
PROBLEM 04
App verification gating: production use beyond 100 accounts requires passing Google's review process before you can ship
That's one connector. Your agent product will eventually need Salesforce, Slack, Notion, HubSpot, and whatever else your customers ask for. Each has its own OAuth quirks and failure modes.
Scalekit maintains every connector. You maintain none of them.