Analytics API: How to Serve Governed Metrics to Any Consumer

An analytics API exposes your metrics programmatically. Here's how to build one that serves dashboards, AI agents, and customer integrations from the same definitions.

6 min read

You have metrics. Revenue, active users, churn, usage per customer. They live in your data warehouse. Now you need to serve them to multiple consumers: your product's frontend, your customers' integrations, an AI agent, a scheduled report generator.

The first instinct is custom API endpoints. One endpoint for revenue by region. Another for churn by plan. Another for the customer dashboard. Each endpoint has its own SQL query, its own response format, its own maintenance burden. By the twentieth endpoint, you're running a bespoke analytics service.

An analytics API backed by a semantic layer gives you one query interface that serves every consumer. Define metrics once. Query them from anywhere.

What is an analytics API?

An analytics API is a programmatic interface for querying metrics. Instead of connecting to a database and writing SQL, consumers call an API endpoint with the metrics they want and get structured results back.

curl https://analytics.example.com/v1/query \
  -H "Authorization: Bearer bon_pk_..." \
  -d '{
    "measures": ["orders.total_revenue", "orders.order_count"],
    "dimensions": ["orders.region"],
    "timeDimensions": [{
      "dimension": "orders.created_at",
      "granularity": "month",
      "dateRange": ["2026-01-01", "2026-03-31"]
    }],
    "orderBy": { "orders.total_revenue": "desc" }
  }'

The response is structured JSON:

{
  "data": [
    { "orders.region": "EMEA", "orders.total_revenue": 142000, "orders.order_count": 340, "orders.created_at": "2026-01-01" },
    { "orders.region": "APAC", "orders.total_revenue": 98000, "orders.order_count": 220, "orders.created_at": "2026-01-01" }
  ]
}

Every consumer uses the same interface. The dashboard frontend, the customer's API integration, the scheduled report, and the AI agent all query the same endpoint with the same metric definitions. The analytics API handles query generation, execution, caching, and access control.

Why not just build API endpoints?

Custom endpoints work at small scale. They break at medium scale:

Metric drift. /api/revenue and /api/dashboard/revenue both return "revenue" but use different SQL queries. One gets updated. The other doesn't.

N+1 endpoint problem. Every new metric or dimension combination is a new endpoint. Revenue by region. Revenue by plan. Revenue by region AND plan. Revenue by region AND plan AND month. The combinatorial explosion is real.

No caching strategy. Each endpoint hits the warehouse on every request. Response times grow with data volume. You end up building a caching layer per endpoint.

No access control. Each endpoint implements its own auth. Customer A's key works on all endpoints or none. Per-tenant, per-metric access control is custom code.

An analytics API backed by a semantic layer handles all of this from a single query interface.

Building an analytics API with a semantic layer

Define your metrics

cubes:
  - name: orders
    sql_table: public.orders
    measures:
      - name: total_revenue
        sql: "CASE WHEN status != 'refunded' AND type != 'trial' THEN amount ELSE 0 END"
        type: sum
      - name: order_count
        type: count
    dimensions:
      - name: region
        sql: region
        type: string
      - name: plan
        sql: plan_name
        type: string
      - name: created_at
        sql: created_at
        type: time
    security_context:
      - name: tenant_filter
        sql: "{SECURITY_CONTEXT.tenant_id} = customer_id"

Deploy the API

bon deploy

Your analytics API is live. The REST endpoint, TypeScript SDK, and MCP server all serve these governed definitions. No custom endpoint code.

Query from anywhere

TypeScript SDK:

import { createClient } from "@bonnard/sdk";

const bon = createClient({ apiKey: "bon_pk_..." });

const { data } = await bon.query({
  measures: ["orders.total_revenue"],
  dimensions: ["orders.region"],
  orderBy: { "orders.total_revenue": "desc" },
});

Raw SQL via SDK:

const { data } = await bon.sql(
  `SELECT region, MEASURE(total_revenue) FROM orders GROUP BY 1 ORDER BY 2 DESC`
);

React components:

import { BonnardProvider, BarChart, useBonnardQuery } from "@bonnard/react";
import "@bonnard/react/styles.css";

// Charts query the same API, same definitions, same access controls

AI agents via MCP: Agents call explore_schema and query through the same governed interface.

One set of metric definitions. Four consumer types. Same numbers everywhere.

Analytics API vs alternatives

Approach Metric governance Multi-tenant Caching Maintenance
Custom REST endpoints None (per-endpoint SQL) DIY DIY High (per endpoint)
GraphQL API Schema-level DIY Per-resolver Medium-high
Direct warehouse access None Manual None Low (but dangerous)
BI tool API (Looker, Metabase) Tool-specific Tool-specific Tool-specific Medium
Semantic layer API (Bonnard) YAML definitions Structural (token exchange) Pre-aggregation Low (schema changes only)

When you need an analytics API

You need one when:

  • Multiple consumers query the same metrics (frontend + backend + AI agents)
  • Customers integrate with your data via API
  • You're building embedded analytics with a frontend SDK
  • AI agents need programmatic access to governed data
  • Your data team is tired of building custom endpoints

You don't need one when:

  • A single dashboard covers all use cases
  • Nobody queries your metrics programmatically
  • The data model is so simple that direct SQL is fine

Getting started

Cloud:

npm install -g @bonnard/cli
bon init
bon deploy

Self-hosted:

npx @bonnard/cli init --self-hosted
docker compose up -d
bon deploy

Your analytics API is live after bon deploy. Query via REST, TypeScript SDK (@bonnard/sdk), React SDK (@bonnard/react), or MCP for AI agents (bon mcp).

For the full architecture: What Is a Semantic Layer?. For customer-facing use cases: How to Build Customer-Facing Analytics for B2B SaaS.

Self-host free under Apache 2.0, or use Bonnard Cloud for managed infrastructure.

Frequently asked questions

What is an analytics API?

An analytics API is a programmatic interface for querying business metrics. Instead of writing SQL against a database, consumers call an API with the measures, dimensions, and filters they want. The API handles query generation, caching, and access control.

How is an analytics API different from a REST API?

A generic REST API exposes resources (users, orders, invoices). An analytics API exposes metrics (revenue, churn rate, active users) with aggregation, filtering, and time dimensions built in. The query interface is designed for analytical questions, not CRUD operations.

Do I need an analytics API for AI agents?

If AI agents query your data, an analytics API is the governed path. The alternative is giving agents direct database access (dangerous) or text-to-SQL (inconsistent). An analytics API backed by a semantic layer gives agents governed access to metric definitions. See What Is an Agentic Semantic Layer?.

What about GraphQL for analytics?

GraphQL works for analytics APIs but you end up building the aggregation, caching, and access control layer yourself. A semantic layer provides these out of the box with a purpose-built query interface for analytical workloads.

Ready to ship a customer-ready MCP?

Turn your semantic layer, dbt, or warehouse into a governed, per-customer MCP for your customers' agents.