Skip to content
Relationsheet

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/sheets
returns

{ "sheets": [ { "id": "…", "name": "Tasks" } ] }

statuses

200 the key's table; 400 another database refusal, with its message; 401 missing, unknown or revoked key; 429 rate limit exceeded.

rate limit

1 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 }

statuses

200 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 limit

1 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>/rows
sheetId (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": [] }

statuses

201 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 limit

2 (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": "…" } } }

statuses

200 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 limit

1 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": [] }

statuses

200 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 limit

2 (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." }

statuses

200 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 limit

1 call, and 1 write.

Errors.

Every error body is JSON with an error string; some add a reason.

400

The request is malformed (bad JSON, no known fields) or the database refused it for another reason; the body's error says which.

401

The API key is missing, unknown or revoked.

402

The workspace's plan limit is reached (for example, rows per table). The body has reason "plan_limit".

403

The API key is read-only and the operation writes.

404

The table or row does not exist, is in the recycle bin, or is not the key's table.

409

The row owns a sub-table set to block deletion. The body has reason "subtable_blocks_delete" and says what to change.

429

A rate limit is exceeded. Wait the Retry-After seconds and retry.

Limits.

300 calls per minute

per key, every operation

120 writes per minute

per key: append, update and delete

1,200 calls per minute

per 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.