The Semantic Layer has two APIs: GraphQL, for metadata and queries over HTTP, and an Arrow Flight SQL endpoint, reached through JDBC or ADBC, that returns results as Arrow tables. From Python you rarely need to talk to either directly: dbt Labs’ dbt-sl-sdk wraps both, picks the right one for each call and returns pyarrow tables.

Copy and paste

pip install "dbt-sl-sdk[sync]" pandas
export DBT_SL_HOST=semantic-layer.cloud.getdbt.com   # EMEA: semantic-layer.emea.dbt.com
export DBT_SL_ENVIRONMENT_ID=123456
export DBT_SL_TOKEN=dbtc_...                         # service token or personal access token
# sl_query.py
import os

from dbtsl import SemanticLayerClient

client = SemanticLayerClient(
    environment_id=int(os.environ["DBT_SL_ENVIRONMENT_ID"]),
    auth_token=os.environ["DBT_SL_TOKEN"],
    host=os.environ["DBT_SL_HOST"],
)

with client.session():                      # every API call goes inside a session
    # what exists
    for m in client.metrics():
        print(m.name, "-", m.description)
    print([d.qualified_name for d in client.dimensions(metrics=["revenue"])])   # customer__country, ...

    # a query: same names and filters as dbt sl query
    table = client.query(
        metrics=["revenue", "order_count"],
        group_by=["metric_time__month", "customer__country"],
        where=["{{ TimeDimension('metric_time', 'day') }} >= '2026-01-01'"],
        order_by=["metric_time__month", "-revenue"],
        limit=100,
    )
    df = table.to_pandas()
    print(df)

    # a saved query, and the SQL a query would run
    print(client.query(saved_query="monthly_sales_by_country").to_pandas())
    print(client.compile_sql(metrics=["revenue"], group_by=["metric_time__month"]))

What the client does

The parameters mirror dbt sl query: group_by uses the same names (customer__country, metric_time__month), where takes the same Jinja filter strings, order_by takes names with a - for descending order, and saved_query replaces metrics and group_by. A dimension’s qualified_name (customer__country) is the form to use in group_by; its name alone (country) is not. The SDK validates the combination before sending it: an order_by on a column that isn’t in metrics or group_by fails locally with “Cannot determine if the specified order_by clause … is a metric or a dimension/entity”.

Async applications

In FastAPI or another asyncio application, install dbt-sl-sdk[async] and use from dbtsl.asyncio import AsyncSemanticLayerClient with async with client.session() and await on each call. The sync client would block the event loop.

Without the SDK

Tokens

A service token needs the Semantic Layer Only and Metadata Only permissions; a personal access token runs queries with the user’s own credentials. Keep tokens in environment variables or a secrets manager, never in notebooks you share.

The parameters were validated with dbt-sl-sdk 0.13.4; the endpoints and the API flow come from the dbt documentation (October 2026).

References