API Documentation
Query your Google Sheets data programmatically using the SheetBoy API.
Base URL: https://sheetboy.com
Get Started
Sign in with Google
Create an account by signing in with the Google account that has access to your sheets. This stores your credentials securely for API access.
Sign inCreate an API key
Go to the Developer page and generate an API key. Save the key securely — it is only shown once.
Developer pageGet tokens from admin
Contact the SheetBoy admin to get tokens credited to your account. Every API operation costs tokens (e.g. 1 token per query).
View token costsImport & query via API
Import a Google Sheet (shared with your Google account) via the API, then query it with natural language. See examples below.
View examplesAuthentication
All API requests require an API key. Generate one from your Developer page.
Include your API key in the Authorization header:
Authorization: Bearer sw_live_your_api_key_here
Google Sheets access: The API uses your Google credentials (stored when you first sign in) to access sheets. Any Google Sheet shared with your Google account can be imported and queried via the API.
Token Billing
Every API operation costs tokens. Your admin assigns tokens to your account. If you run out, API calls return 402 with your current balance and the required cost.
| Operation | Cost |
|---|---|
| Import sheet | 5 tokens per 100 rows (rounded up) |
| Query | 1 token |
| Sync | 5 tokens |
| Edit cells | 2 tokens |
| Get sheet info / Manage tabs | Free (0 tokens) |
Check your balance anytime via GET /api/v1/tokens. Token balance and cost are also returned in X-Token-Balance and X-Token-Cost response headers.
Example: Importing a sheet with 250 rows costs ceil(250/100) × 5 = 15 tokens. A sheet with 50 rows costs 5 tokens (minimum).
/api/v1/sheets/:idGet sheet metadata including tab list with numbers and active status. Free (0 tokens).
Success Response 200
{
"sheet_id": "a1b2c3d4-...",
"name": "Sales Data",
"row_count": 1500,
"tab_count": 3,
"active_tabs": ["Revenue", "Summary"],
"tabs": [
{ "number": 1, "name": "Revenue", "active": true, "headers": ["Date", "Amount"] },
{ "number": 2, "name": "Summary", "active": true, "headers": ["Region", "Total"] },
{ "number": 3, "name": "Raw", "active": false, "headers": ["ID", "Value"] }
],
"message": "2 of 3 tabs active. Queries use: Revenue, Summary."
}/api/v1/sheets/:idUpdate which tabs are active for queries. Free (0 tokens). Inactive tabs stay stored but are excluded from queries.
Request Body
// By tab name
{ "active_tabs": ["Revenue", "Summary"] }
// By tab number (1-indexed)
{ "active_tabs": [1, 3] }
// Mixed (names and numbers)
{ "active_tabs": ["Revenue", 3] }
// Reset to all tabs active
{ "active_tabs": null }| Field | Type | Required | Description |
|---|---|---|---|
| active_tabs | array | null | Yes | Array of tab names (strings) or numbers (1-indexed), or null to reset all tabs to active |
Success Response 200
{
"sheet_id": "a1b2c3d4-...",
"active_tabs": ["Revenue", "Summary"],
"tabs": [
{ "number": 1, "name": "Revenue", "active": true },
{ "number": 2, "name": "Summary", "active": true },
{ "number": 3, "name": "Raw", "active": false }
],
"message": "Active tabs updated. Queries will now use: Revenue, Summary."
}Error Responses
| Status | Meaning |
|---|---|
400 | Invalid tab name/number, or empty array (at least 1 tab required) |
401 | Missing or invalid API key |
404 | Sheet not found or access denied |
Endpoints
/api/v1/queryRun a natural-language question against an imported sheet. Requires API key auth. Only active tabs are included in the query (see PATCH /sheets/:id).
Request Body
{
"sheet_id": "uuid-of-your-sheet",
"question": "What is the total revenue?"
}| Field | Type | Required | Description |
|---|---|---|---|
| sheet_id | string | Yes | UUID of the imported sheet |
| question | string | Yes | Natural-language question (max 1,000 characters) |
Success Response 200
{
"answer": "The total revenue is $12,50,000.",
"type": "text",
"tokens_used": 450,
"cached": false,
"cost": 0.005
}Error Responses
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
402 | Insufficient tokens |
404 | Sheet not found or access denied |
429 | Rate limit exceeded |
/api/v1/sheetsImport a Google Sheet by URL or upload data directly. Requires API key auth.
Mode 1: Google Sheet URL
Pass a Google Sheets URL to import. The sheet must be shared with your connected Google account.
{
"sheet_url": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms/edit"
}Mode 2: Direct Data Upload
Upload data directly as an array of objects with explicit headers. Useful for CSV data or non-Google sources.
{
"headers": ["Name", "Revenue", "Region"],
"data": [
{ "Name": "Product A", "Revenue": 50000, "Region": "US" },
{ "Name": "Product B", "Revenue": 32000, "Region": "EU" },
{ "Name": "Product C", "Revenue": 41000, "Region": "US" }
]
}Success Response 200
{
"sheet_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Sales Data",
"status": "ready",
"row_count": 1500,
"column_count": 12,
"tab_count": 3,
"active_tabs": null,
"tabs": [
{ "number": 1, "name": "Revenue", "row_count": 500, "headers": ["Date", "Amount"] },
{ "number": 2, "name": "Summary", "row_count": 200, "headers": ["Region", "Total"] },
{ "number": 3, "name": "Raw", "row_count": 800, "headers": ["ID", "Value"] }
],
"message": "Sheet imported with 3 tabs. All tabs are active by default. ..."
}Use the returned sheet_id in subsequent /api/v1/query calls. If the same Google Sheet URL was already imported, the existing sheet ID is returned. Each tab includes a number (1-indexed) for easy reference. By default, all tabs are active (active_tabs: null). Use PATCH /api/v1/sheets/:id to select specific tabs.
Error Responses
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
400 | Invalid body -- must provide either sheet_url or data + headers |
/api/v1/sheets/:id/syncRe-fetch the latest data from Google Sheets. Costs 5 tokens per sync.
Success Response 200
{
"sheet_id": "a1b2c3d4-...",
"name": "Sales Data",
"status": "synced",
"row_count": 1500,
"column_count": 12,
"tab_count": 3,
"last_synced_at": "2026-02-13T10:00:00Z"
}/api/v1/editWrite cell updates back to a Google Sheet. Costs 2 tokens. Max 100 cells per request. The sheet must be shared with edit (write) permission.
Request Body
{
"sheet_id": "uuid-of-your-sheet",
"edits": [
{ "cell": "A1", "value": "Updated Name" },
{ "cell": "B2", "value": 42000 },
{ "cell": "C3", "value": "=SUM(B1:B2)" }
],
"tab": "Sheet1"
}| Field | Type | Required | Description |
|---|---|---|---|
| sheet_id | string | Yes | UUID of the imported sheet |
| edits | array | Yes | Array of {cell, value} objects. Cell uses A1 notation. |
| tab | string | No | Tab name (defaults to first tab) |
Success Response 200
{
"success": true,
"cells_updated": 3
}/api/v1/tokensCheck your current token balance and recent transaction history.
Response 200
{
"balance": 485,
"costs": { "import": 5, "query": 1, "sync": 5, "edit": 2 },
"history": [
{
"amount": -1,
"balance_after": 485,
"operation": "query",
"description": "API query",
"created_at": "2026-02-13T09:30:00Z"
}
]
}API Key Management
Note: The keys endpoints use session authentication (browser cookies), not API key auth. These are meant to be called from your logged-in browser session or with your session cookie.
/api/v1/keysCreate a new API key. The raw key is only returned once.
Request Body
{
"name": "My Bot Key"
}Response 200
{
"key": "sw_live_abc123def456...",
"id": "key-uuid",
"name": "My Bot Key"
}Save the key immediately. It will not be shown again.
/api/v1/keysList all API keys for the authenticated user.
Response 200
{
"keys": [
{
"id": "key-uuid",
"name": "My Bot Key",
"key_prefix": "sw_live_abc1...",
"is_active": true,
"last_used_at": "2025-06-15T10:30:00Z",
"created_at": "2025-06-01T08:00:00Z"
}
]
}/api/v1/keysRevoke an API key. The key will immediately stop working.
Request Body
{
"key_id": "key-uuid-to-revoke"
}Response 200
{
"success": true
}Code Examples
cURL
curl -X POST https://sheetboy.com/api/v1/query \
-H "Authorization: Bearer sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"sheet_id": "your-sheet-id",
"question": "Total sales last month?"
}'JavaScript / Node.js
const response = await fetch("https://sheetboy.com/api/v1/query", {
method: "POST",
headers: {
"Authorization": "Bearer sw_live_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
sheet_id: "your-sheet-id",
question: "What are the top 5 products by revenue?",
}),
});
const data = await response.json();
console.log(data.answer);Python
import requests
response = requests.post(
"https://sheetboy.com/api/v1/query",
headers={
"Authorization": "Bearer sw_live_your_key",
"Content-Type": "application/json",
},
json={
"sheet_id": "your-sheet-id",
"question": "What are the top 5 products by revenue?",
},
)
data = response.json()
print(data["answer"])Import a Sheet via API (cURL)
# Import from Google Sheets URL
curl -X POST https://sheetboy.com/api/v1/sheets \
-H "Authorization: Bearer sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"sheet_url": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms/edit"
}'
# Upload data directly
curl -X POST https://sheetboy.com/api/v1/sheets \
-H "Authorization: Bearer sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"headers": ["Name", "Revenue"],
"data": [
{"Name": "Product A", "Revenue": 50000},
{"Name": "Product B", "Revenue": 32000}
]
}'Managing Tabs
# Get sheet info with tab list
curl https://sheetboy.com/api/v1/sheets/YOUR_SHEET_ID \
-H "Authorization: Bearer sw_live_your_key"
# Activate only tabs 1 and 3
curl -X PATCH https://sheetboy.com/api/v1/sheets/YOUR_SHEET_ID \
-H "Authorization: Bearer sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "active_tabs": [1, 3] }'
# Activate by name
curl -X PATCH https://sheetboy.com/api/v1/sheets/YOUR_SHEET_ID \
-H "Authorization: Bearer sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "active_tabs": ["Revenue", "Summary"] }'
# Reset to all tabs active
curl -X PATCH https://sheetboy.com/api/v1/sheets/YOUR_SHEET_ID \
-H "Authorization: Bearer sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "active_tabs": null }'Full Workflow (Python)
import requests
API_KEY = "sw_live_your_key"
BASE = "https://sheetboy.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
# Step 1: Import a sheet
import_resp = requests.post(
f"{BASE}/sheets",
headers=HEADERS,
json={
"sheet_url": "https://docs.google.com/spreadsheets/d/YOUR_ID/edit"
},
)
sheet_id = import_resp.json()["sheet_id"]
print(f"Imported sheet: {sheet_id}")
# Step 2: Query the sheet
query_resp = requests.post(
f"{BASE}/query",
headers=HEADERS,
json={
"sheet_id": sheet_id,
"question": "What are the total sales by region?",
},
)
print(query_resp.json()["answer"])Getting Your Sheet ID
After importing a sheet in the SheetBoy dashboard, you can find the sheet ID in two places:
- The URL when viewing a sheet:
sheetboy.com/sheets/[id] - The sheet details card on your dashboard
- The response from
POST /api/v1/sheetswhen importing via the API
Tip: The sheet ID is a UUID like a1b2c3d4-e5f6-7890-abcd-ef1234567890. It is different from the Google Spreadsheet ID in the original URL.
Need Help?
If you have questions about the API, reach out to us at support@sheetboy.com