REST API

A data source's tables, from another application, with a key. Everything below is also in /api/v1/openapi.json, generated from the live schema.

On this page

A key #

Make one under the project's settings, in API keys. It is shown once and stored as a hash. The key says which project; every request names the data source with ?source=, and one that names none is refused with the names the project has. A project's id never appears in an address.

A key is read and write, or read only (GET, HEAD, OPTIONS), and may be narrowed to some tables of the source it was made on. It acts for the person who made it: taken out of the project, they take its access with them. Writes made with a key are listed in the project's Activity.

Paid orders, biggest first
curl -H "Authorization: Bearer 10lines_XXXX" \
  "https://10lines.io/api/v1/tables/orders?source=shop&per_page=50&order_by=-total&status[eq]=paid"

What there is #

RequestDoes
GET /api/v1/schema tables, columns, relationships and saved views
GET /api/v1/openapi.json the same as an OpenAPI 3.1 document, only the tables the key reaches
GET /api/v1/tables/:table rows
GET /api/v1/tables/:table/:id one row
POST /api/v1/tables/:table a new row
PUT, PATCH /api/v1/tables/:table/:id a change to a row
DELETE /api/v1/tables/:table/:id a row gone
POST, PATCH, DELETE /api/v1/tables/:table/bulk many rows at once, all or nothing
GET /api/v1/tables/:table/export.csv, export.json the whole table, streamed
/api/v1/tables/:table/:id/links/:relationship many-to-many links: GET lists them, PUT replaces the set, POST adds, DELETE removes

Listing rows #

OptionExampleMeans
page, per_page ?per_page=50 page by number; at most 200 a page
fields ?fields=id,code only these columns
order_by ?order_by=code,-total by several columns; - is descending
q ?q=ada the text columns contain it; a whole number is also looked for in the number columns
filters ?total[gt]=10 eq, ne, gt, gte, lt, lte, in (comma-separated), contains, starts_with, ends_with, null (true or false); ?total=10 is eq
view ?view=3 a saved view; anything else given narrows it further
cursor ?cursor= walk a big table, below

A page answers data and, under meta, page, per_page, total and total_pages. A read has five seconds; when counting the rows takes longer, the page still comes back, total_exact is false and total is what is certain.

A big table #

Paging by number makes the database count past every row it skips. A cursor costs the same wherever it lands: start with an empty ?cursor= and a single order_by column, and follow next_cursor until it is null. It does not count the table, so there is no total.

GET /api/v1/tables/orders?source=shop&order_by=id&cursor=
→ {"data": [...], "meta": {"per_page": 25, "next_cursor": "g2gCYQFhAg"}}

GET /api/v1/tables/orders?source=shop&order_by=id&cursor=g2gCYQFhAg
→ {"data": [...], "meta": {"per_page": 25, "next_cursor": null}}

Writing #

A row's values go under data. They are cast and checked against the column's type and its field settings before anything is written, and a refusal is a 422 with an errors map by column.

curl -X POST -H "Authorization: Bearer 10lines_XXXX" \
  -H "Content-Type: application/json" \
  -d '{"data": {"code": "A-1001", "total": 42}}' \
  "https://10lines.io/api/v1/tables/orders?source=shop"

A bulk call carries data as a list (each with an id for an update) or ids for a delete, up to 1000 rows. It is all or nothing, and a refusal names the position or id of every row that failed.

curl -X POST -H "Authorization: Bearer 10lines_XXXX" \
  -H "Content-Type: application/json" \
  -d '{"data": [{"code": "A-1"}, {"code": "A-2"}]}' \
  "https://10lines.io/api/v1/tables/orders/bulk?source=shop"

curl -X DELETE -H "Authorization: Bearer 10lines_XXXX" \
  -H "Content-Type: application/json" \
  -d '{"ids": [1, 2]}' \
  "https://10lines.io/api/v1/tables/orders/bulk?source=shop"

Caching and browsers #

Reads carry an ETag; send it back as If-None-Match and an unchanged answer is a 304 with no body. CORS headers are sent, from any origin unless the server is told otherwise: requests authenticate with a key in a header, not a cookie.

Limits and errors #

StatusWhen
401 no key, or not one this server issued
403 a write with a read-only key, a table the key does not reach, or a missing or unknown ?source=
404 no such table or row
409 a unique column already has that value
422 values that do not fit, with errors by column
429 over the key's limit; every answer carries x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset, and this one retry-after