
nhsbsa is a low-level R client for the NHS Business Services Authority
(NHSBSA) Open Data Portal, a CKAN
data catalogue that publishes open datasets about NHS activity in
England — prescribing, dental, pharmaceutical and contractor data among
them. The package provides thin wrappers around the portal’s API actions
and returns plain data — tibbles for tabular results and lists for
metadata — leaving the interpretation of any particular dataset to the
caller.
The package deliberately contains no knowledge of specific datasets, and wraps the useful read subset of the API’s actions. Function names and arguments mirror the CKAN API, so if you know the API you already know the package. See the portal’s own API page and the CKAN Action API reference for background. If you need an action the package does not yet wrap, please open an issue.
nhsbsa is experimental and a work in
progress. It was developed with Claude Code, modelled
on the design of the author’s trud and gtexr API-client packages.
Some functionality has yet to be exercised interactively against the
live API, so please treat results with care. Bug reports, comments and
suggestions are very welcome via the issue tracker.
Install the released version of nhsbsa from CRAN with:
install.packages("nhsbsa")Or install the development version from GitHub with:
# install.packages("pak")
pak::pak("rmgpanw/nhsbsa")library(nhsbsa)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, unionFind datasets — list every id, or search:
datasets <- nhsbsa_package_list()
length(datasets)
#> [1] 2217
hits <- nhsbsa_package_search(q = "prescribing", rows = 5)
hits$count
#> [1] 661Browsing the portal website
and clicking a tag such as #Prescribing filters the
dataset list (the page URL becomes
/dataset/?tags=Prescribing). A filter query finds the
datasets with that tag:
nhsbsa_package_search(fq = 'tags:"Prescribing"')$count
#> [1] 18The API returns more datasets than the website shows for the tag, because the website hides the Freedom of Information disclosure log by default. Exclude that organisation to match the website’s count:
nhsbsa_package_search(
fq = 'tags:"Prescribing" -organization:freedom-of-information-disclosure-log'
)$count
#> [1] 5List a dataset’s resources (files), including each file’s download URL:
resources <- nhsbsa_list_resources("bnf-code-information-current-year")
resources |>
select(name, format, url) |>
slice_head(n = 6)
#> # A tibble: 6 × 3
#> name format url
#> <chr> <chr> <chr>
#> 1 BNF_CODE_CURRENT_202503_VERSION_88 CSV https://opendata.nhsbsa.net/d…
#> 2 BNF_CODE_CURRENT_202504_VERSION_88 CSV https://opendata.nhsbsa.net/d…
#> 3 BNF_CODE_CURRENT_202505_VERSION_88 CSV https://opendata.nhsbsa.net/d…
#> 4 BNF_CODE_CURRENT_202506_VERSION_88 CSV https://opendata.nhsbsa.net/d…
#> 5 BNF_CODE_CURRENT_202507_VERSION_88 CSV https://opendata.nhsbsa.net/d…
#> 6 BNF_CODE_CURRENT_202508_VERSION_88_FINAL CSV https://opendata.nhsbsa.net/d…Download one of them to disk. You choose the destination
directory (it must already exist), and the file is saved
there under its own name:
path <- nhsbsa_download_resource(
"bnf-code-information-current-year",
resource_id = resources$id[[1]],
directory = tempdir()
)Read rows from a tabular (datastore) resource without downloading the
whole file. The datastore identifies a resource by its
name (e.g. "EPD_202401"), and field names
are case-sensitive. Use fields, sort and
limit/offset to read:
nhsbsa_datastore_search(
resource_id = "EPD_202401",
fields = c("PCO_CODE", "BNF_CHEMICAL_SUBSTANCE", "ITEMS"),
sort = "ITEMS desc",
limit = 5
)
#> Warning: ! Retrieved 5 of 18080573 matching rows; 18080568 not returned.
#> ℹ Fetch the next page with `offset = 5` (reusing your other arguments),
#> increasing `offset` until all rows are retrieved.
#> ℹ Raising `limit` returns more rows per request, up to the server-side maximum.
#> # A tibble: 5 × 3
#> PCO_CODE BNF_CHEMICAL_SUBSTANCE ITEMS
#> <chr> <chr> <int>
#> 1 11J00 1404000H0 3584
#> 2 06H00 0212000B0 3571
#> 3 02Y00 0212000B0 3469
#> 4 12F00 1404000H0 3160
#> 5 11M00 1404000H0 3038To filter by value or aggregate, use SQL. On this portal,
datastore_search does not apply the CKAN
filters/q parameters, so SQL is the way to
filter:
nhsbsa_datastore_search_sql(
resource_id = "EPD_202401",
sql = "SELECT PCO_CODE, SUM(ITEMS) AS items
FROM `EPD_202401`
WHERE PCO_CODE = 'W2U3Z'
GROUP BY PCO_CODE
ORDER BY items DESC
LIMIT 5"
)
#> # A tibble: 1 × 2
#> PCO_CODE items
#> <chr> <int>
#> 1 W2U3Z 3129964nhsbsa_datastore_search_sql(
resource_id = "EPD_202401",
sql = "SELECT PCO_CODE, SUM(ITEMS) AS items
FROM `EPD_202401`
GROUP BY PCO_CODE
ORDER BY items DESC
LIMIT 5"
)
#> # A tibble: 5 × 2
#> PCO_CODE items
#> <chr> <int>
#> 1 91Q00 3151968
#> 2 W2U3Z 3129964
#> 3 A3A8R 3124609
#> 4 D9Y0V 2697474
#> 5 15N00 2413359See vignette("nhsbsa") for an overview of the portal,
how the package maps onto the website, and the different ways to query
data.