Hi Bruno,
Thanks for your question, and sorry for the delay!
One thing to pin down before I point at endpoints: when you say “the list of all available series”, do you mean every series DBnomics publishes, or the series of one dataset your user has picked? The two lead to different designs, and only the second one is worth building.
All of DBnomics is about 44,000 datasets and 1.5 billion series today (Eurostat alone around 895M, CEPII 170M, mostly sparse cross-products of dimensions). Neither you nor your users can work from that list: a picker over 1.5 billion rows is a search problem, not a listing problem.
A single dataset is more manageable, though not always small: Eurostat/ei_cphi_m holds 3,339 series, Eurostat/MIGR_ASYRESCRA holds 42,325,076. Even there, GET /v22/series/{provider}/{dataset}?observations=0&limit=1000 pages through the whole set (num_found gives the total, 1000 series per call), but a list of 42 million codes is not a selection interface either.
What selects series is the dimension dictionary. A DBnomics series is a combination of dimension values, and every dataset declares its dimensions and the values it uses. That is literally the left column of the faceted search on a dataset page: you pick the country out of a list, then the indicator, then the unit, and the set of matching series collapses as you click.
Dimensions are declared per dataset, with no harmonization across DBnomics. The same idea shows up as geo in Eurostat, REF_AREA in INSEE, country elsewhere, and the value codes differ too: France is FR in Eurostat and FE in INSEE/IPC-2015. A cross-provider picker means mapping that yourself, a per-dataset picker gets it for free.
So the cheap call is the dataset metadata:
GET https://api.db.nomics.world/v22/datasets/Eurostat/ei_cphi_m
which returns dimensions_codes_order, dimensions_labels and dimensions_values_labels. Reshaped into something a UI can iterate:
import json, requests
BASE = "https://api.db.nomics.world/v22"
def dataset_dimensions(provider, dataset):
url = f"{BASE}/datasets/{provider}/{dataset}"
meta = requests.get(url, timeout=30).json()["datasets"]["docs"][0]
return {
code: {
"label": meta["dimensions_labels"].get(code, code),
"values": meta["dimensions_values_labels"].get(code, {}),
}
for code in meta["dimensions_codes_order"]
}
for code, dim in dataset_dimensions("Eurostat", "ei_cphi_m").items():
print(f"{code} ({dim['label']}): {len(dim['values'])} values")
On that dataset you get freq (Time frequency, 1 value), unit (Unit of measure, 3), s_adj (Seasonal adjustment, 1), indic (Indicator, 25) and geo (Geopolitical entity, 45). Twenty-five indicators times forty-five countries, each with its label: that is the picker, and it fits on one screen.
Once the user has ticked values, you query by dimensions instead of by series code:
selection = {"geo": ["FR", "DE"], "indic": ["CP-HI00"], "unit": ["HICP2015"]}
resp = requests.get(
f"{BASE}/series/Eurostat/ei_cphi_m",
params={"dimensions": json.dumps(selection), "observations": 1, "limit": 1000},
timeout=60,
).json()["series"]
json.dumps is required: requests expands a dict passed as a param into repeated dimensions=geo&dimensions=indic entries and drops the values, which the API rejects with a 400. num_found is 2 here, and docs carries the periods and the values. With observations=0 you show the user what the combination matches before ingesting anything, and with limit=0 you get num_found alone when all you need is the count.
The keys of dimensions are the dataset’s own codes, copied from dimensions_codes_order: geo works on Eurostat, and on INSEE/IPC-2015 the same filter returns 0 without an error. The values are codes from dimensions_values_labels, not labels: {"geo": ["France"]} returns 0 where {"geo": ["FR"]} returns 75.
Hope this helps!