# Reed.co.uk Jobs Scraper (`scrapyx/reed-jobs-scraper`) Actor

Scrapes job vacancies with full descriptions from Reed.co.uk — one of the UK's largest job boards, with 100,000+ live vacancies. Keyword and location search with salary, job-type and date filters, plus direct job-URL lookup. No pagination ceiling.

- **URL**: https://apify.com/scrapyx/reed-jobs-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Jobs, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 results

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.co.uk Jobs Scraper

Scrapes job vacancies with full descriptions from
**[Reed.co.uk](https://www.reed.co.uk)** — one of the UK's largest job boards,
with over 100,000 live vacancies at any time.

Public data only. No login, no API key, no browser. This reads the same public
pages Reed's own website renders — it is not the key-gated partner API.

### What you get

Two record types share one dataset, told apart by `recordType`.

#### `JOB` — one row per vacancy

Search rows carry title, employer, salary range, location, job type, posting
date and a description snippet. With **Fetch full job descriptions** on (the
default), each row also gets a `jobDetails` object:

- the complete job description (HTML as the employer wrote it)
- salary breakdown, contract type, part/full-time flags
- created / display / expiry dates
- application method (easy-apply vs external), eligibility notes
- recruiter and employer identifiers

#### `SEARCH_SUMMARY` — one row per keyword

Upstream's match total, how deep the run paged, and exactly which filters were
sent.

### Input

| Field | What it does |
| --- | --- |
| **Search keywords** | one search per entry, each with its own summary row |
| **Location** | town, city, county or postcode — applies to every keyword |
| **Distance from location** | radius in miles (needs a location) |
| **Salary from / to** | annual salary range |
| **Posted within** | today, last 3 days, or last week |
| **Job type toggles** | full-time, part-time, contract, temporary, graduate, agency |
| **Job URLs** | scrape specific vacancies directly, skipping search |
| **Max jobs per keyword** | `0` = unlimited, and Reed has no page ceiling |
| **Fetch full job descriptions** | off = fast listing-only crawl, 1 request per 25 jobs |

#### Example

```json
{
  "keywords": ["python developer", "data engineer"],
  "location": "London",
  "salaryFrom": 50000,
  "datePosted": "lastweek",
  "maxItems": 500,
  "includeJobDetails": true
}
```

### Three things this actor is honest about

**1. It only offers filters that actually work.** Reed answers an unrecognised
filter with HTTP 200 and the *unfiltered* result set — no error. Every option
here was verified to genuinely move the match count, and the ones that don't
were left out on purpose:

- `permanent` looks like a real filter and isn't — it returns the exact
  unfiltered baseline, so it is not offered.
- Only `today`, `last 3 days` and `last week` are honoured for date. "Last
  month" and "yesterday" are silently ignored by Reed, so they are not offered
  and an unknown value is refused before the run starts.

**2. Reed has no working sort.** `sortby` was tested with six different values
including a deliberately bogus one — all returned the identical results. Rather
than ship a knob that does nothing, this actor offers no sort and reports
`sortSupported: false` on every summary row.

**3. Expired jobs are reported, not crashed on.** Reed returns HTTP **410
Gone** (not 404) for a filled or expired ad. Those become a clean
`_error: "gone"` row, or a `_detailError` note on an otherwise good search row
— one job expiring mid-crawl never costs you the other 99.

### Notes on reliability

- **No pagination ceiling.** Unlike most portals, Reed serves rows at
  `pageno=4036` on a 100,000-match query. `maxItems: 0` really does mean
  everything — set a bound unless you mean it.
- **No WAF challenge.** Cloudflare is in the path but passive; data comes back
  cold on every TLS profile tested.
- **Runs unattended.** No human-captured session, no expiring token.
- **Residential proxy by default** on cloud runs.

### Output envelope

Every record carries `_input`, `_source` and `_scrapedAt`. Upstream field names
pass through **verbatim** — no renaming. `_source` is `S1-nextdata-search` for
listing-only rows and `S1-nextdata-search+S2-nextdata-jobdetail` once
descriptions are attached.

Note the search row's own fields sit under `jobDetail` (singular, upstream's
naming); the fuller detail-page payload is `jobDetails` (plural).

See [`CRAWLING_METHOD.md`](CRAWLING_METHOD.md) for the full reverse-engineering
trail, including the per-filter verification table and the 410 finding.

# Actor input Schema

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

One search per entry — each keyword runs as its own query and gets its own SEARCH\_SUMMARY row. Leave empty to browse every job (optionally narrowed by location and the filters below).

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

Town, city, county or postcode, e.g. London, Manchester, Bristol, EC1. Leave empty to search the whole UK. Applies to every keyword in the run.

## `proximity` (type: `integer`):

Widen the search around the location. Only has an effect when a location is set — a run that sets this without a location is refused rather than silently ignoring it.

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

Annual salary floor. Leave empty or 0 for no minimum.

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

Annual salary ceiling. Leave empty or 0 for no maximum.

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

Only these three windows are honoured by Reed. Anything else (last month, yesterday…) is silently ignored upstream and returns the unfiltered search, so those options are deliberately not offered and unknown values are refused.

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

Restrict results to full-time positions.

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

Restrict results to part-time positions.

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

Restrict results to fixed-term contract positions.

## `temp` (type: `boolean`):

Restrict results to temporary positions.

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

Restrict results to graduate-level and entry-level positions.

## `agency` (type: `boolean`):

Restrict to jobs posted by agencies rather than direct employers.

## `jobUrls` (type: `array`):

Scrape specific vacancies directly, skipping search. Accepts a full URL (https://www.reed.co.uk/jobs/x/57088337) or a bare numeric job id. Can be combined with a keyword search in the same run.

## `maxItems` (type: `integer`):

Stop paginating a keyword after this many jobs. Set to 0 for unlimited — Reed has no pagination ceiling, so unlimited really does mean the entire result set (browsing with no keyword reaches 100,000+ jobs). Set a bound unless you mean it.

## `includeJobDetails` (type: `boolean`):

Fetch the complete ad (full HTML description, salary breakdown, contract details, application info, recruiter) for every result via one extra request per job. Turn this off for a fast listing-only crawl — search rows already carry title, employer, salary range, location, job type and a description snippet.

## `maxConcurrency` (type: `integer`):

Upper bound on requests in flight at once, across keyword pagination and detail fetches.

## `proxyConfiguration` (type: `object`):

Reed runs no active bot challenge and a direct connection works, but Residential is the default for cloud runs — container egress is a different posture than a home connection, and this portfolio has lost a full cloud run to that difference before.

## Actor input object example

```json
{
  "keywords": [
    "python developer"
  ],
  "location": "",
  "proximity": 0,
  "salaryFrom": 0,
  "salaryTo": 0,
  "datePosted": "",
  "fullTime": false,
  "partTime": false,
  "contract": false,
  "temp": false,
  "graduate": false,
  "agency": false,
  "jobUrls": [],
  "maxItems": 100,
  "includeJobDetails": true,
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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": [
        "python developer"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/reed-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": ["python developer"] }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/reed-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": [
    "python developer"
  ]
}' |
apify call scrapyx/reed-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/reed-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/PNkUQSf6XopeXyeKu/builds/n7g4w8cdbTJ4P3LQO/openapi.json
