How-to guide
Query with DuckDB
This guide shows how to query imibare datasets directly with DuckDB, either through the Apache Iceberg integration or by reading a flat Parquet file over HTTP.
Credential requirement: the Iceberg path needs credentials. Cloudflare R2 Data Catalog does not support unauthenticated public access, so you need a Cloudflare API token with Workers R2 Storage and Workers R2 Data Catalog permissions. If you only need the current data, read the Parquet file directly instead: that path needs no credentials at all.
Prerequisites
pip install duckdbSet credentials in your environment:
export R2_ACCOUNT_ID=<your-cloudflare-account-id>export R2_ACCESS_KEY_ID=<your-r2-key-id>export R2_SECRET_ACCESS_KEY=<your-r2-secret>export R2_CATALOG_URL=https://catalog.cloudflarestorage.com/<account-id>/imibare-dataexport R2_CATALOG_TOKEN=<your-catalog-token>Connect to the Iceberg catalog
import osimport duckdb
account_id = os.environ["R2_ACCOUNT_ID"]
# Create the S3-compatible secret for R2duckdb.sql(f""" CREATE SECRET r2 ( TYPE S3, KEY_ID '{os.environ["R2_ACCESS_KEY_ID"]}', SECRET '{os.environ["R2_SECRET_ACCESS_KEY"]}', ENDPOINT '{account_id}.r2.cloudflarestorage.com', REGION 'auto', URL_STYLE 'path' )""")
# Attach the Iceberg catalog (read-only)duckdb.sql(f""" ATTACH '{account_id}_imibare-data' AS imi ( TYPE ICEBERG, ENDPOINT '{os.environ["R2_CATALOG_URL"]}', TOKEN '{os.environ["R2_CATALOG_TOKEN"]}', READ_ONLY )""")Run queries
# Latest CPI dataduckdb.sql("SELECT * FROM imi.rw_nisr_cpi_monthly ORDER BY date DESC LIMIT 10").show()
# Annual average CPI by categoryduckdb.sql(""" SELECT date_trunc('year', date) AS year, category, AVG(value) AS avg_cpi FROM imi.rw_nisr_cpi_monthly GROUP BY 1, 2 ORDER BY 1 DESC, 2""").show()
# USD exchange rate in 2024duckdb.sql(""" SELECT date, rwf_per_unit FROM imi.rw_bnr_fx_daily WHERE currency = 'USD' AND year(date) = 2024 ORDER BY date""").show()Load results into pandas
df = duckdb.sql(""" SELECT date, value FROM imi.rw_nisr_cpi_monthly WHERE category = 'overall' ORDER BY date""").df() # .df() returns a pandas DataFrameTable names
DuckDB table names use underscores, not dots:
| Dataset ID | DuckDB table |
|---|---|
rw.nisr.cpi.monthly | imi.rw_nisr_cpi_monthly |
rw.nisr.gdp.annual | imi.rw_nisr_gdp_annual |
rw.bnr.fx.daily | imi.rw_bnr_fx_daily |
rw.minecofin.budget-execution.quarterly | imi.rw_minecofin_budget_execution_quarterly |
rw.rssb.contributions.annual | imi.rw_rssb_contributions_annual |
Rule: replace dots with underscores, hyphens with underscores.
List all tables
duckdb.sql("SHOW ALL TABLES").show()Time-travel queries
Iceberg snapshots let you query a table as it existed on a specific date. Check your installed DuckDB version for the exact syntax, as it varies between releases:
duckdb.sql(""" SELECT * FROM imi.rw_nisr_cpi_monthly AT (TIMESTAMP => TIMESTAMPTZ '2025-06-01 00:00:00') ORDER BY date DESC LIMIT 5""").show()Read a flat Parquet file directly (no Iceberg, no credentials)
Every dataset is published as a flat Parquet file. Reading one needs no credentials:
import duckdb
duckdb.sql(""" SELECT * FROM read_parquet( 'https://<account-id>.r2.cloudflarestorage.com/imibare-data/v1/rw/nisr/cpi/monthly/latest.parquet' ) LIMIT 10""").show()The path follows the convention v1/{country}/{institution}/{topic}/{frequency}/latest.parquet.
Reference
- Dataset IDs reference: all valid dataset IDs
- Python package reference:
imi.load()alternative - DuckDB documentation: SQL syntax, Iceberg extension, secret management