# CWJobs Scraper - UK IT Jobs, Employer or Agency (`studio-amba/cwjobs-scraper`) Actor

Scrape UK IT and tech jobs from CWJobs.co.uk. Each job comes with CWJobs' own direct employer or agency label, permanent or contract type, salary, location, skills and posting date.

- **URL**: https://apify.com/studio-amba/cwjobs-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 result scrapeds

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

## CWJobs Scraper

**CWJobs Scraper** extracts IT and tech job adverts from [CWJobs.co.uk](https://www.cwjobs.co.uk), the UK IT job board of the Totaljobs Group. Every job comes with CWJobs' own label for the advertiser (direct employer or recruitment agency), the contract type (permanent or contract), salary, location, skills, posting date and a link to the advert.

You can search by keyword and location, or leave both empty and get every IT job on the board for the last day, three days, week or two weeks.

### How to scrape CWJobs data

1. Open CWJobs Scraper in the Apify Console.
2. Enter a keyword such as `developer`, `data engineer` or `cyber security`, or leave it empty for the whole board.
3. Optionally enter a UK location such as `london` or `manchester`.
4. Pick how recent the jobs should be under **Posted within** (default: last 7 days).
5. Optionally keep only direct employers or only agencies, and only permanent or only contract jobs.
6. Set **Max results** and click **Start**.
7. Download the results as JSON, CSV or Excel from the Output tab, or read them through the Apify API.

### What you can use it for

- **Hiring signals for recruiters.** A weekly list of UK companies that advertise IT roles themselves, with the stack they hire for.
- **Agency market mapping.** Which recruitment agencies post which IT roles, in which regions, and how many.
- **Salary and rate benchmarking** for permanent and contract IT roles across the UK.
- **Labour market research** on demand for skills such as Python, AWS, Azure or Kubernetes.
- **Job aggregation** for niche IT job sites and alerts.

### Input

| Field | Type | Description |
|-------|------|-------------|
| `searchQuery` | string | Job keyword. Empty = every job on CWJobs. |
| `location` | string | UK town, city or region. Empty = whole UK. |
| `postedWithin` | string | `1`, `3`, `7` or `14` days, or empty for any time. Default `7`. |
| `companyType` | string | `all` (default), `direct` or `agency`. Uses CWJobs' "Type of Company" filter. |
| `contractType` | string | `all` (default), `permanent` or `contract`. |
| `tagAdvertiserAndContractType` | boolean | Default `true`. Fills `advertiserType` and `contractType` (see below). |
| `maxResults` | integer | Maximum jobs to return. Default 100, hard cap 50,000. |
| `proxyConfiguration` | object | Default: Apify automatic proxy. No residential proxy needed. |

#### Example input

```json
{
    "searchQuery": "",
    "postedWithin": "7",
    "companyType": "all",
    "maxResults": 5000
}
```

#### About the advertiser and contract labels

CWJobs does not put "Direct Employer" or "Agency" on the job itself. It only exposes them through its search filters. With `tagAdvertiserAndContractType` switched on (the default), the actor runs the same search again with each filter and labels the jobs it finds. This costs a few extra requests per run. Switch it off for a faster run where `advertiserType` and `contractType` are empty.

CWJobs only labels part of its adverts. In October 2026 about half of the adverts of a week carried a "Direct Employer" or "Agency" label. Adverts that arrive through employers' own applicant tracking systems (large tech companies, universities, public bodies) usually have no label. Those jobs come back with `advertiserType: null`, never with a guessed value.

### Output

Each item is one job advert.

| Field | Description |
|-------|-------------|
| `jobId` | CWJobs / Totaljobs job id |
| `jobTitle` | Job title |
| `company` | Advertiser name (employer or agency) |
| `companyId` | CWJobs company id |
| `companyProfileUrl` | The advertiser's job list on CWJobs |
| `companyLogo` | Logo URL |
| `advertiserType` | `direct_employer`, `agency` or `null` |
| `advertiserTypeSource` | `job` (CWJobs labels this advert) or `company` (label taken from the same company's other adverts) |
| `contractType` | `permanent`, `contract` or `null` |
| `location`, `postCode` | Location as advertised |
| `locationPrecision` | `region` when the advert only names a region (South East, Yorkshire, UK), `place` otherwise |
| `locationFromAdvert`, `locationFromAdvertSource` | With `resolveRegionOnlyLocations` on: the town read from the advert text of a region-only advert, null when the advert does not say |
| `salary`, `currency` | Salary text as advertised, `GBP` when given |
| `remoteOption` | Work from home label |
| `skills` | Skills tagged by CWJobs |
| `descriptionSnippet` | Start of the description |
| `sourceSite` | Group site the advert was posted on. CWJobs shows Totaljobs IT adverts too |
| `anonymousAdvertiser` | `true` when the advertiser hides its name |
| `sponsored` | Paid placement |
| `postedDate`, `expiryDate` | ISO dates |
| `url` | Link to the advert |
| `scrapedAt` | Run timestamp |

#### Example output

```json
{
    "jobId": 108178124,
    "jobTitle": "Full Stack Engineer",
    "company": "Leeds Building Society",
    "companyId": 334816,
    "companyProfileUrl": "https://www.cwjobs.co.uk/jobs/leeds-building-society?cmpId=334816",
    "advertiserType": "direct_employer",
    "advertiserTypeSource": "job",
    "contractType": "permanent",
    "location": "Leeds",
    "salary": "Competitive",
    "currency": "GBP",
    "sourceSite": "Totaljobs.com",
    "postedDate": "2026-10-15T08:15:46.537Z",
    "expiryDate": "2026-11-25T09:15:46.537Z",
    "url": "https://www.totaljobs.com/job/full-stack-engineer/leeds-building-society-job108178124",
    "scrapedAt": "2026-10-15T09:00:00.000Z"
}
```

### Cost estimate

The actor reads CWJobs' result list as JSON, 25 jobs per request, on Apify's automatic proxy. A 20-job test run used about $0.0005 of platform usage. A full week of the whole board (about 9,000 jobs) is roughly 800 requests including the labelling passes. Usage for a run only settles after the run has finished with SUCCEEDED, so a dataset read during a run shows a cost that is far too low.

### Limitations

- CWJobs labels only part of its adverts as "Direct Employer" or "Agency". Unlabelled adverts have `advertiserType: null`.
- `postedWithin` is CWJobs' own filter. It also returns some older adverts that CWJobs still treats as recent (in October 2026 about a third of a "last 7 days" pull had an original `postedDate` older than 7 days). Filter on `postedDate` if you need the strict window.
- Some agencies label themselves as direct employers. Check the name if the split matters to you.
- Company websites are not shown on CWJobs. `companyProfileUrl` points to the company's job list on CWJobs.
- Totaljobs IT adverts are cross-posted to CWJobs, so the same job can also appear in a Totaljobs dataset.
- The run fails on purpose when no job is found, so an empty dataset never looks like a successful run.

### About CWJobs

CWJobs.co.uk is the IT and technology job board of the Totaljobs Group (StepStone). It lists permanent and contract roles across software development, data, cloud, infrastructure, security, IT support and project delivery, from employers and recruitment agencies across the UK.

### Maintained by Studio Amba

We run 700+ scrapers for European websites, and the priority is keeping
every one of them working. Automated runs test this actor against the live
site and verify the output is complete. When the website changes, the
scraper usually gets repaired the same day, without you having to report
anything. Questions and issues go straight to the people who built it, and
most get an answer the same day.

If this actor works for you, a review on its Store page helps other people
find it.

### Need this data on a schedule, or a custom version?

We run this scraper as a managed service for businesses: scheduled runs,
deduplication, delta detection, and delivery to your inbox, Google Sheets,
or API, maintenance included. We can also build a custom version with your
exact fields and filters, or combine multiple sources into one feed.

See [studioamba.dev/services](https://studioamba.dev/services/) or email
<hello@studioamba.dev> for a free data sample.

# Actor input Schema

## `searchQuery` (type: `string`):

Job keyword, for example 'developer', 'data engineer' or 'cyber security'. Leave empty to get every job on CWJobs (CWJobs only lists IT and tech jobs).

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

UK town, city or region, for example 'london' or 'manchester'. Leave empty for the whole UK.

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

Only jobs posted in the last 1, 3, 7 or 14 days. Leave on 'Any time' for every live job.

## `companyType` (type: `string`):

Only direct employers, only recruitment agencies, or both. Uses CWJobs' own 'Type of Company' filter.

## `contractType` (type: `string`):

Only permanent jobs, only contract jobs, or both.

## `tagAdvertiserAndContractType` (type: `boolean`):

ON by default and load-bearing: CWJobs only exposes the 'Direct Employer / Agency' and 'Permanent / Contract' labels through its filters, so the actor runs extra filtered passes to fill `advertiserType` and `contractType`. Turn it off for a faster run with those two fields empty (null). Jobs that CWJobs does not label stay null either way.

## `resolveRegionOnlyLocations` (type: `boolean`):

OFF by default. About a fifth of CWJobs adverts give only a region as location ('South East', 'Yorkshire', 'UK'); CWJobs' 'South East' also covers London. When ON, the actor opens each such advert and reads the town from the advert text into `locationFromAdvert` (null when the advert does not say). One extra request per region-only advert, so the run takes longer.

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

Maximum number of jobs to return. Hard cap 50,000.

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

Proxy settings. The CWJobs result endpoint works on Apify's automatic proxy pool (no residential proxy needed, recon 2026-10-15).

## Actor input object example

```json
{
  "searchQuery": "developer",
  "postedWithin": "7",
  "companyType": "all",
  "contractType": "all",
  "tagAdvertiserAndContractType": true,
  "resolveRegionOnlyLocations": false,
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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 = {
    "searchQuery": "developer",
    "postedWithin": "7",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/cwjobs-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 = {
    "searchQuery": "developer",
    "postedWithin": "7",
    "maxResults": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/cwjobs-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 '{
  "searchQuery": "developer",
  "postedWithin": "7",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call studio-amba/cwjobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studio-amba/cwjobs-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/hUhSheMDwbA7UFt8w/builds/xkv8Qn3s9237gSgnD/openapi.json
