Our engine never sees your warehouse password
datahashi

02Agents & API

Ask for the metric. We write the SQL.

Send metric names, dimensions and filters over REST or MCP. The rows you get back match every other tool.

Request · semantic-query.json
{
  "metrics": ["avg_order_value"],
  "dimensions": [
    "Customer.region",
    { "name": "ordered_at",
      "entity": "Order",
      "grain": "month" }
  ],
  "filters": [
    { "dimension": "status",
      "entity": "Order",
      "op": "eq",
      "value": "completed" }
  ],
  "limit": 100
}
Compiled SQL
SELECT c.region,
  date_trunc('month', o.ordered_at)
    AS ordered_at,
  SUM(o.amount) / COUNT(o.id)
    AS avg_order_value
FROM public.orders o
JOIN public.customers c
  ON o.customer_id = c.id
WHERE o.status = 'completed'
GROUP BY 1, 2
LIMIT 100;
result · example values
regionordered_atavg_order_value
EMEA2026-01-01118.40
EMEA2026-02-01121.05
NA2026-01-01132.90
NA2026-02-01130.15

One request, the SQL it becomes, and the rows that come back.

Who asks

Agents, apps and BI send the same request

One JSON shape, whoever is asking. Agents also get a catalog to choose from.

Agents, over MCP
They sign in with OAuth, see only the metrics they may use and ask for them by name. They can't invent a join or read a hidden row.
Apps, over REST
One endpoint. Apps ask for metrics by name, never for tables.
BI and analysts
The same metric names the agent uses, so the numbers match.
mcp · tools/call
{
  "name": "run_semantic_query",
  "arguments": {
    "metrics": ["revenue"],
    "dimensions": [{
      "name": "ordered_at",
      "entity": "Order",
      "grain": "quarter"
    }]
  }
}

Reference

Eight fields in, six error codes out

Request fields

metrics
Governed metric names, like "avg_order_value".
measures
Raw measure names. Explore mode only.
dimensions
What to group by, including time buckets.
filters
Conditions like eq, gt, in or contains. Never raw SQL.
order
Sort by any field you selected.
view
Limit the query to a curated view.
limit
Row cap. Cost limits apply too.
version
Pin a model version. Defaults to the latest.

Error codes

HTTPCodeWhen
400bad_requestinvalid request
401unauthenticatedmissing or invalid token
403forbiddengoverned key asked for raw measures
404not_foundunknown metric or dimension
429over_budgetover the cost limit
503overloadedservice at capacity

Every error has one shape: { "error": { "code", "message" } }

Full API reference →

What you can rely on

Same request, same answer

  • Joins found for you

    Ask for a measure and a dimension. The model supplies the join.

  • Totals that don't inflate

    One-to-many joins never count a row twice.

  • Averages that match finance

    Computed from totals, never by averaging averages.

  • A request that outlives your warehouse

    Switch warehouses and every caller keeps sending the same JSON.

  • Repeat questions skip the scan

    A short-lived result cache answers a repeated question without touching the warehouse. Opt-in rollups do the same for your busiest ones.

  • One noisy caller can't slow the rest

    A rate limiter answers with a clear 429 or 503 instead of making everyone wait.

We'll publish speed numbers once there's a benchmark. How the password stays in your network →

Send your first request

Follow the query guide, or open the console and create a key.