Google Suite
Unified Python SDK for Google Workspace APIs with Clean Architecture.
Features
- 🔐 Unified Auth - Single OAuth flow for all Google APIs
- 📧 Gmail - Send, receive, search, labels, attachments
- 📅 Calendar - Events, calendars, scheduling
- 📁 Drive - Files, folders, sharing, upload/download
- 📊 Sheets - Read/write, upsert, typed rows, validation, conditional formats, charts, pivots, export (engine from GSpreadManager)
- ✅ Tasks - Task lists, tasks, subtasks, due dates, complete/reopen
- 👥 Contacts - List, search, create, update and delete contacts (People API)
- 🔎 Search Console (read-only) - Sites, search analytics, URL inspection, sitemaps
- 📈 Google Analytics 4 (read-only) - Account summaries, properties, reports, realtime reports
- 🏷️ Tag Manager (read-only) - Accounts, containers, workspaces, tags, triggers, variables, versions
- 🚀 REST API - Single FastAPI gateway for all services
- 💻 CLI - Unified command-line interface
Installation
pip install gsuite-sdk
Optional extras:
pip install gsuite-sdk[cloudrun] # Google Cloud Secret Manager support
pip install gsuite-sdk[all] # All optional dependencies (FastAPI, CLI)
Quick Start
Prerequisites
You need OAuth credentials from Google Cloud Console. See Getting Credentials for a step-by-step guide.
Authentication
from gsuite_core import GoogleAuth
# OAuth (interactive, opens browser)
auth = GoogleAuth()
auth.authenticate()
# Or with service account
auth = GoogleAuth.from_service_account("service-account.json")
Gmail
from gsuite_gmail import Gmail, query
gmail = Gmail(auth)
# Get unread messages with fluent API
for message in gmail.get_unread():
print(f"{message.sender}: {message.subject}")
message.mark_as_read().archive() # Fluent chaining!
# Send email with signature
gmail.send(
to=["[email protected]"],
subject="Hello",
body="World",
signature=True, # Appends your Gmail signature
)
# Search with query builder (inspired by simplegmail)
messages = gmail.search(
query.newer_than(days=7) & query.has_attachment() & query.from_("[email protected]")
)
# Or use construct_query for dict-style
messages = gmail.search(query.construct_query(
unread=True,
newer_than=(7, "day"),
labels=["Work"],
))
Drive
from gsuite_drive import Drive
drive = Drive(auth)
# List files
for file in drive.list_files():
print(f"{file.name} ({file.mime_type})")
# Upload
uploaded = drive.upload("document.pdf")
print(f"Uploaded: {uploaded.web_view_link}")
# Download
file = drive.get("file_id")
file.download("local_copy.pdf")
# Create folder and upload into it
folder = drive.create_folder("My Folder")
drive.upload("file.txt", parent_id=folder.id)
Calendar
from gsuite_calendar import Calendar
calendar = Calendar(auth)
# Get upcoming events
events = calendar.get_upcoming(days=7)
for event in events:
print(f"{event.start}: {event.summary}")
# Create event
calendar.create_event(
summary="Meeting",
start="2026-01-30T10:00:00",
end="2026-01-30T11:00:00",
)
Sheets
from gsuite_sheets import Sheets
sheets = Sheets(auth)
# Open spreadsheet by title or URL
sheet = sheets.open("My Spreadsheet")
# Or by key
sheet = sheets.open_by_key("1A2B3C...")
# Or by URL
sheet = sheets.open_by_url("https://docs.google.com/spreadsheets/d/...")
# Read values
values = sheet.get_values("Sheet1!A1:D10")
print(values) # [[row1], [row2], ...]
# Write values
sheet.update_values("Sheet1!A1", [["Name", "Email"], ["John", "[email protected]"]])
# Append rows
sheet.append_values("Sheet1", [["Jane", "[email protected]"]])
# Batch operations
sheet.batch_update([
{"range": "Sheet1!A1", "values": [["Header1", "Header2"]]},
{"range": "Sheet1!A2", "values": [["Data1", "Data2"]]},
])
# Create new spreadsheet
new_sheet = sheets.create("Budget 2026")
print(f"Created: {new_sheet.url}")
# List all spreadsheets
for spreadsheet in sheets.list_spreadsheets():
print(f"{spreadsheet['name']} - {spreadsheet['id']}")
Tasks and Contacts
Tasks and Contacts are opt-in: their scopes are not in the default login (contacts is a sensitive scope). Log in with them and enable the Google Tasks API / People API in your Cloud project:
gsuite auth login --force --scopes default,tasks,contacts
from datetime import date
from gsuite_contacts import Contacts
from gsuite_tasks import Tasks
tasks = Tasks(auth)
task = tasks.create_task("Pay rent", due=date(2026, 2, 1))
for t in tasks.list_tasks(show_completed=False):
print(t.title, t.due, "overdue" if t.is_overdue else "")
tasks.complete_task(task.id)
contacts = Contacts(auth)
for c in contacts.search("ana"):
print(c.display_name, c.email, c.phone)
ana = contacts.create(given_name="Ana", emails=["[email protected]"])
contacts.update(ana.id, phones=["+54 11 5555-5555"])
Search Console, Analytics (GA4) and Tag Manager (read-only)
These three are read-only and opt-in: their scopes (webmasters.readonly,
analytics.readonly, tagmanager.readonly) are not in default or all, so
existing logins keep working without re-consent. To use them:
- In your Google Cloud project enable the Google Search Console API, the Google Analytics Admin API, the Google Analytics Data API and the Tag Manager API.
- If your OAuth consent screen lists scopes explicitly, add the three above.
- Log in again. A login replaces the stored scopes, so list every set you still need (here, with Tasks and Contacts too):
gsuite auth login --force --scopes default,tasks,contacts,marketing
marketing is searchconsole,analytics,tagmanager; each can also be asked for
alone. Restart the MCP server / redeploy the API (gsuite auth export) after.
from gsuite_analytics import Analytics
from gsuite_searchconsole import SearchConsole
from gsuite_tagmanager import TagManager
sc = SearchConsole(auth)
sc.list_sites()
sc.query(
"sc-domain:example.com", "2026-09-01", "2026-09-30",
dimensions=["query", "page"],
filters=[{"dimension": "country", "operator": "equals", "expression": "arg"}],
row_limit=100,
)
sc.inspect_url("sc-domain:example.com", "https://example.com/blog/")
ga = Analytics(auth)
ga.account_summaries()
ga.run_report(
"123456789", ["date", "sessionSource"], ["activeUsers", "sessions"],
date_ranges=[("28daysAgo", "today")],
dimension_filters=[{"field": "country", "value": "Argentina"}],
limit=50, order_by="sessions",
)
ga.run_realtime_report("123456789", ["country"], ["activeUsers"])
gtm = TagManager(auth)
container = gtm.list_containers("accounts/123")[0]["path"]
workspace = gtm.list_workspaces(container)[0]["path"]
gtm.list_tags(workspace), gtm.list_triggers(workspace), gtm.list_versions(container)
REST API
# Start unified API server
gsuite serve --port 8080
# Or with Docker
docker run -p 8080:8080 gsuite-api
# All services under one roof
curl http://localhost:8080/gmail/messages/unread
curl http://localhost:8080/calendar/events/upcoming
curl http://localhost:8080/drive/files
curl http://localhost:8080/sheets/list
curl http://localhost:8080/sheets/{spreadsheet_id}/values/Sheet1!A1:D10
curl http://localhost:8080/tasks/lists/@default/tasks
curl "http://localhost:8080/contacts/search?q=ana"
# Read-only, need the "marketing" scopes
curl http://localhost:8080/searchconsole/sites
curl -X POST http://localhost:8080/analytics/properties/123456789:runReport \
-H 'Content-Type: application/json' -d '{"metrics": ["sessions"], "dimensions": ["date"]}'
curl "http://localhost:8080/tagmanager/tags?parent=accounts/1/containers/2/workspaces/3"
Interactive API docs available at /docs when server is running. See API Documentation section below for details.
CLI
# Authentication
gsuite auth login # Opens browser for OAuth
gsuite auth status # Check auth status
gsuite auth export # Export token for Cloud Run
# Gmail
gsuite gmail list --unread # List unread messages
gsuite gmail read MSG_ID # Read specific message
gsuite gmail send --to [email protected] --subject "Hi" --body "Hello"
gsuite gmail search "from:boss has:attachment"
gsuite gmail labels # List all labels
# Calendar
gsuite calendar list --days 7 # Upcoming events
gsuite calendar today # Today's events
gsuite calendar week # Week view
gsuite calendar create "Meeting" --start "2026-01-30 10:00"
gsuite calendar calendars # List calendars
# Sheets
gsuite sheets list # List all spreadsheets
gsuite sheets open "Budget" # Open by title
gsuite sheets read SHEET_ID "Sheet1!A1:D10" # Read range
gsuite sheets write SHEET_ID "Sheet1!A1" "Name,Email"
gsuite sheets append SHEET_ID "Sheet1" "John,[email protected]"
gsuite sheets create "New Sheet" # Create spreadsheet
# Tasks (login with --scopes default,tasks)
gsuite tasks lists # Task lists
gsuite tasks ls --due-before 2026-02-28
gsuite tasks add "Pay rent" --due 2026-02-01
gsuite tasks done TASK_ID
# Contacts (login with --scopes default,contacts)
gsuite contacts search ana
gsuite contacts add -g Ana -e [email protected] -p "+54 11 5555-5555"
gsuite contacts show CONTACT_ID
# Server
gsuite serve --port 8080 # Start REST API
gsuite status # Overall status
Architecture
google-suite/
├── packages/
│ ├── core/ # Shared auth, config, token storage
│ ├── gmail/ # Gmail client + query builder
│ ├── calendar/ # Calendar client
│ ├── drive/ # Drive client (upload, download, share)
│ ├── sheets/ # Sheets client + engine (from GSpreadManager)
│ ├── tasks/ # Tasks client
│ ├── contacts/ # Contacts client (People API)
│ └── mcp/ # MCP server (published as gsuite-mcp)
├── api/ # Unified FastAPI REST gateway
├── cli/ # Unified CLI (Typer + Rich)
├── skills/gsuite-sdk/ # Agent skill (SKILL.md + references)
├── plugins/gsuite-sdk/ # Claude Code plugin: a synced copy of the skill + the MCP server
├── docs/ # Documentation site (MkDocs)
└── tests/integration/ # Opt-in tests against a real Google account
Design Principles
- Clean Architecture - Domain entities, interfaces, infrastructure separation
- Provider Agnostic - Swap implementations via interfaces
- Shared Auth - One OAuth flow grants access to all services
- Independent Packages - Install only what you need
- Unified Gateway - Single API/CLI for all services
For detailed architecture decisions and design patterns, see Architecture Documentation.
Use with AI agents
skills/gsuite-sdk is an
Agent Skill that teaches an agent to use the gsuite
CLI (JSON output) and the SDK. Install it in your agent:
npx skills add PabloAlaniz/google-suite # Claude Code, Codex, Cursor, Gemini CLI, Copilot, ...
claude plugin marketplace add PabloAlaniz/google-suite # Claude Code plugin
claude plugin install gsuite-sdk@gsuite-sdk
gemini extensions install https://github.com/PabloAlaniz/google-suite # Gemini CLI extension
In a Claude Code session: /plugin install gsuite-sdk --marketplace PabloAlaniz/google-suite.
MCP server
gsuite-mcp exposes Gmail, Calendar, Drive, Sheets,
Tasks and Contacts as MCP tools for any MCP client:
claude mcp add gsuite -e GSUITE_TOKEN_DB_PATH="$PWD/tokens.db" -- uvx gsuite-mcp
It is in the official MCP Registry
as io.github.PabloAlaniz/gsuite-sdk.
On OpenClaw it is pabloalaniz/gsuite-sdk on ClawHub.
The agent still needs the SDK and a login: pip install "gsuite-sdk[cli]" and
gsuite auth login (see Quick Start).
Standalone Repos
This monorepo consolidates and extends these standalone projects:
- Gmail-API - Standalone Gmail API
- Calendar-API - Standalone Calendar API
The standalone repos remain functional for existing deployments.
Configuration
Environment variables (prefix GSUITE_):
| Variable | Description |
|---|---|
GSUITE_CREDENTIALS_FILE | OAuth credentials JSON path |
GSUITE_TOKEN_STORAGE | sqlite or secretmanager |
GSUITE_TOKEN_DB_PATH | SQLite token database path |
GSUITE_GCP_PROJECT_ID | GCP project (for Secret Manager) |
GSUITE_API_KEY | API key for REST endpoints (required unless GSUITE_ALLOW_NO_API_KEY=true) |
GSUITE_CORS_ORIGINS | Comma-separated origins allowed by CORS (off by default) |
See api/README.md for every REST API setting.
Development
# Clone
git clone https://github.com/PabloAlaniz/google-suite.git
cd google-suite
# Install everything, pinned by uv.lock
uv sync --all-extras
# Run tests
uv run pytest
# Lint
uv run ruff check packages api cli
Deployment
Cloud Run (GCP)
Deploy the REST API to Cloud Run with Secret Manager for token storage:
# Build and push Docker image (from the repo root)
docker build -f api/Dockerfile -t gcr.io/YOUR_PROJECT/gsuite-api .
docker push gcr.io/YOUR_PROJECT/gsuite-api
# Deploy to Cloud Run
gcloud run deploy gsuite-api \
--image gcr.io/YOUR_PROJECT/gsuite-api \
--platform managed \
--region us-central1 \
--set-env-vars GSUITE_TOKEN_STORAGE=secretmanager \
--set-env-vars GSUITE_GCP_PROJECT_ID=YOUR_PROJECT \
--set-secrets GSUITE_API_KEY=gsuite-api-key:latest \
--allow-unauthenticated
The API rejects every request until GSUITE_API_KEY is set. If you protect
the service with IAM instead (--no-allow-unauthenticated), set
GSUITE_ALLOW_NO_API_KEY=true rather than the key.
Note: For Secret Manager token storage, ensure your Cloud Run service account has secretmanager.versions.access permission.
Docker Compose
For local development or self-hosted deployment:
version: '3.8'
services:
gsuite-api:
build:
context: .
dockerfile: api/Dockerfile
ports:
- "8080:8080"
environment:
GSUITE_CREDENTIALS_FILE: /secrets/credentials.json
GSUITE_TOKEN_DB_PATH: /data/tokens.db
GSUITE_API_KEY: your-api-key-here
volumes:
- ./credentials.json:/secrets/credentials.json:ro
- ./data:/data
API Documentation
When running the REST API server, interactive API docs are available at:
- Swagger UI:
http://localhost:8080/docs - ReDoc:
http://localhost:8080/redoc
Troubleshooting
Token refresh failed
Problem: TokenRefreshError: Failed to refresh token
Solution:
- Delete the token database:
rm ~/.gsuite/tokens.db(or your configured path) - Re-authenticate:
gsuite auth login - For service accounts, verify the JSON file hasn't expired
Missing scopes
Problem: HttpError 403: Insufficient Permission
Solution:
- Check required scopes in error message
- Re-authenticate with additional scopes:
gsuite auth login --scopes gmail.readonly,calendar,drive - Or update
GSUITE_DEFAULT_SCOPESenvironment variable
Credentials not found
Problem: CredentialsNotFoundError: OAuth credentials file not found
Solution:
- Download OAuth credentials from Google Cloud Console
- Save as
credentials.jsonin working directory - Or set
GSUITE_CREDENTIALS_FILEenvironment variable
Import errors
Problem: ModuleNotFoundError: No module named 'gsuite_gmail'
Solution:
- Install the package:
pip install gsuite-sdk - Or for development:
uv sync --all-extras(see CONTRIBUTING.md)
License
MIT - see LICENSE
Author
Pablo Alaniz (@PabloAlaniz)