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.
Before you start
Section titled “Before you start”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, insidecurrent_path; the receipt’s owntable_idfield 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.
Download the current release
Section titled “Download the current release”GET /api/v2/tables/{table_id}/currentx-api-key: mr_live_…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:
| Header | Meaning |
|---|---|
ETag | A strong tag derived from the bytes’ SHA-256. Stable for as long as the release is current. |
Content-Digest | The 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-Length | Exact. The body is read and verified in full before it is streamed. |
X-Request-Id | Quote this in any support request; it names the exact event on our side. |
Polling without re-downloading
Section titled “Polling without re-downloading”Send the ETag you already hold and you get a 304 instead of the bytes when nothing has changed:
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.
What “current” means
Section titled “What “current” means”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.
Limits
Section titled “Limits”GETandOPTIONSonly.HEADanswers405.- Range requests are not supported. The response says so with
Accept-Ranges: none, so use a whole-object fetch rather thanwget -c. - Artifacts are capped at 256 MiB. Larger releases answer
413. - Requests are rate-limited per key. A
429carriesRetry-Afterwhen the limit can say how long to wait.
Ask whether the release is still current
Section titled “Ask whether the release is still current”GET /api/v2/tables/{table_id}/freshnessx-api-key: mr_live_…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.availablesays whether there is a passing release to download.current.etagmatches theETagthe download route returns for that release, so you can compare it with a file you already have without fetching it. It isnullwhen a release exists but its coordinate could not be read — a different fact fromavailable: false.freshness.statesays 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:
| state | meaning |
|---|---|
fresh | Within the expected publication schedule. |
awaiting_update | An update is due inside the expected window and has not arrived yet. |
learning | Not enough evidence yet to promise a schedule. |
stale | The expected window passed without an update. |
source_unhealthy | Two or more consecutive reads of a source failed. The last good release is still served. |
dormant | The source is retired or out of service and is not expected to change. |
unknown | Freshness 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.
Responses
Section titled “Responses”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.
| Status | Download | Freshness |
|---|---|---|
200 | Parquet bytes. | Freshness JSON, whether or not a release exists. |
304 | Your If-None-Match matches the current release. | — |
401 | Missing, malformed, revoked, or expired key, or a key that is not mr_live_. | Same. |
402 | The key is valid but the subscription is not active. | Same. |
404 | Unknown, foreign, or not-yet-activated Table id. These are deliberately indistinguishable. | Same. |
405 | HEAD or any method other than GET/OPTIONS. | Same. |
409 | The Table exists but has no passing release yet. | — (reported as current.available: false) |
413 | The release exceeds 256 MiB. | — |
429 | Per-key rate limit. Retry-After when known. | Same, always with Retry-After. |
502 | Upstream integrity failure: the stored bytes did not match the release’s recorded digest, or the release metadata was inconsistent. Nothing was served. | — |
503 | The service is temporarily unavailable. Retry after the Retry-After interval. | Same. |
504 | The upstream storage read timed out. Nothing is known to be wrong with the release. | — |
Retrying
Section titled “Retrying”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.
Privacy and accounting
Section titled “Privacy and accounting”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.
See also
Section titled “See also”- Hosted manifest — how the SDK uses the same key
- Credentials
- Errors & exceptions