Offerings
Features & Capabilities
Customer Stories
Learn
Engage
Books
A semantic layer that's built like a real programming language, not duct-taped together from YAML and SQL strings. So your business logic stays reusable, composable, and maintainable like real code.
Model orders { table_name: 'ecommerce.orders' dimension status { type: 'text' } dimension created_at { type: 'datetime' } measure revenue { type: 'number' definition: @aql sum(order_items.quantity * products.price) ;; } } # assemble models into a governed dataset Dataset ecommerce { models: [orders, order_items, products, users] relationship(order_items.order_id > orders.id) relationship(order_items.product_id > products.id) }
Model your metrics, dimensions, and relationships in AML. It compiles to native SQL on your warehouse, and every consumer queries the same governed definitions.
Drag-and-drop exploration and AI chat on governed datasets.
iframe & JS SDK, SSO/JWT, row-level security, white-label.
Query by metric name over MCP: Claude, Cursor, Slack bots.
Programmatic access for custom apps and data pipelines.
Pull governed metrics into Python or your own product.
Governed metrics in Google Sheets for last-mile analysis.
"We wanted to define analytics logic centrally and have people reuse it, to get faster speed to insight. With Holistics, we can have correct, reusable measurements and metrics going out there."
YAML was built to serialize data, not to model a business, so when logic repeats you copy-paste it or template SQL with Jinja. AML is a real language: functions, types, and inheritance let you write logic once and reuse it everywhere, so nothing drifts.
# 50 country tiles = 50 copies of the same block tiles: - type: single_value title: "United States" metric: orders.revenue filter: field: users.country operator: is value: "United States" # …49 more, copy-pasted, one per country # reuse? hardcode the list in Jinja and pray: {% for c in ["United States", "Germany", …] %} - type: single_value title: "{{ c }}" metric: orders.revenue filter: {field: users.country, value: "{{ c }}"} {% endfor %}
Func revenue_kpi(country: String) { VizBlock { label: country viz: SingleValue { dataset: ecommerce series { field { ref: r(orders.revenue) } } filter { field: r(users.country) operator: 'is' value: country } } } } Dashboard country_kpis { block kpi1: revenue_kpi('United States') block kpi2: revenue_kpi('Germany') // …one function, 50 tiles }
"I have KPI widgets that appear in 50 rows of my dashboard. I took one function, parameterized it, and just pass in the variables. The ability to parameterize a widget and reuse it was a game changer."
extend
Declare the delta, not a copy. Edits to the base flow through; the compiler merges and catches conflicts at build time.
Model active_users = users.extend({ dimension activated_at {} })
Func
Typed, parameterized generators for metrics and partials. Named args, defaults, inferred return types.
Func revenue_for(p: Model) { metric order_value { … } }
Partial
Bundle a set of metrics once, then snap it onto any dataset. One metric store, reused everywhere.
Dataset us = us_market.extend(revenue_pack)
Change a threshold in one place; everything downstream updates. Branch logic with if / else.
if / else
const HIGH_VALUE = 25000
Organize logic into modules and reuse the same object across tenants, so multi-client modeling stays clean instead of copying folders.
A static type system validates as you write, with autocomplete, go-to-definition, and compile-time errors. TypeScript for analytics.
"I can simply extend my existing data model by referencing a different schema. Extend gives me a clean way of doing this, so when the schema changes, I don't have to worry about editing in two different places."
Every definition (models, metrics, dashboards, charts) is plain-text code in Git. Every change goes through a reviewable pull request instead of a silent click in a UI.
metric revenue = - sum(orders.amount) + sum(order_items.quantity * products.price) + | where(orders.status != 'refunded')
revenue | where(orders.status != 'refunded') | relative_period(-1 year)
A dedicated analytics query language (AQL) that queries your models, not raw tables, and compiles to native SQL.
Metrics build on other metrics, by name. Redefine the base and everything derived from it updates automatically, with no copies to fall out of sync.
metric revenue = sum(order_items.quantity * products.price)
metric arpu = revenue / count(users.id)
metric ltv = arpu * lifespan_months
revenue
arpu
ltv
"The semantic layer is Holistics' biggest draw, because I was able to build reusable models, dimensions, and metrics — and then describe them as code."
Because your whole BI is plain-text AML, you're never locked into one way of working. Build visually in the GUI, write AML in your IDE, or hand the repo to a coding agent. Every path produces the same governed model.
Model by clicking. The visual builder writes AML for you and stays in two-way sync with the code.
Write AML directly, with types, autocomplete, and Git. Software rigor for your business logic.
Because it's code, agents develop on it natively: the built-in Copilot, or Claude Code and Cursor in your IDE. They write the AML; you review the PR and validate the SQL.
Every path produces the same AML, governed in Git and compiled to native SQL.
No proprietary engine, no data leaving your warehouse, no logic you can't read. A programmable layer on top of the stack you already run.
Definitions are plain-text AML in your own Git repo — not rows in a vendor database. Leave anytime, with readable logic.
Every query compiles to native SQL you can read. No black-box execution engine, nothing hidden.
Pushes computation down to your data, so nothing moves and results stay fresh. Snowflake, BigQuery, Postgres, Redshift, Databricks, and more.
Sits above your transformation layer and reads the models you already build. Not a rip-and-replace.
No. YAML in Git is still YAML — no functions, no types, no real composition; reuse means copy-paste or Jinja string-templating. AML is a typed language where reuse (extend, Func, Partial) is a first-class feature the compiler checks for you.
"We add a new client, copy and paste all the data models, and change the data source. Holistics effectively allows us to be repeatable."
In production across hundreds of enterprises.
Go deeper: AML vs YAML · the reusability guide · the AQL query language →