Everything on this portal is drawn from a read-only JSON API on this same
server. It is documented here so that the audience most likely to want the
numbers — people working in a notebook, a spreadsheet or R — does not have to
reverse-engineer it from the pages.
The routes
/api/v1/summary
Headline counts across the corpus: providers, authorities, contract
notices and their values, the latest workforce census metrics, and the
pipeline's own collection state.
No parameters.
Returns providers, authorities,
contracts (with value_is_concentrated, which is
why this portal publishes no headline contract total),
workforce, pipeline,
funnel (the candidate-to-evidence verification funnel:
discovered, undecided, promoted,
rejected, evidence_rows).
/api/v1/summary
/api/v1/providers
One row per provider, with matched contract value, tribunal and CQC
counts, latest charity income, and the register identifiers
(company_number, charity_number) so a reader can
verify at source.
No parameters.
Returns providers: an array.
/api/v1/providers
/api/v1/providers/{provider_key}/timeline
The deep dive on one provider: dated events from charity financials,
tribunal decisions, NHS job adverts and contract awards, plus its CQC
locations and the entity edges that link it to companies and charities.
provider_key is the provider_key field from
/api/v1/providers.
Returns provider, events,
cqc_locations, entity_edges,
tribunal_cases, charity_finance,
cqc_inspections, disclosure (with
gaps, disclosed and not_searched
kept apart), filings, pfd_mentions,
caveats.
/api/v1/providers/change_grow_live/timeline
/api/v1/authorities
Every commissioning authority: ons_code, name,
type, region. The ONS code is the join key for
the geography, contracts and NDTMS routes.
No parameters.
/api/v1/authorities
/api/v1/authorities/{ons_code}
Everything the warehouse holds about one authority: coverage ticks
(W-12), grant allocation and budgeted spend as published, the budget
lines behind the budget figure (W-27), treatment estimates with their
paired confidence intervals, and the contracts it let.
ons_code is the ons_code field from
/api/v1/authorities.
Returns authority, coverage,
grant, budget, budget_detail,
treatment (fingertips and
ndtms, with the same pairing rules the treatment page
uses), contracts, caveats.
The grant and the budget are different figures from different documents
and are never combined or divided — the payload keeps them separate and
so must anything built on it. budget_detail carries amounts
as published: no per-capita, no inflation adjustment, no ratio.
/api/v1/authorities/E08000025
/api/v1/compare
The existing series for two or more authorities or providers, drawn on
shared axes — the campaign's "how does my authority compare?" answered
in the only shape this pipeline may give it: the reader picks the peers.
No new data: every series is the series an existing endpoint returns
(grant and budget as the geography page draws them, treatment as the
treatment page draws it, contracts by publication year as the contracts
page counts them), composed for the chosen entities.
| Parameter | Meaning |
ons_code | An authority from /api/v1/authorities. Repeatable. |
provider_key | A provider from /api/v1/providers. Repeatable. |
Returns authorities, providers,
series keyed by layer — grant,
budget, treatment, contracts for
authorities, charity and provider_contracts
for providers — each carrying its own caveat, plus
caveats.cross_layer.
The charts share axes with each other's authorities, never with each
other's layers: nothing in the payload is combined, differenced or
divided, and a gap between one chart and another is not a finding.
At least one parameter is required; an unknown code is a 400.
/api/v1/compare?ons_code=E08000025&ons_code=E06000047&provider_key=change_grow_live
/api/v1/layers
The geography map's overlay layers: contracts aggregated to one point
per commissioning authority, CQC-regulated locations, latest treatment
rates per authority, and coverage — how many evidence kinds the
warehouse holds per authority. Each layer carries its own caveats, read
from the same source the export layers use, so the portal and the
downloads cannot drift apart.
Returns layers, each with label,
features and caveats. PFD reports are
deliberately absent: they have no geometry, and coroner areas must not
be mapped as if they were authorities.
No parameters.
/api/v1/layers
/api/v1/contracts
Contract notices, with aggregates computed over the whole filtered corpus
and a window of individual notices. The aggregates are complete;
the notices array is not, and
total says how many rows the filters actually match. For
every row, use the export route below.
| Parameter | Meaning |
provider_key | Only notices whose supplier name matches a known alias of this provider. |
buyer_ons_code | Only notices let by this authority. |
year_from, year_to | Publication year bounds, inclusive. |
psr_only | 1 for notices let under the Provider Selection Regime only. |
limit | Rows in the notices window. Default 500, maximum 5,000. Ignored by the export, which reads every row. |
Returns total, total_value_gbp,
value_concentration, matched_to_provider,
date_range, by_year, by_provider,
by_procedure_type, by_quarter,
value_bands (in fixed bands that never move with the
filters), ending_soon (notices whose published end date
falls within two years, with the caveat saying what that does and does
not mean), top_buyers, notices,
caveats.
Each notice carries three link fields and they are three different
claims: source_url is the API page the bytes came from — a
paginated cursor, which is provenance and not a destination;
notice_web_url is the address the release itself published,
or null; notice_link is the one to put in front
of a reader, with notice_link_basis saying whether it was
published or constructed.
/api/v1/contracts?psr_only=1&year_from=2025
/api/v1/pay
The pay evidence, and the most caveat-heavy payload here: indicative wage
per employee from charity accounts, NHS job adverts by band, repeatedly
advertised roles, and the workforce census metrics with their
verification flags.
| Parameter | Meaning |
provider_key | One provider's series only. |
year_from, year_to | Financial year bounds, inclusive. |
Returns charity_wage_series, nhs_job_adverts,
nhs_job_by_band, repeat_advertised_roles,
workforce_census, census_verified_count,
census_total, statutory_pay_rates,
living_wage_accreditations, gender_pay_gap_reports,
provider_published_pay, ons_ashe_observations,
skills_for_care_estimates, caveats.
The wage figure is wages and salaries divided by an average employee
count. It is not a pay scale, a median salary or anyone's earnings, and
the caveat saying so travels in the payload.
/api/v1/pay
/api/v1/council_spend
Payment lines from council spend-transparency files. This is actual
published payment evidence, kept separate from procurement notices and
authority budgets. The response does not calculate a payment total.
| Parameter | Meaning |
authority_ons_code | One authority from /api/v1/authorities. |
provider_key | One tracked provider, linked only by exact normalised payee-name match. |
limit | Maximum payment lines returned; defaults to 500. |
Returns total (the number of matching lines, not an amount),
payments, files (including unreadable files),
and caveats.
/api/v1/council_spend
/api/v1/geography
One value per authority for a chosen metric — the choropleth's data,
without the geometry. Geometry is 14 MB and does not change between
metrics, so it is fetched once from /api/v1/boundaries and
joined on ons_code.
| Parameter | Meaning |
metric | One of grant_total (the default), grant_drug_alcohol, grant_per_head, budget_public_health, treatment_numbers, contract_value. |
year | Financial year. Omit for the latest available. |
Returns metric, metric_label,
year, unit, allocation_status,
features, authority_mean, min,
max, available_years, caveat.
authority_mean is the mean across authorities and is
labelled as such. It is not "the England figure", which is a different
number published separately.
A grant allocation and a budgeted spend are different figures from
different documents: do not substitute one for the other, and do not
compute a ratio between them.
/api/v1/geography?metric=grant_per_head
/api/v1/boundaries
Authority boundary geometry as a GeoJSON FeatureCollection,
keyed by ons_code. Large, and cached: fetch it once.
No parameters.
Returns type, features, meta
(source_url, retrieved_at,
count).
/api/v1/boundaries
/api/v1/fingertips
Treatment demand indicators from OHID Fingertips: the indicator
catalogue, the per-authority series, and the England series alongside it.
| Parameter | Meaning |
indicator_id | One Fingertips indicator. |
topic | Indicators under one topic. |
substance | Indicators about one substance. |
ons_code | One authority's values. |
Returns indicators, series,
england_series, caveat.
/api/v1/fingertips
/api/v1/ndtms
Local-authority NDTMS figures. Called without an authority it returns the
catalogue — which sheets exist, how many authorities and publications
each covers — rather than 17,231 rows.
| Parameter | Meaning |
ons_code | One authority's rows. Without it, only the catalogue is returned. |
table_ref | One published sheet. |
Returns datasets, authority,
estimates, other_rows.
These are modelled estimates published with 95% confidence intervals, and
the interval is part of the figure. An estimate in estimates
carries lower, upper and
has_interval; where the publication printed bounds that
cannot be attached to one estimate unambiguously, they are left
unattached and has_interval is false. A confidence interval
on the wrong estimate is invented, which is worse than an absent one.
other_rows holds the rows with no number in them —
suppression markers kept verbatim. A marker is not a zero.
/api/v1/ndtms
/api/v1/pfd
Coroners' Prevention of Future Deaths reports: reports by year (with
the metadata stubs counted on the chart, not in a footnote), by coroner
area, the concern-term finding aid, and the two mention facts kept
apart — being sent a report and being named in one
are different mention_types and are never summed.
No parameters.
Returns totals, by_year,
by_coroner_area, concern_terms,
mentions (sent_to_providers,
naming_providers, recipient_organisations),
recent, caveats. The names of the deceased
are in restricted tables this route cannot read, and no restricted
table is listed anywhere in the payload.
/api/v1/pfd
/api/v1/claims
The campaign's claims, each linked to the evidence that supports it
and approved by a named reviewer. Nothing in this response is
computed: the claim text is what a person wrote, the citations are
rows a person picked, and the caveats are lines a person wrote about
what may not be computed from it.
No parameters.
Returns claims: an array of id,
claim_text, caveats (one line each),
citations (each with a resolved object of
label, url, source_url and
retrieved_at — or null when the row it named
is no longer in the warehouse), published_by,
published_at and note. Only published claims
are served; a claim is published only by a recorded decision.
/api/v1/claims
/api/v1/freshness
When each source table was last written by a pipeline run — the newest
retrieved_at per table. Served on its own route rather
than inside /api/v1/summary because it is seconds of full
table scans; the overview loads it lazily after first paint.
No parameters.
Returns tables: an array of label,
table and retrieved_at (or no date at all —
never collected is "never", not a zero), plus caveat.
/api/v1/freshness
/api/v1/export
The same figures as a file, with the provenance written into it.
A CSV gets separated from any accompanying note within about a day of
leaving this server, so the endpoint it came from, the filters that
produced it, the number of rows it holds and the licence terms are all
comment lines at the top of the file — and the same object is in the
X-Provenance response header.
| Parameter | Meaning |
endpoint | One of summary, providers, authorities, contracts, pay, geography, fingertips, ndtms. |
format | csv (the default) or json. |
| any filter | Every parameter the underlying route accepts is forwarded, and is recorded in the file's filters_applied line. |
An export is complete for its filters. It is not the
page's window: a contracts export reads every notice the filters match,
streamed, however many that is, and the # rows: line
says how many so the file can be checked against itself. A
limit in the query is ignored here.
/api/v1/export?endpoint=contracts&format=csv&psr_only=1