The datasets of the BFS
catalog (article type “Data”) are in the PXWeb API. Each dataset has
a BFS number, for example px-x-1502040100_131.
In this article, we search the catalog and download the number of university students.
The PXWeb API is the API of STAT-TAB. The BFS is replacing STAT-TAB with the Swiss Stats Explorer. First, check if your statistic is on stats.swiss. See Get started.
Search the catalog
catalog <- bfs_get_catalog_data(language = "en", extended_search = "student")
catalog
#> # A tibble: 4 × 6
#> title language number_bfs number_asset publication_date url_px
#> <chr> <chr> <chr> <chr> <date> <chr>
#> 1 University of applie… en px-x-1502… 36454608 2026-03-24 https…
#> 2 University of applie… en px-x-1502… 36454606 2026-03-24 https…
#> 3 University students … en px-x-1502… 36454426 2026-03-24 https…
#> 4 University students … en px-x-1502… 36454424 2026-03-24 https…title searches only in the titles.
extended_search also searches in the summaries. You can
also filter the catalog with:
-
spatial_division:"Switzerland","Cantons","Districts","Communes","Other spatial divisions"or"International". -
prodima: the theme (see below). -
publishing_year_startandpublishing_year_end. -
inquiry,institutionororder_nr(the BFS number).
The English and Italian catalogs have fewer datasets than the German and French catalogs.
Themes
| Theme | prodima |
|---|---|
| 00 Statistical basis and overviews | 900001 |
| 01 Population | 900010 |
| 02 Territory and environment | 900035 |
| 03 Work and income | 900051 |
| 04 National economy | 900075 |
| 05 Prices | 900084 |
| 06 Industry and services | 900092 |
| 07 Agriculture and forestry | 900104 |
| 08 Energy | 900127 |
| 09 Construction and housing | 900140 |
| 10 Tourism | 900160 |
| 11 Mobility and transport | 900169 |
| 12 Money, banks and insurance | 900191 |
| 13 Social security | 900198 |
| 14 Health | 900210 |
| 15 Education and science | 900212 |
| 16 Culture, media, information society, sports | 900214 |
| 17 Politics | 900226 |
| 18 General government and finance | 900239 |
| 19 Crime and criminal justice | 900257 |
| 20 Economic and social situation of the population | 900269 |
| 21 Sustainable development, regional and international disparities | 900276 |
bfs_get_catalog_data(language = "en", prodima = 900212) # Education and science
#> # A tibble: 5 × 6
#> title language number_bfs number_asset publication_date url_px
#> <chr> <chr> <chr> <chr> <date> <chr>
#> 1 University of applie… en px-x-1502… 36454608 2026-03-24 https…
#> 2 University of applie… en px-x-1502… 36454606 2026-03-24 https…
#> 3 University students … en px-x-1502… 36454426 2026-03-24 https…
#> 4 University students … en px-x-1502… 36454424 2026-03-24 https…
#> 5 Compulsory education… en px-x-1502… 36429912 2026-02-26 https…The whole catalog
One request gives at most limit results (1000 by
default). To get the whole catalog, make one request for each theme. Do
this only when you need it, and save the result. This way, you don’t
overload the API:
themes_prodima <- c(
900001, 900010, 900035, 900051, 900075, 900084, 900092, 900104, 900127,
900140, 900160, 900169, 900191, 900198, 900210, 900212, 900214, 900226,
900239, 900257, 900269, 900276
)
catalog_all <- themes_prodima |>
purrr::map(function(x) bfs_get_catalog_data(language = "de", prodima = x)) |>
purrr::list_rbind() |>
distinct()
readr::write_csv(catalog_all, "catalog_all.csv")All the metadata
With return_raw = TRUE, you get the catalog exactly as
the API returns it, with all the metadata in nested data frames:
catalog_raw <- bfs_get_catalog_data(language = "en", extended_search = "student", return_raw = TRUE)
as_tibble(catalog_raw$description)
#> # A tibble: 4 × 9
#> titles$main categorization$colle…¹ bibliography$period abstractShort
#> <chr> <list> <chr> <chr>
#> 1 University of applie… <df [2 × 4]> 1997-2025 This dataset…
#> 2 University of applie… <df [2 × 4]> 1997-2025 This dataset…
#> 3 University students … <df [2 × 4]> 1990-2025 This dataset…
#> 4 University students … <df [2 × 4]> 1980-2025 This dataset…
#> # ℹ abbreviated name: ¹categorization$collection
#> # ℹ 17 more variables: categorization$prodima <list>, $inquiry <list>,
#> # $spatialdivision <list>, $classification <list>, $institution <list>,
#> # $publisher <list>, $tags <list>, $dataSource <list>, $copyrights <list>,
#> # $termsOfUse <list>, $serie <list>, $periodicity <list>,
#> # shortSummary <df[,2]>, language <chr>, highlight <lgl>, discontinued <lgl>,
#> # downloadable <lgl>Download a dataset
bfs_get_data(number_bfs = "px-x-1502040100_131", language = "en")
#> # A tibble: 19,320 × 5
#> Year `ISCED Field` Sex `Level of study` `University students`
#> <chr> <chr> <chr> <chr> <dbl>
#> 1 1980/81 Education science Male First university degr… 545
#> 2 1980/81 Education science Male Bachelor 0
#> 3 1980/81 Education science Male Master 0
#> 4 1980/81 Education science Male Doctorate 93
#> 5 1980/81 Education science Male Further education, ad… 13
#> 6 1980/81 Education science Female First university degr… 946
#> 7 1980/81 Education science Female Bachelor 0
#> 8 1980/81 Education science Female Master 0
#> 9 1980/81 Education science Female Doctorate 70
#> 10 1980/81 Education science Female Further education, ad… 52
#> # ℹ 19,310 more rowsbfs_get_data_comments() gives the comments (footnotes)
of the dataset.
Download only part of a dataset
A large dataset can be too big for the API. Then choose only the data
you need with query. First, get the variables of the
dataset and their values:
metadata <- bfs_get_metadata(number_bfs = "px-x-1502040100_131", language = "en")
metadata_values <- metadata |>
tidyr::unnest_longer(c(values, valueTexts)) |>
select(code, text, values, valueTexts)
metadata_values
#> # A tibble: 95 × 4
#> code text values valueTexts
#> <chr> <chr> <chr> <chr>
#> 1 Jahr Year 0 1980/81
#> 2 Jahr Year 1 1981/82
#> 3 Jahr Year 2 1982/83
#> 4 Jahr Year 3 1983/84
#> 5 Jahr Year 4 1984/85
#> 6 Jahr Year 5 1985/86
#> 7 Jahr Year 6 1986/87
#> 8 Jahr Year 7 1987/88
#> 9 Jahr Year 8 1988/89
#> 10 Jahr Year 9 1989/90
#> # ℹ 85 more rowsThe query is a named list. The names are the variable codes
(code), and the values are the codes of the values you want
(values). "*" selects all the values of a
variable.
filter(metadata_values, text == "Year", valueTexts %in% c("2020/21", "2021/22"))
#> # A tibble: 2 × 4
#> code text values valueTexts
#> <chr> <chr> <chr> <chr>
#> 1 Jahr Year 40 2020/21
#> 2 Jahr Year 41 2021/22
filter(metadata_values, text == "Level of study")
#> # A tibble: 5 × 4
#> code text values valueTexts
#> <chr> <chr> <chr> <chr>
#> 1 Studienstufe Level of study 0 First university degree or diploma
#> 2 Studienstufe Level of study 1 Bachelor
#> 3 Studienstufe Level of study 2 Master
#> 4 Studienstufe Level of study 3 Doctorate
#> 5 Studienstufe Level of study 4 Further education, advanced studies and ot…
bfs_get_data(
number_bfs = "px-x-1502040100_131",
language = "en",
query = list(
Jahr = c("40", "41"), # 2020/21 and 2021/22
Studienstufe = c("2", "3"), # Master and Doctorate
"ISCED Fach" = "0", # Education science
Geschlecht = "*" # all sexes
)
)
#> # A tibble: 8 × 5
#> Year `ISCED Field` Sex `Level of study` `University students`
#> <chr> <chr> <chr> <chr> <dbl>
#> 1 2020/21 Education science Male Master 151
#> 2 2020/21 Education science Male Doctorate 121
#> 3 2020/21 Education science Female Master 555
#> 4 2020/21 Education science Female Doctorate 306
#> 5 2021/22 Education science Male Master 143
#> 6 2021/22 Education science Male Doctorate 115
#> 7 2021/22 Education science Female Master 599
#> 8 2021/22 Education science Female Doctorate 318If a variable is not in the query, it is not in the result: the data is summed over this variable, if the dataset allows it.
For a very large dataset, you can also download the whole PX file
with bfs_download_asset(), and read it with the pxR
package. See Tables and files.
