Sheets API
Workbooks, tabs, columns, cells with formulas, rows, CSV and XLSX import and export, and links with records.
Sheets is a spreadsheet that lives next to your data. This API creates workbooks, reads and writes cells (with formulas recalculated for you), changes the layout, imports and exports files, and moves data between sheets and records.
If the Sheets app is switched off in Settings › Apps, these routes answer 404 NOT_FOUND.
Endpoints
Permissions are checked against the workbook (its key is the resource). sheets:read opens and reads, sheets:write edits cells, rows, columns and tabs, sheets:manage creates, renames and deletes workbooks.
| Method | Path | Permission | What it does |
|---|---|---|---|
GET |
/orgs/{orgId}/sheets/workbooks |
sheets:read |
List workbooks |
POST |
/orgs/{orgId}/sheets/workbooks |
sheets:manage |
Create a workbook |
GET |
/orgs/{orgId}/sheets/workbooks/{wbId} |
sheets:read |
Get a workbook with its tabs and columns |
PATCH |
/orgs/{orgId}/sheets/workbooks/{wbId} |
sheets:manage |
Rename |
DELETE |
/orgs/{orgId}/sheets/workbooks/{wbId} |
sheets:manage |
Delete |
POST |
/orgs/{orgId}/sheets/workbooks/{wbId}/tabs |
sheets:write |
Add a tab |
PATCH · DELETE |
/orgs/{orgId}/sheets/workbooks/{wbId}/tabs/{tabId} |
sheets:write |
Rename or delete a tab |
POST |
…/tabs/{tabId}/columns |
sheets:write |
Add a column |
PATCH · DELETE |
…/tabs/{tabId}/columns/{colId} |
sheets:write |
Change or delete a column |
GET |
…/tabs/{tabId}/rows |
sheets:read |
Read a window of rows |
POST |
…/tabs/{tabId}/cells |
sheets:write |
Write cells |
POST |
…/tabs/{tabId}/rows/insert |
sheets:write |
Insert rows |
POST |
…/tabs/{tabId}/rows/delete |
sheets:write |
Delete rows |
GET |
…/tabs/{tabId}/export.csv |
sheets:read |
Download a tab as CSV |
POST |
…/workbooks/{wbId}/tabs/import |
sheets:write |
Import a CSV as a new tab |
GET |
…/workbooks/{wbId}/export.xlsx |
sheets:read |
Download the workbook as XLSX |
POST |
/orgs/{orgId}/sheets/import |
sheets:manage |
Import an XLSX as a new workbook |
POST |
/orgs/{orgId}/sheets/from-records |
sheets:manage |
Open records of a type as an editable sheet |
POST |
/orgs/{orgId}/sheets/from-query |
sheets:manage |
Turn a query result into a sheet |
POST |
…/tabs/{tabId}/push-records |
sheets:write |
Write a record-linked tab's edits back to records |
(… is /orgs/{orgId}/sheets/workbooks/{wbId}.) Several people can edit the same workbook at once; the last write to a cell wins.
Workbooks
{
"id": "0190fc00-…",
"key": "budget-2026",
"name": "Budget 2026",
"size_bytes": 20480,
"tabs": [
{
"id": "0190fc01-…",
"name": "Sheet1",
"pos": 0,
"max_row": 41,
"columns": [ { "id": "0190fc02-…", "key": "A", "label": "Item", "pos": 0, "type": "text" } ]
}
],
"state": "warm",
"created_at": "2026-10-01T08:00:00Z",
"updated_at": "2026-10-04T09:00:00Z"
}
GET /workbooks answers { "workbooks": [ … ] } (without tabs). POST /workbooks takes name (required) and key (optional) and answers 201 with the workbook (no tabs or state yet; read it with GET). DELETE answers 200 { "deleted": true }. Errors: 409 KEY_TAKEN, 402 LIMIT_EXCEEDED. A new tab starts with 26 columns (A to Z).
POST …/columns takes label, type (default text) and an optional config (select options, currency unit) and answers 201 with the column. Column type is text, number, currency, date, checkbox or select. The type cannot change once created. A cell holds a value of its column's type, or a formula. Limits: 20 tabs per workbook, 200 columns per tab, 100,000 rows per tab (lower on some plans, then 402 LIMIT_EXCEEDED). max_row is the highest row in use, or -1 for an empty tab.
Note:
stateiscold,warming,warmorfailed: a workbook is loaded into memory when it is opened, and reads of a cold workbook still work, only more slowly. When your plan's memory allowance is full, loading another workbook is refused with503 SHEETS_MEMORY. Close other workbooks in the app, or upgrade.
GET …/tabs/{tabId}/rows
| Parameter | Notes |
|---|---|
from |
First row, starting at 0. Default 0 |
limit |
Default 200, at most 500 |
{
"data": {
"rows": [
{ "r": 0, "cells": { "A": { "v": "Rent" }, "B": { "v": 42000 } } },
{ "r": 1, "cells": { "A": { "v": "Total" }, "B": { "v": 42000, "f": "=SUM(B1:B1)" } } }
]
}
}
Cells are keyed by column key. v is the value, f the formula when the cell holds one, and e an error such as #REF!. Rows with no data are not returned.
POST …/tabs/{tabId}/cells
Write up to 10,000 cells in one call. Dependent formulas are recalculated and the cells that changed come back.
| Field | Type | Required | Notes |
|---|---|---|---|
cells |
array | Yes | Each { "r": row, "c": column key, "input": "text" } |
input is exactly what a person would type. An empty string clears the cell. A leading = makes it a formula. Anything else is read as a value of the column's type.
curl -s -X POST "https://axisiq.co/api/v1/orgs/$ORG/sheets/workbooks/$WB/tabs/$TAB/cells" \
-H "Authorization: Bearer $AXIS_KEY" -H "Content-Type: application/json" \
-d '{"cells": [{"r": 0, "c": "A", "input": "Rent"}, {"r": 0, "c": "B", "input": "42000"}, {"r": 1, "c": "B", "input": "=SUM(B1:B1)"}]}'
{
"data": {
"changed": [ { "tab_id": "0190fc01-…", "r": 1, "c": "B", "cell": { "v": 42000, "f": "=SUM(B1:B1)" } } ],
"truncated": false
}
}
truncated is true when too many cells changed to list; read the rows again. The formula language follows Excel, with about 45 functions, references across tabs, and AXLOOKUP to read your records.
rows/insert and rows/delete take { "at": 5, "count": 2 }: the starting row (counted from 0, like r) and how many rows. Inserting shifts the rows down; deleting removes at up to at + count - 1 and shifts the rest up. Formula references are rewritten, and a reference to a deleted row becomes #REF!. count must be at least 1. Response { "ok": true }.
Import and export
- CSV:
GET …/tabs/{tabId}/export.csvdownloads a tab.POST …/tabs/import(multipart,file) adds the CSV as a new tab named after the file. Response201{ id, name, pos }. - XLSX:
GET …/export.xlsxdownloads the workbook.POST /sheets/import(multipart,file) creates a workbook and answers201with it.
Sheets and records
POST /sheets/from-records with { "type_key": "products", "name": "Products", "ids": [ … ] } exports a record type, or just the listed records (up to 500), into a new record-linked tab. ids is optional. Response 201: { "workbook_id", "tab_id", "truncated" }.
Edit the sheet, then POST …/tabs/{tabId}/push-records (no body) writes the changes back as the caller: rows with an id update that record, rows without one create a new record and the id is written back into the sheet. Empty cells are skipped (a push cannot clear a value). At most 2,000 rows per push. The response is { "updated", "created", "failed", "errors": [ { "row", "message" } ] }; a failed row does not stop the others.
POST /sheets/from-query with { "sql": "SELECT …", "name": "…" } runs a read-only query as the caller and puts the result in a new sheet. If the query reads exactly one record type and selects id, the tab is record-linked too. Response 201: { "workbook_id", "tab_id", "truncated", "linked" }.
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR |
Bad cells (1 to 10,000 per call), unknown column, bad type |
| 402 | LIMIT_EXCEEDED |
Free-plan or plan row and memory allowance reached |
| 403 | FORBIDDEN |
Missing the workbook permission |
| 404 | NOT_FOUND |
No such workbook, tab or column |
| 409 | KEY_TAKEN |
Workbook key already used |
| 503 | SHEETS_MEMORY |
Memory allowance exceeded |
Related
- Record types and fields: computed fields use the same formula language
- In the product: Sheets