Querying the semantic layer via REST
Call the semantic layer over REST with a metrics × dimensions × filters payload and read certified results - conceptual request and response shapes.
The datahashi team4 min read

A Semantic Query is a structured object: metrics × dimensions × filters. BI tools, apps, and MCP agents send the same shape. Over REST you send that object with a governed credential and get certified rows back. Callers never invent SQL.
This post walks through the conceptual request and response. Exact URL paths live in the API reference; treat the payloads below as the contract you design against.
What you send
Authenticate with a bearer token (API key for services, or the token from an OAuth sign-in for interactive clients). The credential carries Data Roles from policy.yaml. A role grants the metrics, dimensions, and measures that caller may use. Nothing in the request body can widen those grants.
Conceptual request body:
{
"metrics": ["avg_order_value"],
"dimensions": [
"Customer.region",
{
"name": "ordered_at",
"entity": "Order",
"grain": "month"
}
],
"filters": [
{
"dimension": "status",
"entity": "Order",
"op": "eq",
"value": "completed"
}
],
"order": [{ "ref": "ordered_at" }],
"limit": 100
}
Field meanings:
- metrics - Governed metric names from the published model.
- dimensions - Group-bys as
Entity.nameor{ name, entity, grain }for time buckets. - filters - Structured predicates (
eq,ne,lt,le,gt,ge,in,not_in,contains,starts_with,ends_with,is_null). Never raw SQL. - order / limit - Sort and row cap; limit is also bounded by the workspace cost policy.
- view (optional) - Restrict the query to a curated view.
- version (optional) - Pin a published model version; defaults to current.
datahashi resolves join paths, keeps aggregations fan-out-safe, and compiles SQL for your warehouse (nine engines, including Snowflake, BigQuery, Postgres, and MySQL). A Query Runner you control runs the query; results are not copied into a separate store as part of ordinary querying.
What you get back
A successful response is certified rows for the metrics and dimensions you asked for - same definitions a console user or MCP agent would get for the same object. You do not receive the compiled SQL in the agent/MCP path, and REST clients should treat the semantic result as the contract, not reverse-engineer warehouse SQL.
Errors are a flat JSON object: error, code, and reason, plus ref and suggestions when the engine has them. Codes you can branch on include:
| HTTP | Code | When |
|---|---|---|
| 400 | invalid | Bad grain, bad operator, type mismatch, or ambiguity |
| 401 | unauthenticated | Missing or invalid bearer token |
| 403 | forbidden | Not permitted, a row filter could not be applied, or the runner policy denied the query |
| 404 | not_found | Unknown or ungranted name. The two look the same. |
| 429 | over_budget | Query exceeds the workspace cost policy |
| 503 | overloaded | Service at capacity |
Governance is enforced in the request shape: an unknown name and an ungranted name fail the same way, a grant that omits raw measures cannot be widened by asking for them, and filters are structured rather than appended as SQL fragments after the fact.
Same shape as MCP and the console
If you already use MCP, the mapping is direct. run_semantic_query takes the same metrics, dimensions, and filters. list_catalog and describe_metric help agents discover names before they call. The server also exposes drill_down, refine_semantic_query, and list_dimension_values. None of the six tools accept or return SQL.
The console playground speaks the same object. That is how you verify an app integration: run the payload in the playground, then send it over REST with the service credential. Matching results mean the metric is certified end to end - not that two SQL dialects happened to agree.
Every call is metered, so you can attribute volume to the API key or agent that sent it.
Practical checklist
- Publish the model (immutable artifact with access rules).
- Issue a governed credential for the app.
- Send metrics × dimensions × filters - no SQL in the body.
- Handle stable error codes instead of scraping messages.
- Pin
versionwhen you need a fixed cut for a release.
REST is not a second semantic layer. It is one of three doors (console, REST, MCP) into the same published definitions. Ask for the number by name; let the layer write the SQL.
Product notes
An occasional email when we ship something or write something worth reading.