The hands-off way: loading Jedox into any warehouse with dlt
· Jedox
A declarative, Python-native pipeline that pulls Jedox cubes, dimensions and logs and loads them into DuckDB, Snowflake, BigQuery or a Fabric landing zone - schema and pagination handled for you.
In getting Jedox data into Microsoft Fabric (and any other server) I walked through the manual routes - the OData Hub, the OLAP HTTP API, and the Integrator. This post is about the option I reach for when I want a real, repeatable pipeline without hand-rolling one: dlt (data load tool), the open-source Python library for extract-and-load.
The idea is declarative. You describe the source - base URL, auth, which resources - and dlt does the unglamorous parts: pagination, schema inference, type coercion, state, and loading into one of a dozen destinations. There's a verified Jedox source that wraps Jedox's own APIs, so you don't even write the request code.
1. Get a token
dlt's Jedox source authenticates with a Personal Access Token (bearer), not a username/password. Create one in the Jedox Cloud Console at https://console.cloud.jedox.com/settings under Personal Access Tokens, then drop it into .dlt/secrets.toml:
[sources.jedox_source]
token = "your_personal_access_token_here"
Keeping the token in secrets.toml (or an env var / vault) is the whole point - it never touches your code.
2. Install
uv add "dlt[hub]" # or: pip install "dlt[duckdb]"
3. The pipeline
This is the source-and-pipeline scaffold straight from the dlt Jedox docs - a @dlt.source that points at a Jedox API with bearer auth, lists the resources you want, and a pipeline that runs them into a destination:
import dlt
from dlt.sources.rest_api import RESTAPIConfig, rest_api_resources
@dlt.source
def jedox_source(token=dlt.secrets.value):
config: RESTAPIConfig = {
"client": {
"base_url": "https://logs.{instance}.cloud.jedox.com/logs",
"auth": {"type": "bearer", "token": token},
},
"resources": [
{"name": "logs", "endpoint": {"path": "logs", "data_selector": "rows"}},
{"name": "olap_server", "endpoint": {"path": "api"}},
],
}
yield from rest_api_resources(config)
def get_data() -> None:
pipeline = dlt.pipeline(
pipeline_name="jedox_pipeline",
destination="duckdb", # swap for snowflake / bigquery / postgres / filesystem
dataset_name="jedox_data",
)
load_info = pipeline.run(jedox_source())
print(load_info)
get_data()
Run it:
uv run python jedox_pipeline.py
uv run dlthub show # opens a dashboard of what was loaded
4. What you can actually load
The Jedox source exposes five resources, each wrapping a different Jedox API:
integrator_odata- the OData Hub (JSON, OData v4): cubes, dimensions, stored Views and their cells. This is the one that brings your actual planning and reporting data. Point the clientbase_urlat your OData Flat API URL - the one with the database name at the end,https://odata.<instance>.cloud.jedox.com/<DatabaseName>(the same URL Power BI uses).olap_server- the low-level OLAP HTTP API (CSV responses) for OLAP queries and server-object listings. It works on cloud and on-premise; the host/port vary, so confirm them for your instance.olap_admin- the same OLAP HTTP API entry point for on-premises installs.server_browser- the admin browser for inspecting databases, cubes and dimensions (metadata/lineage).logs- the Cloud Logs API (filtered, paginated; up to 100,000 rows per request) - handy for auditing who changed what.
So for "load my P&L cube into the warehouse," the resource you care about is integrator_odata pointed at your database's OData Flat API; the others are there for metadata and audit.
5. Pick a destination - one line
The reason dlt is worth the dependency: the same pipeline lands in whatever you're feeding, by changing one argument.
pipeline = dlt.pipeline(
pipeline_name="jedox_pipeline",
destination="snowflake", # duckdb | postgres | bigquery | snowflake | redshift | databricks | filesystem
dataset_name="jedox_data",
)
dlt supports DuckDB (the default, great for local dev), PostgreSQL, BigQuery, Snowflake, Redshift, Databricks, and Filesystem (S3 / GCS / Azure). That last one is your bridge into Microsoft Fabric: write Parquet to ADLS / OneLake with destination="filesystem", then shortcut or load it into a Lakehouse - exactly the Pattern B landing from the main post, but with dlt managing the files and schema.
6. Query what landed
data = dlt.pipeline("jedox_pipeline").dataset()
df = data.logs.df() # any resource -> pandas DataFrame
print(df.head())
SELECT * FROM jedox_data.logs LIMIT 10;
dlt, Integrator, or raw requests?
They're not rivals so much as different altitudes:
- Raw
requests(OData / OLAP API) - total control, zero dependencies, perfect for a one-off pull or a reconciliation script. You own the pagination and parsing. - dlt - a Python-native, declarative pipeline with schema inference and a dozen destinations, without building the plumbing. My pick when an analyst wants Jedox in a warehouse and values Python over the Jedox stack.
- Jedox Integrator - Jedox-native, no Python at all, with direct JDBC/Parquet loads and a scheduler. The right tool when you already live in Jedox and want the export governed inside it.
Use raw requests first to understand the shape of your data. When you want it loading reliably into a warehouse, move to dlt. And when the pipeline should belong to Jedox itself, build it in the Integrator.