The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Getting started with dataseries

What is dataseries.org?

Switzerland publishes a lot of official statistics, but they are spread across many providers: the Federal Statistical Office, SECO, the National Bank, the Federal Finance Administration and others. Each has its own portal, its own file formats and its own update schedule.

dataseries.org collects them in one place. It tracks the sources, harmonises them into a single structure and keeps them current. This package is a client for its public API, so you can pull any of those series straight into R.

How the data is organised

Data comes in datasets. A dataset is a family of related series and is usually a multi-dimensional cube: one time series is a single cell of that cube, addressed by the dataset plus one code per dimension.

Four functions cover the whole package:

Finding something

Start with the catalog. One row per dataset:

cat <- ds_catalog()
nrow(cat)
#> [1] 70
head(cat[, c("id", "title", "frequency", "n_series")])
#>                           id                               title   frequency
#> 1             ch_adecco_sjmi Adecco Group Swiss Job Market Index   quarterly
#> 2            ch_ffa_finances                 Government finances      annual
#> 3               ch_fso_besta           Jobs by economic division   quarterly
#> 4       ch_fso_besta_outlook                  Employment outlook   quarterly
#> 5 ch_fso_construction_prices                 Construction prices semi-annual
#> 6                 ch_fso_cpi   Consumer prices (detailed basket)     monthly
#>   n_series
#> 1        4
#> 2      387
#> 3       60
#> 4       20
#> 5        3
#> 6      595

If you know roughly what you want, ds_search() is finer grained. It returns one row per series across all datasets:

hits <- ds_search("unemployment rate")
head(hits[, c("dataset", "dim", "code", "label")])
#>             dataset    dim code             label
#> 1 ch_fso_unemp_rate origin  tot             Total
#> 2 ch_fso_unemp_rate origin   ch   Swiss nationals
#> 3 ch_fso_unemp_rate origin   ex Foreign nationals

The dataset, dim and code columns are exactly what ds() expects, so a search result can be fed straight back in.

Downloading

The simplest call takes a dataset id and returns every series in it, in long format:

cpi <- ds("ch_fso_cpi")
head(cpi)
#>       item       date   value
#> 1    100_1 1982-12-01 70.0205
#> 2   100_10 1982-12-01 34.7833
#> 3  100_100 1982-12-01 59.1566
#> 4 100_1001 1982-12-01 69.2540
#> 5 100_1002 1982-12-01 65.3241
#> 6 100_1010 1982-12-01 82.6664

To pick one series, pass the dimension codes as named arguments. Filtering happens on the server, so this does not download the whole cube first:

total <- ds("ch_fso_cpi", item = "100_100", from = "2015-01-01")
head(total)
#>      item       date   value
#> 1 100_100 2015-01-01 93.5394
#> 2 100_100 2015-02-01 93.2989
#> 3 100_100 2015-03-01 93.6033
#> 4 100_100 2015-04-01 93.4335
#> 5 100_100 2015-05-01 93.6510
#> 6 100_100 2015-06-01 93.7198

Working with cubes

Which dimensions does a dataset have? ds_meta() tells you:

m <- ds_meta("ch_seco_gdp")
unlist(m$dim_order)
#> [1] "type"      "structure" "seas_adj"
names(m$dimensions$type$levels)
#> [1] "nom"  "real" "gc_q" "gc_y"

So the GDP dataset splits three ways, and a single cell needs one code from each:

gdp <- ds("ch_seco_gdp", type = "real", structure = "gdp", seas_adj = "csa")
tail(gdp)
#>     type structure seas_adj       date    value
#> 180 real       gdp      csa 2024-10-01 200725.0
#> 181 real       gdp      csa 2025-01-01 202350.7
#> 182 real       gdp      csa 2025-04-01 202640.5
#> 183 real       gdp      csa 2025-07-01 201781.0
#> 184 real       gdp      csa 2025-10-01 202218.0
#> 185 real       gdp      csa 2026-01-01 203544.3

Time series objects

Every series on dataseries.org is regular (annual, quarterly or monthly), so it maps cleanly onto R’s ts class. Pass class = "ts":

gdp_ts <- ds("ch_seco_gdp", type = "real", structure = "gdp", seas_adj = "csa",
             class = "ts")
plot(gdp_ts, main = "Swiss real GDP, seasonally adjusted",
     ylab = "CHF million", col = "steelblue")
Line chart of Swiss real GDP from 1980 to 2026, rising with dips around 2009 and 2020

Swiss real GDP, seasonally adjusted, since 1980

Select several cells and you get an mts with one column per series:

two <- ds("ch_fso_cpi", item = c("100_100", "100_1"), from = "2020-01-01",
          class = "ts")
head(two)
#>            100_1 100_100
#> Jan 2020 96.2727 94.0756
#> Feb 2020 96.3427 94.1922
#> Mar 2020 97.0131 94.2645
#> Apr 2020 97.6975 93.9216
#> May 2020 98.3081 93.9610
#> Jun 2020 99.2729 93.9806

For xts, convert in one line: xts::as.xts(ds("ch_fso_cpi", item = "100_100", class = "ts")).

Labels in other languages

The providers publish their labels in German, French and Italian as well, and dataseries.org keeps them. Pass lang to ds_catalog() or ds_search():

head(ds_catalog(lang = "de")[, c("id", "title")])
#>                           id                                       title
#> 1             ch_adecco_sjmi         Adecco Group Swiss Job Market Index
#> 2            ch_ffa_finances                        Öffentliche Finanzen
#> 3               ch_fso_besta      Beschäftigte nach Wirtschaftsabteilung
#> 4       ch_fso_besta_outlook                    Beschäftigungsaussichten
#> 5 ch_fso_construction_prices                                   Baupreise
#> 6                 ch_fso_cpi Konsumentenpreise (detaillierter Warenkorb)

Searching matches the labels in the chosen language, so German terms work directly:

ds_search("arbeitslosen", lang = "de")[, c("dataset", "code", "label")]
#>          dataset               code                                label
#> 1 ch_seco_concon ks_i32_unemp_exp_q 3.2 Entwicklung der Arbeitslosenzahl

Where a translation is missing, English is used.

Caching

Everything downloaded is cached in memory for the session, so repeating a call costs nothing. cache_ls() shows what is held, keyed by request URL, and cache_rm() empties it to force a fresh download:

length(cache_ls())
#> [1] 7
cache_rm()
length(cache_ls())
#> [1] 0

Beyond R

The same data is available from Python through the dataseries package (pip install dataseries), and as plain CSV from any tool that can read a URL:

https://api.dataseries.org/series.csv?dataset=ch_fso_cpi&dims=item=100_100

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.