UK NHS Providers Scraper — ODS Organisation Registry
Pricing
from $3.40 / 1,000 provider records
UK NHS Providers Scraper — ODS Organisation Registry
Search the official NHS Organisation Data Service (ODS) registry of UK healthcare providers — GP practices, pharmacies, dentists, NHS trusts, hospices, care homes. Filter by name, postcode, type, status; optional full address/contacts/roles. Official open-data API (OGL v3), no login, no proxies.
Pricing
from $3.40 / 1,000 provider records
Rating
0.0
(0)
Developer
Nomad.Dev
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
15 hours ago
Last modified
Categories
Share
Search the official NHS Organisation Data Service (ODS) registry of UK healthcare providers — GP practices, pharmacies, dental practices, NHS trusts, hospices, care homes and more. Official open-data API, no login, no proxies, no HTML parsing.
What NHS provider data does this scraper extract?
One flat JSON record per organisation:
| Field | Meaning |
|---|---|
odsCode | Official ODS organisation code (e.g. L81008) |
name | Registered organisation name |
status | Active / Inactive |
primaryRoleId / primaryRoleDescription | Provider type (e.g. GP practice, pharmacy) |
postCode | Registered postcode |
lastChangeDate | Last registry change (useful for incremental syncs) |
orgLink | Canonical registry URL for the organisation |
With Include full details (or in detail mode) each record adds:
| Field | Meaning |
|---|---|
addressLine1/2, town, county, country, uprn | Full registered address |
phone, website | Registered contacts where provided |
roles | All ODS roles with names, primary flag and status |
relationships | Organisation-hierarchy links — [{relTypeId, relTypeName, targetOdsCode, targetName}] (e.g. an NHS trust site "IS OPERATED BY" its parent trust). targetName is null by default — the registry doesn't embed the target's display name in the relationship payload. Turn on Resolve relationship target names (includeRelationshipNames) to fill it in (one extra API call per unique related org) |
operationalStart/End, legalStart/End | Operational and legal dates |
Optional enrichments
| Enrichment | Adds | Turn on with |
|---|---|---|
| Add lat/long geocoding | latitude, longitude — the organisation's postcode geocoded via the free, open postcodes.io service (Ordnance Survey / ONS open data). null when a postcode can't be resolved. Works in search or detail mode. | includeGeo: true |
| Resolve relationship target names | Fills relationships[].targetName with each related organisation's registered name (e.g. the parent NHS trust). Needs detail data — detail mode, or includeDetails: true in search mode. | includeRelationshipNames: true |
With Only new since last run enabled, every returned record also carries isNew: true.
How to scrape NHS providers with this Actor
- Pick a mode:
search(filters below) ordetail(passodsCodes). - In search mode set at least one filter:
name,postCode(full or partial, e.g.BS1),providerType(GP practice, pharmacy, dental, NHS trust, hospice, care home, …),roleId(any official role code),lastChangeDatefor a one-off incremental sync, oronlyNewSinceLastRunfor automatic incremental syncs across scheduled runs. - Run and export JSON, CSV or Excel — or call it over the API:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("nomad-agent/uk-nhs-ods-providers-scraper").call(run_input={"mode": "search","postCode": "BS1","providerType": "pharmacy","includeDetails": True,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["odsCode"], "|", item["name"], item["postCode"])
curl -X POST \"https://api.apify.com/v2/acts/nomad-agent~uk-nhs-ods-providers-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"mode": "search", "postCode": "BS1", "providerType": "gp_practice"}'
Input
| Field | Type | Default | Description |
|---|---|---|---|
mode | select | search | search queries the registry with the filters below; detail fetches full records for the ODS codes in odsCodes. |
name | string | – | Case-insensitive substring match on the organisation name. |
postCode | string | (prefilled BS1 in Console) | Full or partial UK postcode; prefix match on the registered address. |
providerType | select | any | Common provider types mapped to official ODS role codes (GP practice, pharmacy, dental, NHS trust, NHS trust site, hospice, care home, optical site, independent provider, branch surgery). |
specialty | string | – | Free-text specialty (e.g. pharmacy, hospice, dental), matched against the ODS role vocabulary. Ignored if providerType (non-any) or roleId is set. |
roleId | string | – | Any official ODS role code (advanced) — overrides providerType and specialty. |
status | select | active | active, inactive, or all. |
lastChangeDate | string (YYYY-MM-DD, datepicker) | – | Only organisations changed on or after this date. The registry rejects dates more than ~185 days in the past (HTTP 406). |
onlyNewSinceLastRun | boolean | false | Search only organisations changed since this Actor's own last successful run (server-side, via the registry's LastChangeDate filter). Overrides lastChangeDate once a prior run exists. Search mode only. Tags every record isNew: true. |
odsCodes | array | – | ODS codes to fetch in detail mode (e.g. L81008, RA7, FLM49). |
includeDetails | boolean | false | In search mode, enrich every hit with the full registry record — one extra API call per organisation. |
includeGeo | boolean | false | Add latitude/longitude by geocoding each postcode via the free postcodes.io service. Best-effort — unresolved postcodes return null. |
includeRelationshipNames | boolean | false | Resolve relationships[].targetName (needs detail data) — one extra API call per unique related organisation. |
maxItems | integer | 100 | Hard cap on organisations returned. Each result is billed, so start low and raise once your filter is dialed in. 0 = no cap. |
concurrency | integer | 8 | (Advanced) Parallel detail-record fetches (detail mode / includeDetails), 1–16. |
Output example
Search mode (includeDetails: false):
{"odsCode": "L81008","name": "SHIREHAMPTON GROUP PRACTICE","status": "Active","primaryRoleId": "RO177","primaryRoleDescription": "PRESCRIBING COST CENTRE","postCode": "BS11 9SB","orgRecordClass": "RC1","lastChangeDate": "2024-07-15","orgLink": "https://directory.spineservices.nhs.uk/ORD/2-0-0/organisations/L81008","source": "nhs-ods"}
Detail mode / includeDetails: true — a real NHS trust site, showing the relationships link back to its parent trust:
{"odsCode": "A0A1E","name": "CAMHS - ADASH","status": "Active","primaryRoleId": "RO198","primaryRoleDescription": "NHS TRUST SITE","postCode": "AL10 8HR","orgRecordClass": "RC2","lastChangeDate": "2024-05-08","orgLink": "https://directory.spineservices.nhs.uk/ORD/2-0-0/organisations/A0A1E","source": "nhs-ods","addressLine1": "DOWNS FARM CENTRE","town": "HATFIELD","country": "ENGLAND","roles": [{"id": "RO198", "name": "NHS TRUST SITE", "primary": true, "status": "Active"},{"id": "RO31", "name": "PPA EPACT SYSTEM", "primary": false, "status": "Active"}],"relationships": [{"relTypeId": "RE6", "relTypeName": "IS OPERATED BY", "targetOdsCode": "RWR", "targetName": null}],"operationalStart": "2024-03-22"}
Integrations
Every run's results are available as a dataset you can export as JSON, CSV, Excel (XLSX), or query straight from the API with run-sync-get-dataset-items. Wire it into Make, Zapier, or n8n via their native Apify integrations, or drive it programmatically through the Apify MCP server so an LLM agent can call this Actor as a tool.
Pricing
Pay per event: $0.05 per Actor start and $0.004 per organisation returned. 100 providers ≈ $0.45. No subscription — pay only for what you fetch.
Use cases
- Healthcare lead generation: every pharmacy, GP practice or dental practice in a region, with addresses and phone numbers
- Master-data / CRM enrichment keyed on official ODS codes
- Incremental registry syncs via
lastChangeDate, or automatically on a schedule viaonlyNewSinceLastRun - Health-tech integrations that need canonical NHS organisation reference data
- Market analysis of UK primary care, community pharmacy and independent providers
- Mapping organisational hierarchy (e.g. which trust operates which site) via the
relationshipsfield (withincludeRelationshipNamesfor resolved names) - Geospatial analysis and mapping via optional
latitude/longitudegeocoding (includeGeo)
FAQ
Is it legal to scrape NHS ODS data? Yes. The ORD API is the official, public, unauthenticated open-data API of NHS England's Organisation Data Service, and the data is published under the Open Government Licence v3 — free to use, including commercially, with attribution ("Contains information from NHS England, licenced under the current version of the Open Government Licence").
Do I need an API key or login? No. The ORD API requires no authentication of any kind.
Which countries does it cover? Primarily England (plus border/cross-registered organisations in Wales, Scotland and Northern Ireland where they hold ODS codes).
Why does a GP practice show PRESCRIBING COST CENTRE as its primary role?
That is how ODS models GP practices — the primary role is RO177 with RO76 (GP PRACTICE) as a non-primary role. Filtering by the "GP practice" provider type handles this for you.
What if an ODS code isn't found? It is logged and skipped — the run continues with the other codes.
How fresh is the data? Every run hits the live registry API. No caching layer in between.
How do I run this on a schedule and only get what's new?
Turn on Only new since last run (onlyNewSinceLastRun). It stores this Actor's last successful run date and, on the next run, asks the registry for organisations changed since then — no need to track ids yourself, and no re-paying for organisations you already have. First run with the flag on (no prior state yet) behaves like a normal search; every run after that is incremental.
What's in relationships?
Organisation-hierarchy links from the registry's own data, e.g. an NHS trust site record links back to the trust that operates it (relTypeId: "RE6", "IS OPERATED BY"). Only present in detail mode / with Include full details on. targetName is null by default; turn on Resolve relationship target names (includeRelationshipNames) to fill in each related organisation's name.
Can I get map coordinates?
Yes — turn on Add lat/long geocoding (includeGeo) and each record gains latitude/longitude, geocoded from its postcode via the free, open postcodes.io service. Unresolvable postcodes return null.
Something broken or missing? Open an issue on the Actor's Issues tab — it is monitored and fixes ship fast.
Related Actors
More official-registry and government-data scrapers from the same family: