REST API

JSON over HTTPS at https://rows.page/api/v1, with CORS open to every origin.

The OpenAPI 3.1 description is at /openapi.json.

Authentication

Send Authorization: Bearer <credential>. The prefix decides what it is.

Dataset token
rpt_ + 32 characters. Returned once when a dataset is created, and in the owner link. Grants new versions, findings, edits and delete for that one dataset.
API key
rpk_ + 32 characters. Created on your account page and shown once. Grants everything on your account's datasets, plus keyed pushes.
Firebase ID token
What the signed-in browser sends. You won't need it from scripts.

Keeping an anonymous dataset takes both: your account in Authorization and the dataset token in X-Rows-Token.

Endpoints

Paths are relative to /api/v1.

  • POST /push

    Push a file (raw body). Always answers JSON.

    Auth: optional

  • GET /datasets/<id>

    Meta, versions, findings and a preview of the latest version.

    Auth: public or owner

  • PATCH /datasets/<id>

    Change title, summary, id_column or visibility.

    Auth: owner

  • DELETE /datasets/<id>

    Delete the dataset.

    Auth: owner

  • GET /datasets/<id>/versions/<n>

    Version meta and preview.

    Auth: public or owner

  • GET /datasets/<id>/versions/<n>/data

    Raw bytes, with Range support. Also at /<id>/v<n>/raw.<format>.

    Auth: public or owner

  • POST /datasets/<id>/findings

    Pin a finding: {title, note, q, sort, cols, sql, version, row_count}.

    Auth: owner

  • PATCH, DELETE /datasets/<id>/findings/<fid>

    Edit or remove a finding.

    Auth: owner

  • POST /datasets/<id>/keep

    Attach an anonymous dataset to your account.

    Auth: account + X-Rows-Token

  • POST /datasets/<id>/report

    Report abuse.

    Auth: none

  • GET /datasets

    Your datasets, cursor paging.

    Auth: account

  • GET /me

    Account, plan, limits and usage.

    Auth: account

  • GET, POST, DELETE /keys, /keys/<id>

    Manage API keys.

    Auth: signed in

  • POST, PUT /uploads, /uploads/<id>/<part>, /uploads/<id>/complete

    Multipart upload for files over 100 MB, in 64 MB parts.

    Auth: Pro or Max

  • GET /fetch?url=

    Capped proxy for remote files that block CORS.

    Auth: none

  • GET /health

    {ok, version}

    Auth: none

Push

POST /api/v1/push takes the file as the raw request body, with parameters in the query string or X-Rows-* headers. It always answers JSON. PUT to https://rows.page/<filename> is the same push for curl -T.

shell
curl --data-binary @orders.csv \
  -H "Authorization: Bearer rpk_..." \
  "https://rows.page/api/v1/push?key=nightly-orders&id_column=order_id&title=Nightly%20orders"

Push response

id string
Dataset id.
version number
Version number, 1 for a new dataset.
url string
Dataset page. Public datasets open for anyone with the link.
owner_url string | null
Page URL with #t=<token>, which opens with owner controls. Creation only.
token string | null
Dataset token (rpt_...) for re-pushes and findings. Creation only, so store it.
format string
Detected format.
bytes number
File size in bytes.
rows number | null
Row count, null if it couldn't be counted.
rows_exact boolean
False when rows is an estimate.
columns array
{name, type} per column. Nested fields use dot paths.
expires_at string | null
When an anonymous dataset gets deleted. Null once it's kept.
unchanged boolean
True when the file matched the latest version, so no version was added.
changes object | null
On a new version: previous_version, rows_delta, columns_added, columns_removed, diff_url.
keep object | null
{required, url, message}: whether the dataset still needs keeping, and where.
limits object
plan, max_file_bytes, datasets_used, datasets_limit.

Errors

Errors share one shape: error is a stable code, message is for humans, and request_id helps us find the request.

402 Payment Required
{
  "error": "upgrade_required",
  "message": "Free accounts keep 10 datasets. Pro keeps 100.",
  "reason": "keep_limit",
  "plan_needed": "pro",
  "upgrade_url": "https://rows.page/pricing",
  "request_id": "9f2c1a7b"
}
201
New dataset created.
200
New version added, or unchanged: true when the file matches the latest version.
400 bad_format
The body isn't readable CSV, TSV, JSON, JSONL or Parquet.
401 / 403
Missing, invalid or wrong dataset token or API key.
402 upgrade_required
A plan limit was hit. The body has reason, plan_needed and upgrade_url.
413 too_large
Over the request or plan size cap. The body names your cap and the multipart API.
429
Rate limited. Retry after Retry-After seconds.