# NHS Jobs Scraper (`steadyscrape/nhs-jobs-scraper`) Actor

Scrape NHS Jobs (jobs.nhs.uk) vacancies in England and Wales: title, employer, Agenda for Change band, salary, location, postcode, closing date and contract, plus each advert's person specification and Skilled Worker visa sponsorship. Export to JSON, CSV or Excel, or via API.

- **URL**: https://apify.com/steadyscrape/nhs-jobs-scraper.md
- **Developed by:** [Steady Scrape](https://apify.com/steadyscrape) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 jobs

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## NHS Jobs Scraper

Scrape vacancies from **[NHS Jobs](https://www.jobs.nhs.uk)** (jobs.nhs.uk), the NHS's own job board for **England and
Wales**. This NHS Jobs scraper searches by **keyword**, **location** with a **distance in miles**, **Agenda for Change pay
band** or **medical grade**, **staff group**, **contract type** (permanent, fixed-term, bank, locum…), **working pattern**,
**pay range** and **employer**, and gives every job with its **employer**, **salary as shown and as numbers**, **places and
postcodes**, **date posted**, **closing date**, **contract** and **working pattern**.

From each job's own advert it adds the **pay scheme and band**, the **salary note** (per annum, pro rata, London weighting),
the **employer's reference number**, **every job location with its address**, the **job summary, main duties and
description**, the **person specification** (essential and desirable criteria), whether **Skilled Worker visa sponsorship**
applicants are welcome, whether **UK professional registration** and a **DBS check** are required, and the employer's
**address and website**. A filter keeps only the jobs that **welcome visa sponsorship**.

No login, no browser. Pay only for the jobs you get.

### What you can do with this NHS Jobs scraper

- **International recruitment**: every NHS nursing, medical and allied health vacancy whose advert says applicants who need
  **Skilled Worker sponsorship** are welcome, with band, salary and closing date.
- **Healthcare and locum agencies**: new bank, locum and fixed-term posts by staff group, band and area, every morning.
- **Job alerts and feeds**: schedule it daily with **Posted within** and **Only new jobs** to receive only the adverts posted
  since the last run.
- **NHS workforce and pay analysis**: vacancies by band, staff group, employer and region, with published pay ranges.
- **Lead lists of employers**: trusts, GP practices, hospices and care providers hiring now, with their address and website.

### Input

| Field | What it does |
|---|---|
| **Keywords** | Job titles or skills as you would type them in NHS Jobs' search: `nurse`, `staff nurse`, `healthcare assistant`, `physiotherapist`, `consultant psychiatrist`. Each keyword is one search; empty searches every job. |
| **Locations** | A postcode, town, city or region in England or Wales (`Leeds`, `LS1 4AP`, `London`, `Cardiff`, `Yorkshire`, `Wales`). Each keyword is searched in each location. |
| **Distance around each location** | Only in the place named, or +5, 10, 20, 30, 50 or 100 miles. Each job then shows its distance. |
| **Employer** | NHS Jobs' employer filter (`Leeds Teaching Hospitals`). |
| **Pay bands and grades** | Band 2 to Band 9 (8A–8D) and the medical grades: Specialty Doctor, Foundation Doctor, Consultant, Specialty Registrar, Doctor - Other, Specialist, Very senior manager. |
| **Staff groups** | Nursing & Midwifery Registered, Medical & Dental, Allied Health Professionals, Additional Clinical Services, Administrative & Clerical, Healthcare Scientists, Estates & Ancillary, Additional Professional Scientific & Technical, Students. |
| **Contract types** | Permanent, Fixed-term, Bank, Locum, Apprenticeship, Training, Secondment, Honorary, Voluntary, NHS Reservist. |
| **Working patterns** | Full time, Part time, Job-share, Flexible working, Home or remote working, Compressed hours, Term time hours, Annualised hours. |
| **Pay ranges** | £10,000 steps from £0 to £100,000 plus. |
| **Sort by** | Date posted (newest first, the default), best match, closing date, salary high to low or low to high. |
| **Posted within** | The last day, 3, 7, 14 or 30 days. On a newest-first search the run stops at the first older job. |
| **NHS Jobs search links** | Any search results page of jobs.nhs.uk, read with its filters as they are. |
| **Advert links** | Job adverts (`https://www.jobs.nhs.uk/candidate/jobadvert/C9236-26-1380`), one job with details each, closed adverts included. |
| **Include advert details** | On by default: opens each advert (charged as job-with-details). Off: only what the results list shows (charged as job). |
| **Only jobs welcoming Skilled Worker visa sponsorship** | Keeps only adverts whose Certificate of Sponsorship section welcomes applicants who need sponsorship. Adverts left out are not charged. |
| **Max jobs per search** | 100 by default; 0 for every job a search finds. |
| **Only new jobs since the last run** | Monitor mode for scheduled runs: jobs delivered before are skipped and not charged. |

Example — Band 5 and Band 6 nursing jobs within 20 miles of Manchester posted this week, only those welcoming visa
sponsorship:

```json
{
    "keywords": ["nurse"],
    "locations": ["Manchester"],
    "distance": "20",
    "payBands": ["BAND_5", "BAND_6"],
    "staffGroups": ["NURSING_AND_MIDWIFERY_REGD"],
    "postedWithin": "7",
    "onlySponsorshipWelcome": true,
    "maxJobsPerSearch": 200
}
```

### Output: NHS Jobs data

One row per job (texts shortened):

```json
{
    "rowType": "job",
    "status": "ok",
    "search": "nurse · Rochdale (+10 miles)",
    "searchUrl": "https://www.jobs.nhs.uk/candidate/search/results?keyword=nurse&location=Rochdale&distance=10&sort=publicationDateDesc&language=en",
    "position": 1,
    "jobId": "C9236-26-1380",
    "url": "https://www.jobs.nhs.uk/candidate/jobadvert/C9236-26-1380",
    "title": "District Nurse - Charge Nurse",
    "employer": "Northern Care Alliance NHS Foundation Trust",
    "locations": ["Rochdale OL11 5BU"],
    "postcodes": ["OL11 5BU"],
    "distanceMiles": 1.6,
    "salaryText": "£39,959 to £48,117 a year",
    "salaryMin": 39959,
    "salaryMax": 48117,
    "salaryPeriod": "year",
    "postedDate": "2026-10-23",
    "closingDate": "2026-11-08",
    "contractType": "Permanent",
    "workingPatterns": ["Full time"],
    "details": {
        "payScheme": "Agenda for change",
        "band": "Band 6",
        "salary": "£39,959 to £48,117 a year",
        "salaryNote": "Per annum",
        "contractDuration": null,
        "referenceNumber": "236-UEC-NM720-26-B",
        "closed": false,
        "jobLocations": [
            { "organisation": null, "addressLines": ["Spotland Bridge Mill", "Mellor Street"], "town": "Rochdale", "county": null, "postcode": "OL11 5BU", "country": "United Kingdom" }
        ],
        "jobSummary": "Are you a passionate and experienced nurse looking to make a real difference in your community?\nAn exciting opportunity has arisen…",
        "mainDuties": "Join our team and play a vital role in delivering exceptional, person-centred care within the community…",
        "aboutUs": "The Northern Care Alliance NHS Foundation Trust (NCA) provides hospital and integrated health and social care services…",
        "jobResponsibilities": "To read more information about the advertised role, and the main job duties/responsibilities please open the Job Description…",
        "personSpecification": [
            {
                "category": "Qualifications",
                "essential": ["Registered Nurse with current NMC registration (Part 1 Adult / General Level 1 or 2)", "Community Specialist Practitioners Degree"],
                "desirable": ["Post Registration experience", "V300 prescribing"]
            }
        ],
        "sponsorshipWelcome": true,
        "ukRegistrationRequired": true,
        "dbsCheckRequired": true,
        "employerAddress": { "organisation": null, "addressLines": ["Spotland Bridge Mill", "Mellor Street"], "town": "Rochdale", "county": null, "postcode": "OL11 5BU", "country": "United Kingdom" },
        "employerWebsite": "https://www.northerncarealliance.nhs.uk/",
        "supportingDocuments": ["FUNCTIONAL REQUIREMENTS.pdf (PDF, 169 KB)", "District Nurse - Charge Nurse.pdf (PDF, 449 KB)"],
        "applyUrl": "https://www.jobs.nhs.uk/candidate/jobadvert/C9236-26-1380/ats-direct-apply",
        "appliesOnEmployerSystem": true
    },
    "scrapedAt": "2026-10-26T08:00:00.000Z"
}
```

A search or link that gives nothing comes back as one **free** row with the reason, for example a place NHS Jobs cannot pin
down, with the places it suggests:

```json
{
    "rowType": "status",
    "status": "location_not_found",
    "search": "nurse · Newport",
    "url": "https://www.jobs.nhs.uk/candidate/search/results?keyword=nurse&location=Newport&sort=publicationDateDesc&language=en",
    "reason": "NHS Jobs found no single place for this location. It suggests: Newport Pagnell; Newport Park, Exeter; Newport, Berkeley; … Use one of these, or a postcode.",
    "scrapedAt": "2026-10-26T08:00:00.000Z"
}
```

### NHS Jobs fields

| Field | Description |
|---|---|
| `search`, `searchUrl`, `position` | The search or link the job came from, its results page, and the job's place in it from 1. |
| `jobId`, `url` | NHS Jobs' advert id and link. |
| `title`, `employer` | As the advert shows them. |
| `locations`, `postcodes` | Each place the job is in, as town and postcode. |
| `distanceMiles` | Miles from the location searched (searches with a location). |
| `salaryText`, `salaryMin`, `salaryMax`, `salaryPeriod` | The salary as shown, and as numbers with `year`, `month`, `week`, `day` or `hour` when it has figures. |
| `postedDate`, `closingDate` | `YYYY-MM-DD`. |
| `contractType`, `workingPatterns` | As NHS Jobs writes them. |
| `details.payScheme`, `details.band` | Agenda for change and its band, medical and dental grades, or Other. |
| `details.salary`, `details.salaryNote` | The advert's salary line and the note under it. |
| `details.referenceNumber`, `details.contractDuration` | The employer's own reference; the length of a fixed-term or bank contract. |
| `details.jobLocations`, `details.employerAddress`, `details.employerWebsite` | Addresses with site name, lines, town, county, postcode and country. |
| `details.jobSummary`, `details.mainDuties`, `details.aboutUs`, `details.jobResponsibilities` | The advert's texts, paragraphs as line breaks. |
| `details.personSpecification` | Criteria by category, essential and desirable. |
| `details.sponsorshipWelcome` | The advert's Certificate of Sponsorship section welcomes Skilled Worker visa applicants. |
| `details.ukRegistrationRequired`, `details.dbsCheckRequired` | UK professional registration and a Disclosure and Barring Service check are required. |
| `details.supportingDocuments` | Names of the job description and other attached files (the files are not downloaded). |
| `details.applyUrl`, `details.appliesOnEmployerSystem`, `details.closed` | The Apply link, whether it goes through the employer's own recruitment system, and whether the advert has closed. |
| `rowType`, `status`, `reason` | `job` rows are charged; `status` rows (`no_results`, `not_found`, `invalid`, `location_not_found`, `site_error`, `blocked`) are free and say why. |
| `scrapedAt` | When the row was read, UTC ISO 8601. |

The run's `SUMMARY` record gives, per search, NHS Jobs' own heading and count ("4919 jobs found for nurse"), the jobs
delivered and the jobs each filter left out.

### Pricing

Pay per event: **US$0.001 per job** read from the results list, **US$0.002 per job with its advert details**. Status rows
are free, and with **Only jobs welcoming Skilled Worker visa sponsorship** the adverts left out are not charged.

### Limits

- NHS Jobs in **England and Wales** only; NHS Scotland and HSC Northern Ireland have their own sites and are not read.
- Results come 10 to a page, read one request a second.
- The advert's contact box (recruiter name, email and phone) is never collected. The advert texts come as the employer
  wrote them, and some name a contact for informal enquiries.
- Sponsorship is what the advert says: an employer can still sponsor without the section, or decide against a candidate who
  needs it.

### Is it legal to scrape NHS Jobs?

This Actor reads public job adverts that anyone can open without an account, at a gentle pace, and leaves out the advert's
contact box (recruiter name, email and phone). It is an **unofficial** tool, **not affiliated with, endorsed by or
connected to the NHS, the NHS Business Services Authority (NHSBSA) or NHS Jobs**. Job adverts are written by the employers
that publish them, and a name or email they wrote into an advert's text is personal data: check the site's terms and the
law that applies to your use of the data (UK GDPR included), especially for commercial use, before you rely on it.

# Actor input Schema

## `keywords` (type: `array`):

Job titles or skills as you would type them in NHS Jobs' "What" box, e.g. <code>nurse</code>, <code>staff nurse</code>, <code>healthcare assistant</code>, <code>physiotherapist</code>, <code>consultant psychiatrist</code>, <code>pharmacist</code>. Each keyword is one search; leave empty to search every job.

## `locations` (type: `array`):

A postcode, town, city or region in England or Wales, e.g. <code>Leeds</code>, <code>LS1 4AP</code>, <code>London</code>, <code>Cardiff</code>, <code>Yorkshire</code>, <code>Wales</code>. Each keyword is searched in each location. A place NHS Jobs cannot pin down (Newport) gives a free row listing the places it suggests.

## `distance` (type: `string`):

Only in the place named, or within this many miles of it. Each job then shows its distance.

## `employer` (type: `string`):

NHS Jobs' employer filter, e.g. <code>Leeds Teaching Hospitals</code>, <code>Guy's and St Thomas'</code>, <code>Aneurin Bevan University Health Board</code>.

## `payBands` (type: `array`):

Agenda for Change bands and medical and dental grades, as NHS Jobs' filter offers them.

## `staffGroups` (type: `array`):

The NHS staff groups of NHS Jobs' filter.

## `contractTypes` (type: `array`):

Permanent, fixed-term, bank, locum and the other contracts of NHS Jobs' filter.

## `workingPatterns` (type: `array`):

Full time, part time, job-share, flexible, home or remote working and the other patterns of NHS Jobs' filter.

## `payRanges` (type: `array`):

Yearly pay ranges of NHS Jobs' filter.

## `sort` (type: `string`):

The order NHS Jobs lists the jobs in. Newest first lets "Posted within" stop at the first older job.

## `postedWithin` (type: `string`):

Only jobs posted on or after this many days before the run (NHS Jobs gives the day a job was posted).

## `searchUrls` (type: `array`):

Search result pages of jobs.nhs.uk, read with their filters as they are, e.g. <code>https://www.jobs.nhs.uk/candidate/search/results?keyword=nurse\&location=Leeds\&distance=10</code>.

## `advertUrls` (type: `array`):

Job adverts of jobs.nhs.uk, e.g. <code>https://www.jobs.nhs.uk/candidate/jobadvert/C9236-26-1380</code>: one job with details each, closed adverts included.

## `includeDetails` (type: `boolean`):

On by default: opens each advert for pay scheme, band, salary note, reference number, every job location, the job summary and description, the person specification, Skilled Worker sponsorship, UK registration, DBS check and the employer's address and website (charged as job-with-details). Off: only what the results list shows (charged as job).

## `onlySponsorshipWelcome` (type: `boolean`):

Keeps only adverts whose "Certificate of Sponsorship" section says applicants who need Skilled Worker sponsorship are welcome. Each advert is opened to check, so details are always included; adverts left out are not charged.

## `maxJobsPerSearch` (type: `integer`):

How many jobs each keyword and location search delivers at most; 0 for every job it finds.

## `onlyNewJobs` (type: `boolean`):

Monitor mode for scheduled runs: jobs this input delivered before are skipped and not charged.

## Actor input object example

```json
{
  "keywords": [
    "nurse"
  ],
  "locations": [],
  "distance": "only",
  "employer": "",
  "payBands": [],
  "staffGroups": [],
  "contractTypes": [],
  "workingPatterns": [],
  "payRanges": [],
  "sort": "newest",
  "postedWithin": "any",
  "searchUrls": [],
  "advertUrls": [],
  "includeDetails": true,
  "onlySponsorshipWelcome": false,
  "maxJobsPerSearch": 20,
  "onlyNewJobs": false
}
```

# Actor output Schema

## `jobs` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": [
        "nurse"
    ],
    "maxJobsPerSearch": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyscrape/nhs-jobs-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": ["nurse"],
    "maxJobsPerSearch": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("steadyscrape/nhs-jobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "nurse"
  ],
  "maxJobsPerSearch": 20
}' |
apify call steadyscrape/nhs-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadyscrape/nhs-jobs-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/G8vRJJ5CRKd3DVF8w/builds/dUhlEkRbSSTV7t2NP/openapi.json
