Help Center · Reference
6 endpoints, one table per key.
The REST API lists, reads, appends, updates and deletes the rows of a table, for automation tools such as n8n and your own scripts. Requests go to https://relationsheet.app/api/v1 and are authenticated with an API key.
Keys and what they reach.
One table per key
Each API key belongs to one table: a workspace admin creates it from that table, and a request for any other table answers 404, the same as a table that does not exist.
Every row and column of it
A key reads (and, unless it is read-only, writes) every row and column of its table. People's row and column permissions do not apply to keys, so share a key like a password.
Creating a key
Keys are created by a workspace admin in the table's Share panel, under REST API. The Free plan cannot create keys; Pro allows 5 per workspace, Team 25 and Business unlimited. A key can be made read-only; a read-only key is refused with 403 on every write. The key is shown once, when it is created.
Sending it
Authorization: Bearer <YOUR_KEY>
# or
x-api-key: <YOUR_KEY>A revoked key is refused with 401 from the next request on.
Values and formulas
Values are converted to each column's type on the server. A string that starts with = is stored as literal text: it is never evaluated as a formula.
Deleting goes to the recycle bin
DELETE moves the row to the recycle bin, together with the sub-tables it owns, exactly like deleting it in the grid. Anyone allowed to edit that row can restore it from the recycle bin for 7 days; when it owns sub-tables, restoring also needs the right to change them. A row that owns a sub-table set to block deletion is refused with 409 and stays where it is.
Endpoints.
List the tables this key can reach
GET /api/v1/sheets
Returns the key's table, as a list of one. A read-only key may list.
curl -H "Authorization: Bearer <YOUR_KEY>" https://relationsheet.app/api/v1/sheetsreturns{ "sheets": [ { "id": "…", "name": "Tasks" } ] }
statuses200 the key's table; 400 another database refusal, with its message; 401 missing, unknown or revoked key; 429 rate limit exceeded.
rate limit1 call.
List a table's rows
GET /api/v1/sheets/{sheetId}/rows
Returns live rows in order (rows in the recycle bin are left out). Page with a cursor: pass the response's nextCursor back as ?after= for the next page; nextCursor is null on the last page.
curl -H "Authorization: Bearer <YOUR_KEY>" "https://relationsheet.app/api/v1/sheets/<TABLE_ID>/rows?limit=100"sheetId (path, required)The table's id (from GET /api/v1/sheets).
after (query)Return rows after this ordinal (the previous page's nextCursor).
limit (query)Rows per page, 1 to 1,000 (default 1,000).
returns{ "rows": [ { "id": "…", "ordinal": 1, "cells": { "<columnId>": { "kind": "text", "text": "…" } } } ], "nextCursor": null }
statuses200 a page of rows and the next cursor; 400 another database refusal, with its message; 401 missing, unknown or revoked key; 404 not the key's table; 429 rate limit exceeded.
rate limit1 call.
Append a row
POST /api/v1/sheets/{sheetId}/rows
Fields are matched to columns by name (case-insensitive) or by column id. Unknown fields are ignored and returned in ignoredFields. This is the inbound webhook for n8n and similar tools.
curl -X POST -H "Authorization: Bearer <YOUR_KEY>" -H "Content-Type: application/json" -d '{"Task":"From the API"}' https://relationsheet.app/api/v1/sheets/<TABLE_ID>/rowssheetId (path, required)The table's id (from GET /api/v1/sheets).
body (JSON)A flat object of field: value, or the same wrapped as { "cells": { … } }. { "Task": "From n8n", "Status": "Todo", "Estimate (hrs)": 5 }
returns{ "ok": true, "id": "…", "ordinal": 6, "ignoredFields": [] }
statuses201 the new row's id and ordinal; 400 bad JSON, not an object, or another database refusal; 401 missing, unknown or revoked key; 402 the plan's row limit is reached; 403 read-only key; 404 not the key's table; 429 rate limit exceeded.
rate limit2 (3 when a value is a formula string that names a row by cell address, such as =B1-C1) calls, and 1 write.
Read one row
GET /api/v1/sheets/{sheetId}/rows/{rowId}
A row in the recycle bin answers 404.
curl -H "Authorization: Bearer <YOUR_KEY>" https://relationsheet.app/api/v1/sheets/<TABLE_ID>/rows/<ROW_ID>sheetId (path, required)The table's id (from GET /api/v1/sheets).
rowId (path, required)The row's id.
returns{ "id": "…", "ordinal": 1, "cells": { "<columnId>": { "kind": "text", "text": "…" } } }
statuses200 the row; 400 another database refusal, with its message; 401 missing, unknown or revoked key; 404 no such live row, or not the key's table; 429 rate limit exceeded.
rate limit1 call.
Update a row's fields
PATCH /api/v1/sheets/{sheetId}/rows/{rowId}
Only the fields you send change; the others are left as they are. Unknown fields are returned in ignoredFields.
curl -X PATCH -H "Authorization: Bearer <YOUR_KEY>" -H "Content-Type: application/json" -d '{"Status":"Done"}' https://relationsheet.app/api/v1/sheets/<TABLE_ID>/rows/<ROW_ID>sheetId (path, required)The table's id (from GET /api/v1/sheets).
rowId (path, required)The row's id.
body (JSON)A flat object of field: value, or { "cells": { … } }. { "Status": "Done" }
returns{ "ok": true, "id": "…", "cells": { … }, "ignoredFields": [] }
statuses200 the row's cells after the update; 400 bad JSON, no known fields, or another database refusal; 401 missing, unknown or revoked key; 403 read-only key; 404 no such row, or not the key's table; 429 rate limit exceeded.
rate limit2 (3 when a value is a formula string that names a row by cell address) calls, and 1 write.
Move a row to the recycle bin
DELETE /api/v1/sheets/{sheetId}/rows/{rowId}
DELETE moves the row to the recycle bin, together with the sub-tables it owns, exactly like deleting it in the grid. Anyone allowed to edit that row can restore it from the recycle bin for 7 days; when it owns sub-tables, restoring also needs the right to change them. A row that owns a sub-table set to block deletion is refused with 409 and stays where it is.
curl -X DELETE -H "Authorization: Bearer <YOUR_KEY>" https://relationsheet.app/api/v1/sheets/<TABLE_ID>/rows/<ROW_ID>sheetId (path, required)The table's id (from GET /api/v1/sheets).
rowId (path, required)The row's id.
returns{ "ok": true, "id": "…", "deleted": true, "binned": true, "message": "The row was moved to the recycle bin. It can be restored from the bin for 7 days." }
statuses200 the row is in the recycle bin; 400 another database refusal, with its message; 401 missing, unknown or revoked key; 403 read-only key; 404 no such live row (already in the bin, or never there), or not the key's table; 409 the row owns a sub-table set to block deletion; 429 rate limit exceeded.
rate limit1 call, and 1 write.
Errors.
Every error body is JSON with an error string; some add a reason.
400The request is malformed (bad JSON, no known fields) or the database refused it for another reason; the body's error says which.
401The API key is missing, unknown or revoked.
402The workspace's plan limit is reached (for example, rows per table). The body has reason "plan_limit".
403The API key is read-only and the operation writes.
404The table or row does not exist, is in the recycle bin, or is not the key's table.
409The row owns a sub-table set to block deletion. The body has reason "subtable_blocks_delete" and says what to change.
429A rate limit is exceeded. Wait the Retry-After seconds and retry.
Limits.
300 calls per minuteper key, every operation
120 writes per minuteper key: append, update and delete
1,200 calls per minuteper workspace, across all of its keys
Each limit is a fixed 60-second window. Over a limit the API answers 429 with a Retry-After header: the seconds until that window resets. A refused call does not count. Each request has a 15-second budget. List rows returns at most 1,000 per page.
Sending data out instead? An automation’s “call a webhook” step does that. More help topics →
Connect a table to your tools.
Start free, then create a key on a paid plan.