Skip to content

Hosted Table API

Every Dataset you build and activate in Mostly Right Cloud exposes its Tables over two small HTTPS endpoints: one returns the latest passing Parquet release, the other tells you whether that release is still current. Both use your subscription’s API key and nothing else.

You need two things:

  • An active subscription’s API key, mr_live_…, from app.mostlyright.md. Device credentials issued to the CLI (mr_cli_…) are not accepted here.
  • A Table id. It is minted when a Dataset is activated and is shown on the Table’s page in the app under Use this Table, together with a ready-made curl. The CLI’s activation receipt carries it too, inside current_path; the receipt’s own table_id field is a different, internal identifier and is not what these routes take.

The base URL is https://api.mostlyright.md. The same routes also answer on the app’s own origin.

GET /api/v2/tables/{table_id}/current
x-api-key: mr_live_…
Terminal window
curl -sS -o table.parquet \
-H "x-api-key: $MOSTLYRIGHT_API_KEY" \
"https://api.mostlyright.md/api/v2/tables/$TABLE_ID/current"

A 200 is the Parquet file itself (Content-Type: application/vnd.apache.parquet), served from Mostly Right’s own servers — never a redirect to object storage, so your key is never forwarded anywhere and the URL you hold is never a replayable signed link.

Each successful response carries:

HeaderMeaning
ETagA strong tag derived from the bytes’ SHA-256. Stable for as long as the release is current.
Content-DigestThe SHA-256 of the bytes you received, measured server-side before the first byte was sent, in RFC 9530 form (sha-256=:<base64>:). X-MostlyRight-Content-Digest carries the same digest as sha256:<hex>.
Content-LengthExact. The body is read and verified in full before it is streamed.
X-Request-IdQuote this in any support request; it names the exact event on our side.

Send the ETag you already hold and you get a 304 instead of the bytes when nothing has changed:

Terminal window
curl -sS -o table.parquet -w '%{http_code}\n' \
-H "x-api-key: $MOSTLYRIGHT_API_KEY" \
-H 'If-None-Match: "sha256-…"' \
"https://api.mostlyright.md/api/v2/tables/$TABLE_ID/current"

A 304 is not counted as a download.

Each request resolves the Table’s latest passing release once, then serves exactly that immutable version. If a refresh publishes a newer release while your request is in flight, you still receive the version that was current when you asked; the next request returns the new one.

  • GET and OPTIONS only. HEAD answers 405.
  • Range requests are not supported. The response says so with Accept-Ranges: none, so use a whole-object fetch rather than wget -c.
  • Artifacts are capped at 256 MiB. Larger releases answer 413.
  • Requests are rate-limited per key. A 429 carries Retry-After when the limit can say how long to wait.
GET /api/v2/tables/{table_id}/freshness
x-api-key: mr_live_…
Terminal window
curl -sS -H "x-api-key: $MOSTLYRIGHT_API_KEY" \
"https://api.mostlyright.md/api/v2/tables/$TABLE_ID/freshness"

The answer is JSON and deliberately separates two questions:

{
"schema_version": "mostlyright-cloud-table-freshness.v1",
"request_id": "…",
"dataset_id": "…",
"table_id": "…",
"current": { "available": true, "etag": "\"sha256-…\"" },
"freshness": {
"state": "awaiting_update",
"reason_code": "within_expected_window",
"confidence": "high",
"continuity": "continuous",
"blocking_source": null,
"last_observed_at": "2026-08-22T11:00:00.000Z",
"last_changed_at": "2026-08-22T06:00:00.000Z",
"data_through": "2026-08-22",
"expected_next_earliest": "2026-08-22T18:00:00.000Z",
"expected_next_latest": "2026-08-22T22:00:00.000Z",
"last_good_after_failed_refresh": false,
"sources_incomplete": false,
"unlisted_source_count": 0,
"sources": [ … ]
}
}

(sources is abridged here; each entry carries the same state, reason, confidence, and timestamp fields for one input.)

  • current.available says whether there is a passing release to download. current.etag matches the ETag the download route returns for that release, so you can compare it with a file you already have without fetching it. It is null when a release exists but its coordinate could not be read — a different fact from available: false.
  • freshness.state says whether that release is still the one you would expect given the sources’ publication schedule. A stale Table still serves its last passing bytes; nothing is withdrawn.

freshness.state is a closed vocabulary:

statemeaning
freshWithin the expected publication schedule.
awaiting_updateAn update is due inside the expected window and has not arrived yet.
learningNot enough evidence yet to promise a schedule.
staleThe expected window passed without an update.
source_unhealthyTwo or more consecutive reads of a source failed. The last good release is still served.
dormantThe source is retired or out of service and is not expected to change.
unknownFreshness could not be established. Nothing is claimed.

unknown is the answer whenever the evidence does not decide; the API never reports fresh as a fallback. When sources_incomplete is true, do not derive your own verdict from sources — the input that would have blocked the Dataset may be one of the ones not listed.

The freshness document is never cached (Cache-Control: no-store) and has no ETag of its own: it is a claim about the passage of time, and a cached copy would become wrong in exactly the direction that matters.

Every JSON refusal from either route uses one envelope — { "schema_version", "error", "request_id" } — with the same id in X-Request-Id. The two exceptions have no body to carry it: a 405 for HEAD, and a 503 from a maintenance fence, which is plain text.

StatusDownloadFreshness
200Parquet bytes.Freshness JSON, whether or not a release exists.
304Your If-None-Match matches the current release.
401Missing, malformed, revoked, or expired key, or a key that is not mr_live_.Same.
402The key is valid but the subscription is not active.Same.
404Unknown, foreign, or not-yet-activated Table id. These are deliberately indistinguishable.Same.
405HEAD or any method other than GET/OPTIONS.Same.
409The Table exists but has no passing release yet.— (reported as current.available: false)
413The release exceeds 256 MiB.
429Per-key rate limit. Retry-After when known.Same, always with Retry-After.
502Upstream integrity failure: the stored bytes did not match the release’s recorded digest, or the release metadata was inconsistent. Nothing was served.
503The service is temporarily unavailable. Retry after the Retry-After interval.Same.
504The upstream storage read timed out. Nothing is known to be wrong with the release.

Treat 429, 503, and 504 as transient and retry with backoff, honouring Retry-After when present. A 503 can be a cold start on our side after a quiet period rather than an outage; a second request a few seconds later normally succeeds. 401, 402, 404, 409, and 413 are not retryable without a change on your side.

A 502 means something upstream did not agree with itself — most often the bytes in storage did not match what the release recorded — so nothing was sent. It is rare and is worth reporting with the X-Request-Id.

Your key id and the path you requested are stored only as keyed SHA-256 hashes; raw keys and signed storage URLs are never persisted. Each successful download is recorded against the exact immutable release it served, together with the measured digest of the bytes, so a delivery can always be traced to the release it came from.