Our engine never sees your warehouse password
datahashi

Query the REST API

Run a Semantic Query, stream CSV, drill into a cell, and list dimension values over HTTP.

Updated

Every consumer sends a Semantic Query to the engine over HTTP. The engine compiles the query into SQL. It applies row-level security. Query Runner then runs the SQL.

Clients never write raw SQL. They never receive warehouse schema details.

Authenticate

Send your data-plane API key or JWT as a Bearer token:

Authorization: Bearer <your-datahashi-key>

The token is the caller. Grants on that caller decide which metrics and measures the query may name.

Run a Semantic Query (POST /v1/query)

curl "$ENGINE_URL/v1/query" \
  -H "Authorization: Bearer $DATAHASHI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "metrics": ["avg_order_value"],
    "measures": ["Order.revenue"],
    "dimensions": [
      "Customer.region",
      { "name": "order_date", "entity": "Order", "grain": "month" }
    ],
    "filters": [
      { "dimension": "status", "entity": "Order", "op": "eq", "value": "completed" }
    ],
    "having": [
      { "ref": "revenue", "op": "gt", "value": 10000 }
    ],
    "order": [
      { "ref": "revenue", "desc": true }
    ],
    "limit": 100,
    "offset": 0
  }'

JSON response shape

{
  "columns": [
    { "name": "region", "type": "string", "kind": "dimension" },
    { "name": "order_date", "type": "timestamp", "kind": "dimension" },
    { "name": "avg_order_value", "type": "float", "kind": "metric" },
    { "name": "revenue", "type": "decimal", "kind": "measure" }
  ],
  "rows": [
    ["na", "2026-03-01T00:00:00Z", 142.5, 45200],
    ["emea", "2026-03-01T00:00:00Z", 138.1, 38100]
  ],
  "row_count": 2,
  "truncated": false,
  "meta": {
    "rollup": { "used": "monthly_order_summary" }
  }
}

columns lines up with each row: dimensions, then metrics, then measures. type is int, float, decimal, string, bool, date, or timestamp. A metric is float. A sum measure is decimal. A count measure is int. There is no annotation array.

Whole amounts are JSON integers. A ratio is the full quotient, not a rounded decimal such as 142.50. The body also includes resolved.

Stream CSV

Add ?format=csv or send Accept: text/csv:

curl "$ENGINE_URL/v1/query?format=csv" \
  -H "Authorization: Bearer $DATAHASHI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "measures": ["Order.revenue"],
    "dimensions": ["Customer.region"]
  }'

Output. The header is the column names. Cells use the same values as JSON, written as text.

region,revenue
na,45200
emea,38100

Drill into a cell (POST /v1/drill-down)

Drill-down turns one aggregated cell into the explore rows behind it. measure is a raw measure, never a metric.

curl "$ENGINE_URL/v1/drill-down" \
  -H "Authorization: Bearer $DATAHASHI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "measure": "Order.revenue",
    "cell": [
      { "dimension": "region", "entity": "Customer", "value": "emea" },
      { "dimension": "order_date", "entity": "Order", "grain": "month", "value": "2026-03-01" }
    ],
    "limit": 50
  }'

The engine turns the cell into an explore query. Time filters are half-open ([2026-03-01, 2026-04-01)).

List dimension values (POST /v1/dimension-values)

Fetch distinct values of a dimension from the warehouse. Use this for dropdowns or search:

curl "$ENGINE_URL/v1/dimension-values" \
  -H "Authorization: Bearer $DATAHASHI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity": "Customer",
    "dimension": "region",
    "q": "em"
  }'

Values follow the caller’s row-level security. q is a case-insensitive substring.

Discover the catalog (POST /v1/catalog)

Inspect granted metrics and dimensions:

curl "$ENGINE_URL/v1/catalog" \
  -H "Authorization: Bearer $DATAHASHI_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

OpenAPI specification

Download the OpenAPI 3.1 specification from the running engine:

curl "$ENGINE_URL/v1/openapi.yaml"

Next: Connect an LLM agent · REST API.