
In this blog, I'll share a developer's perspective on what the new API enables, my learnings from experimenting, and some practical tips and use cases for getting started.

In most organisations, business logic is fractured. Some of it lives in dashboards that don't quite agree, lives in internal tools, scripts, and one-off integrations, each quietly re-implementing business logic in its own way. Even with the best intentions, numbers start to drift over time.
Steep already solves this problem for analytics. By centralising definitions in one semantic model, all numbers stay consistent, understandable, and easy to govern.
With the Steep API, that same semantic model is now available beyond the app itself. Internal tools, custom workflows, and integrations can query data using the same trusted definitions your analysts already rely on.
As of writing this post, we've just released the first version of the API which lets you:
Let's get started with a simple example. Inside the Steep app, open up Settings → API, and generate an API key. For now, we'll only need the “Read metrics” and “Query metric data” scopes, but feel free to pick as many as you feel might be useful.
💡 API keys can't be edited, and the key itself will be encrypted and never shown to you again in the UI, so make sure to save it. More details can be found in the help center.
Let’s start by listing the metrics available in your semantic layer. To authorize the request using the API key, set the Authorization header to your API key prefixed with ApiKey . I’ll use curl in the command line for these examples, but the same principles apply when calling the API programmatically.
curl https://api.steep.app/v1/metrics \
-H "Authorization: ApiKey <Your API key>" \
-G
You should receive a response that follows this structure.
{
"cacheTtlInSeconds": 14400,
"refreshedAt": "2026-01-15T10:13:35.994Z",
"sql": "SELECT
SUM("orders"."order_amount")::float8 AS "metric",
DATE_TRUNC('month', "orders"."order_date")::date AS "time"
FROM
"public"."orders" "orders"
WHERE
"orders"."order_date"::date >= '2025-01-01' AND "orders"."order_date"::date <= '2026-02-01'
GROUP BY
2
ORDER BY
2",
"total": 13,
"data": [
{
"time": "2025-01-01T00:00:00.000Z",
"metric": 35285
},
{
"time": "2025-02-01T00:00:00.000Z",
"metric": 97407
},
...
]
}
💡 Many fields will be returned as null, because by default, joined resources such as dimensions, modules, slices, etc. aren’t resolved. If you want the full details of your metrics, add expand=true as a query parameter.
Now let’s pick a metric and run a query. This time, it’s a POST request with a JSON body. Make sure to pick a timeGrain supported by the metric definition, and include a fromDate and a toDate.
curl https://api.steep.app/v1/metrics/<Your metric ID>/query
-H "Authorization: ApiKey <Your API key>"
-H "Content-type: application/json"
-d '{"timeGrain":"monthly","fromDate":"2025-01-01T00:00:00.000Z","toDat
You should receive a response that follows this structure, with one data point for each month. Note that the generated SQL is also included, which makes it easy to debug and understand exactly what is being executed against your data source.
{
"cacheTtlInSeconds": 14400,
"refreshedAt": "2026-01-15T10:13:35.994Z",
"sql": "SELECT
SUM("orders"."order_amount")::float8 AS "metric",
DATE_TRUNC('month', "orders"."order_date")::date AS"time"
FROM
"public"."orders" "orders"
WHERE
"orders"."order_date"::date >= '2025-01-01' AND "orders"."order_date"::date <= '2026-02-01'
GROUP BY
2
ORDER BY
2",
"total": 13,
"data": [
{
"time": "2025-01-01T00:00:00.000Z",
"metric": 35285
},
{
"time": "2025-02-01T00:00:00.000Z",
"metric": 97407
},
...
]
}
Now that you have the data, it’s up to you to decide what to do with it. The sky is the limit.
Want to set up a leaderboard and rank everyone in the team based on how much the company’s revenue has increased since they joined?
Easy: Get the join date from Slack → Query the revenue metric between those specific dates and now → Sort and compare in a list.
Are you managing a shopping center and want to see store performance overlaid on your custom indoor map?
Easy: Map store IDs to custom map locations → Query metrics with a breakdown on store IDs → Show results on the map.
In both cases, the metric logic is defined in the same semantic model, and every tool gets the same answer.
But custom bespoke software must be really expensive and time-consuming to build, right? That’s not necessarily true, I was surprised to learn how straightforward it was and how little glue code was needed. Once the semantic model is there, most of the work becomes product thinking rather than data plumbing.

Active users by workspace. Quite evenly distributed.

Query volume by workspace. One of these is not like the others 🧐
Another experiment was this viz: Monthly order volume, displayed as dollar bills falling from the sky. Anything is possible.

This initial version of the API is only the beginning, and we’ll be expanding it over time.
We’re starting with read and query use cases, and plan to expand into write and management workflows over time. We want to ensure we’re heading in the right direction and are prioritising the right things, so don’t hesitate to reach out with your feedback and ideas.
The API has just been released for all users, and we’re excited to see what you’ll create with it. Head on over to the documentation to get started, and begin hacking away at your ideas and experiments today.
You can create a workspace here if you don't already have one.
Get help with exploring the platform and discussing use cases for your team.
Explore for free. Get your own workspace or try the demo setup.
More posts

By Nino Höglund·Product updates

By Nino Höglund·Product updates

By Johan Baltzar·Data culture

By Deanne Anderson·Events

By Nino Höglund·Product updates

By Nino Höglund·Product updates