# Reed Jobs Scraper (`wildorigins/uk-jobs-reed`) Actor

🏷️ From $0.60 / 1K | Search and export UK job vacancies from Reed with employer, location, contract type and salary, including an annualised figure so hourly and salaried roles can be compared.

- **URL**: https://apify.com/wildorigins/uk-jobs-reed.md
- **Developed by:** [Wild Origins](https://apify.com/wildorigins) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Reed Jobs Scraper

Export UK job vacancies from Reed, one of the largest job boards in the UK, with salary, employer, location and applicant counts on every result.

### 🔍 What does Reed Jobs Scraper do?

It runs a job search against Reed and returns every vacancy it finds as a clean row of data, ready for a spreadsheet, a database or your own product.

It talks to Reed's official API rather than scraping the site, because the site cannot be paged. reed.co.uk accepts a `pageno` parameter and echoes it back, then serves the same 25 jobs for every page, so a search Reed reported as 352 matches yielded 25 rows. The API returned all 304 of that same search.

You do not need a key. One is built in. Supply your own free key from [reed.co.uk/developers](https://www.reed.co.uk/developers) through the `reedApiKey` input if you would rather the requests ran against your own account.

### 🧭 How does this compare with Reed's official API?

Reed is one of the few UK job boards with a real public API, the [Jobseeker API](https://www.reed.co.uk/developers/jobseeker), and it is good. This Actor is built on it. Using the API directly means registering for a key, authenticating every request with a basic auth header, paging through results 100 at a time with skip and take parameters, and calling a separate details endpoint for each job's full description and contract fields.

What this Actor adds is everything around that: no key to register or manage, search and detail calls combined into one run, pagination and Reed's repeated listings handled for you, timestamps normalised to UTC, and the output landing as a sortable table or JSON, CSV and Excel, wired into schedules, webhooks and the integrations below.

### 📊 What data can I extract from Reed?

One item per job:

| | Field | What it holds |
|---|---|---|
| 🆔 | `jobId` | Reed's stable id for the listing |
| 🏷️ | `title` | The job title as advertised |
| 🏢 | `employer` | The hiring organisation shown on the listing |
| 🆔 | `employerId` | Reed's id for the employer |
| 📋 | `postedBy` | Employer for direct listings, Recruitment agency otherwise |
| 📍 | `location` | Where the job is |
| 💰 | `salaryFrom` | Lower bound of the advertised salary |
| 💰 | `salaryTo` | Upper bound of the advertised salary |
| 💰 | `salaryCurrency` | Currency of the salary figures |
| 💰 | `salaryPeriod` | Hourly, Daily, Weekly, Monthly or Annual |
| 💰 | `salaryAnnualisedFrom` | Reed's own annual equivalent of the lower bound |
| 💰 | `salaryAnnualisedTo` | Reed's own annual equivalent of the upper bound |
| ✅ | `hasSalary` | False when the listing hides pay, useful for filtering |
| 📋 | `jobType` | Permanent, Contract or Temporary |
| ✅ | `isFullTime` | Whether the role is full time |
| ✅ | `isPartTime` | Whether the role is part time |
| 🔢 | `applications` | How many people had already applied |
| 📝 | `descriptionSnippet` | Opening of the job description |
| 📝 | `description` | The full text, when `fetchDetails` is on |
| 📅 | `datePosted` | When the listing was first posted, UTC |
| 📅 | `expiryDate` | When the listing expires, UTC |
| 🔗 | `externalUrl` | Where the application goes when it leaves Reed |
| 🔗 | `url` | Direct link to the listing on Reed |
| 📥 | `searchKeywords` | The keywords this search ran with |
| 📥 | `searchLocation` | The location this search ran with |
| 📅 | `retrievedAt` | When the row was collected |

All timestamps are normalised to UTC. Reed publishes them without a timezone, so a naive parse shifts them by wherever the code happens to run.

### 💡 Why scrape Reed?

**Judging competition before applying.** A job seeker sorts warehouse roles in Manchester by the `applications` column and applies to the ones posted this week with fewer than ten applicants.

**Hiring signals.** A recruiter watching which employers are hiring for the same role in the same town runs the search weekly and diffs the employer column.

**Salary benchmarks from published ranges.** An HR manager pricing a care assistant opening pulls every advertised range within 20 miles, annualised by Reed so hourly and salaried roles compare in one column.

**Job aggregators and boards.** A niche job site for logistics roles feeds deduplicated Reed listings into its own search every morning on a schedule.

**Direct employers only.** Someone tired of agency repostings filters `postedBy` to Employer and sees each vacancy once, from the organisation actually hiring.

**Spotting expiring listings.** A careers adviser pulls roles expiring in the next week so candidates apply before the closing date rather than after it.

### 🚀 How do I use Reed Jobs Scraper?

1. Click **Try for free**.
2. Enter what you are searching for and where, for example `warehouse` in `Manchester`.
3. Narrow it with `proximityMiles`, a salary range, contract type or `datePosted`.
4. Cap the run with `maxResults`, then click **Start**.
5. Download the results as JSON, CSV or Excel, or pull them from the API.

### ⬇️ Input

```json
{
  "keywords": "warehouse",
  "location": "Manchester",
  "proximityMiles": 20,
  "maxResults": 100
}
```

Set at least one of `keywords` or `location`.

| Field | Type | Default | What it does |
|---|---|---|---|
| `keywords` | string | `software developer` | Job title or keywords |
| `location` | string | `london` | Town, city or county |
| `proximityMiles` | integer | `10` | How far from that location to search |
| `salaryFrom` | integer | | Salary floor filter in GBP |
| `salaryTo` | integer | | Salary ceiling filter in GBP |
| `datePosted` | string | `anytime` | Any time, today, last 3 days, last week or last month |
| `permanent`, `contract`, `temporary` | boolean | `false` | Restrict by contract type |
| `fullTime`, `partTime` | boolean | `false` | Restrict by hours |
| `graduate` | boolean | `false` | Graduate roles only |
| `fetchDetails` | boolean | `true` | Add salary period, annualised pay, contract type and the full description |
| `reedApiKey` | string | | Optional, run the requests against your own free Reed key |
| `maxResults` | integer | `100` | Hard cap on billable results |

### ⬆️ Output

#### Table view

Results arrive as a table you can sort and filter in the Console. There is a second view, Salary detail, that lines up the period and the annualised figures for comparing listings against each other.

#### JSON

A typical row:

```json
{
  "title": "Warehouse Operative",
  "employer": "Best Food Logistics",
  "postedBy": "Employer",
  "location": "Oldham",
  "salaryFrom": 13.5,
  "salaryTo": 13.5,
  "applications": 379,
  "datePosted": "2026-06-19T00:00:00.000Z",
  "url": "https://www.reed.co.uk/jobs/warehouse-operative/57036090",
  "jobId": 57036090
}
```

Download it from the run as JSON, CSV or Excel, or read it straight from the API.

### How many jobs one search returns

Up to 1000 per search, in pages of 100, which is Reed's own API limit. A search
for warehouse work within 20 miles of Manchester returns just over 300 and takes
about 15 seconds.

Reed reports a total match count that is considerably larger than the number of distinct jobs it will actually serve, and it repeats listings across pages. This Actor deduplicates by job id and stops once pages stop producing anything new, so you get the real unique set rather than a padded one. Expect fewer results than the headline count Reed displays.

### Fields, and what the API does not carry

`fetchDetails` is on by default and adds the salary period, Reed's own
annualised salary, contract type, full or part time, and the complete job
description rather than a snippet. It costs one extra request per job, which is
fast enough to be worth it in almost every case. Turn it off for a quicker,
cheaper run when a title and a link are all you need.

The annualised figures come from Reed rather than being calculated here. Reed
knows the contracted hours behind an hourly rate and this actor does not, so
multiplying a rate out would be a guess dressed up as a number.

Moving to the API dropped six fields that were only ever available by scraping
the page markup: `county`, `sector`, `subSector`, `remoteOption`, `isEasyApply`
and `eligibleUkOnly`. `isPromoted` and `dateUpdated` went with them. Reed's API
does not expose any of them. In exchange every row now carries `applications`,
the number of people who have already applied, which is the most useful
competition signal on the listing.

`includePromoted` is kept in the input so existing saved configurations stay
valid, but it no longer does anything.

### ⏱️ How long does a run take?

Measured on real runs, so you know what normal looks like and can tell it apart from a run that has stalled.

| Vacancies returned | Typical run time |
|---|---|
| 10 | about 5 seconds |
| 100 | 7 to 11 seconds |
| 218 | about 34 seconds |

Reed's official API serves 100 vacancies to a page and answers quickly, which is why a hundred vacancy search is usually under ten seconds. What lengthens a run is `fetchDetails`, on by default, which opens each vacancy for the full description and the applicant count and is therefore one request per row. A busy search like nurses in London has been measured at 28 seconds for 100 vacancies where a quieter one took 7.

A run returning fewer vacancies than `maxResults` is the search being exhausted rather than a fault. A twenty mile search for nurses in London holds 218 live vacancies, so asking for 500 returns 218 and says so. The first few seconds of any run are the container starting rather than the work.

A run is never silently stuck. Each page of results is logged as it is read, and vacancies are written to the dataset as they are parsed rather than held back to the end, so a run that hits its time limit still leaves everything it had already collected. A search that matches nothing ends successfully with an empty dataset and the reason in its status message.

**Set the run timeout to suit the size of the ask.** This Actor's default is 3600 seconds, which is comfortably more than the largest run in the table above (218 vacancies takes about 35 seconds). You are charged per delivered result rather than per minute, so a generous timeout costs you nothing and a tight one risks losing the run's work. Lower it only if you want a hard ceiling on how long a scheduled run may sit.

### 💰 How much does it cost?

You pay per job returned. Duplicates are removed before charging, so a listing Reed repeats across pages is charged once. Set `maxResults` to cap what a run can cost.

Runs that return nothing cost nothing. Paid Apify plans get a lower rate per job.

### 🔌 Integrations

Send results straight to Google Sheets, Slack, Airtable, Zapier, Make or your own webhook using [Apify integrations](https://docs.apify.com/platform/integrations). You can also trigger a run whenever something happens in another tool.

AI agents can run this Actor too, through the [Apify MCP server](https://mcp.apify.com). An agent connected to mcp.apify.com can discover it, read its input schema and start a run under the identifier `spookyweb/uk-jobs-reed`, then read the finished dataset. That means an assistant asked "what are warehouse jobs near Manchester paying right now" can call this Actor and answer from live Reed listings.

### 🔗 Using Reed Jobs Scraper with the Apify API

```bash
curl -X POST "https://api.apify.com/v2/acts/spookyweb~uk-jobs-reed/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": "warehouse", "location": "Manchester", "proximityMiles": 20, "maxResults": 100}'
```

Or with the Apify client:

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('spookyweb/uk-jobs-reed').call({
  keywords: 'warehouse',
  location: 'Manchester',
  proximityMiles: 20,
  maxResults: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Full detail is in the [Apify API reference](https://docs.apify.com/api/v2), and every run is also callable from the [Python](https://docs.apify.com/api/client/python) and [JavaScript](https://docs.apify.com/api/client/js) clients.

### ❓ FAQ

#### Do I need my own Reed API key?

No, one is built in and the Actor works out of the box. There is an optional `reedApiKey` input if you would rather the requests ran against your own free key from [reed.co.uk/developers](https://www.reed.co.uk/developers).

#### How many jobs can I get from one search?

Up to 1000 per search, fetched in pages of 100, which is Reed's own API limit. Reed's headline match count is larger than the number of distinct jobs it will serve, so expect fewer unique rows than the site advertises. To cover more of a market, run several narrower searches split by town or by job title.

#### What does the applicant count tell me?

`applications` is how many people have already applied through Reed. It is the clearest competition signal on a listing: a role posted three days ago with 400 applications is a different proposition from one posted the same day with 4.

#### What is the difference between an employer and an agency listing?

`postedBy` says which. An Employer listing comes from the hiring organisation directly. A Recruitment agency listing is placed by an intermediary, and the same underlying vacancy can appear more than once through different agencies.

#### What does fetchDetails add?

The salary period, Reed's own annualised salary figures, the contract type, whether the role is full or part time, and the complete job description rather than a snippet. It costs one extra request per job. Turn it off when a title and a link are all you need.

#### Why use Reed's API rather than scraping the site?

Because the site cannot be paged. reed.co.uk accepts a `pageno` parameter and echoes it back, then serves the same 25 jobs for every page. A search Reed reported as 352 matches yielded 25 rows that way. The API returned all 304 of the same search.

### ⚖️ Is it legal to scrape Reed?

This uses Reed's official public API, the documented one Reed provides for exactly this purpose, rather than scraping their website. Job adverts are business information published by employers, not personal data about individuals.

Scraping publicly available data is legal in the UK, the EU and the US. If you go on to combine this data with personal data, that is on you to handle under GDPR. Apify's [ethical scraping guide](https://blog.apify.com/is-web-scraping-legal/) covers the wider picture.

### 👍 Your feedback

Found a bug, or want a field that is not here yet? Open an issue on the Actor's Issues tab. Requests that make the data more useful get built, and problems get fixed quickly.

### 🔎 You might also like

| Actor | What it does |
|---|---|
| [Indeed Jobs Scraper](https://apify.com/spookyweb/indeed-jobs) | Indeed listings with parsed salary figures, past the 15 job sign in wall |
| [LinkedIn Jobs Scraper](https://apify.com/spookyweb/linkedin-jobs) | LinkedIn jobs with company enrichment and CV keyword matching, no login needed |
| [Totaljobs Scraper](https://apify.com/spookyweb/uk-jobs-totaljobs) | UK jobs from Totaljobs with duplicate agency repostings removed |

# Actor input Schema

## `keywords` (type: `string`):

Job title or keywords, for example nurse or software developer.

## `location` (type: `string`):

Town, city or county, for example manchester or london.

## `proximityMiles` (type: `integer`):

Search radius in miles around the location. Reed measures this from the town or postcode given, so a wider radius pulls in neighbouring areas.

## `salaryFrom` (type: `integer`):

Lowest annual salary to accept, in GBP. Reed filters on the advertised range, so jobs that publish no salary are dropped when this is set.

## `salaryTo` (type: `integer`):

Highest annual salary to accept, in GBP. Reed filters on the advertised range, so jobs that publish no salary are dropped when this is set.

## `datePosted` (type: `string`):

Only return jobs posted within this window. Reed's API has no date filter, so this is applied to the results after they are fetched.

## `permanent` (type: `boolean`):

Include permanent roles. Leaving every contract type switched off returns all of them, so these switches narrow the search rather than widen it.

## `contract` (type: `boolean`):

Include fixed term contract roles. Leaving every contract type switched off returns all of them, so these switches narrow the search rather than widen it.

## `temporary` (type: `boolean`):

Include temporary roles. Leaving every contract type switched off returns all of them, so these switches narrow the search rather than widen it.

## `fullTime` (type: `boolean`):

Include full time roles. Combine with a contract type to narrow further, for example full time and permanent together.

## `partTime` (type: `boolean`):

Include part time roles. Combine with a contract type to narrow further, for example part time and temporary together.

## `graduate` (type: `boolean`):

Include roles Reed flags as suitable for graduates. These are a subset of the main listings rather than a separate job board.

## `includePromoted` (type: `boolean`):

Kept so existing saved inputs stay valid. Reed's API does not mark promoted listings, so this setting has no effect.

## `maxResults` (type: `integer`):

Hard cap on results. Each job is one billable result. Reed serves up to 1000 results per search through the API, in pages of 100.

## `reedApiKey` (type: `string`):

Leave blank to use the built in key, which is shared by everyone running this Actor and can spend its hourly quota in a busy hour. For production use, supply your own free key from reed.co.uk/developers: registration takes about two minutes and gives this Actor a private quota that nobody else can exhaust.

## `fetchDetails` (type: `boolean`):

On by default. Adds the salary period, Reed's own annualised salary, contract type, full or part time and the complete job description. Costs one extra fast request per job.

## Actor input object example

```json
{
  "keywords": "software developer",
  "location": "london",
  "proximityMiles": 10,
  "datePosted": "anytime",
  "permanent": false,
  "contract": false,
  "temporary": false,
  "fullTime": false,
  "partTime": false,
  "graduate": false,
  "includePromoted": false,
  "maxResults": 100,
  "fetchDetails": true
}
```

# Actor output Schema

## `results` (type: `string`):

One row per item: job listings with salary and applicant counts.

## `runSummary` (type: `string`):

Counts and stop reason for the run.

# 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": "software developer",
    "location": "london"
};

// Run the Actor and wait for it to finish
const run = await client.actor("wildorigins/uk-jobs-reed").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": "software developer",
    "location": "london",
}

# Run the Actor and wait for it to finish
run = client.actor("wildorigins/uk-jobs-reed").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": "software developer",
  "location": "london"
}' |
apify call wildorigins/uk-jobs-reed --silent --output-dataset

```

## MCP server setup

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

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/4iavHborUyvW5vQgz/builds/7F87dcNSFWf0dS9EY/openapi.json
