# CWjobs Scraper (`maximedupre/cwjobs-scraper`) Actor

Scrape public CWjobs UK tech jobs from keywords, listing URLs, or direct job pages. Export titles, employers, locations, salaries, dates, descriptions, and source links.

- **URL**: https://apify.com/maximedupre/cwjobs-scraper.md
- **Developed by:** [Maxime Dupré](https://apify.com/maximedupre) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

$1.35 / 1,000 scraped 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

### 🔎 Scrape CWjobs UK tech jobs

CWjobs Scraper collects public job listings from [CWjobs](https://www.cwjobs.co.uk/) and saves them as structured data you can export, schedule, or send through the Apify API. Use it to turn CWjobs searches, listing URLs, direct job URLs, and supported public company pages into clean job rows with titles, employers, locations, salaries, posting dates, descriptions, source links, and scrape metadata.

Start with a simple search such as `javascript` in `London`, keep the default result limit for a small first run, and review the dataset before scaling up. If you already have CWjobs URLs, paste listing pages or direct job pages instead of using keyword search.

### ✅ What this CWjobs scraper does

- Searches CWjobs by keyword and optional UK location.
- Scrapes public CWjobs listing pages and direct job pages.
- Accepts CWjobs listing URLs, direct job URLs, and supported public company URLs.
- Normalizes matching Totaljobs job URLs back to CWjobs when the job belongs to the CWjobs source.
- Saves one dataset item per accepted job posting.
- Extracts job title, employer, location, salary, dates, description, workplace type, apply metadata, source position, and scrape time.
- Supports result limits, freshness filters, remote-only searches, and optional minimum salary search filters.
- Can try to resolve apply URLs when you enable the optional apply URL enrichment.
- Works with Apify exports, schedules, webhooks, integrations, and API clients.

This Actor focuses on public CWjobs job data. It does not log in, submit applications, contact employers, scrape candidate data, or collect private account pages.

### 📦 What data you get

Each dataset item is one CWjobs job. Fields can be empty when CWjobs does not show that value for a specific posting.

- `jobId`: source job identifier when available
- `jobUrl`: canonical CWjobs job URL
- `listingUrl`: listing page that produced the job, when found from a listing
- `sourceSite`: source website name
- `title`: job title
- `descriptionHtml` and `descriptionText`: job description content
- `datePosted` and `validThrough`: posting dates when available
- `employmentType` and `industry`: source employment and category labels
- `directApply`, `applyType`, and `applyUrl`: apply metadata when available
- `employer`: employer name, profile URL, and logo URL
- `location`: location text, locality, region, postal code, country, latitude, and longitude
- `salary`: raw salary text plus normalized min, max, currency, period, and confidence
- `workplaceType`: remote, hybrid, or source-provided workplace signal when available
- `searchContext`: input target and search terms that produced the row
- `position`: page position and absolute position from listing results
- `scrapedAt`: scrape timestamp
- `extractionStatus`: completeness status for the saved job

### 🧭 Common use cases

- Track UK tech hiring demand by role, location, employer, or salary band.
- Build repeatable CWjobs exports for recruiting, market research, or job-board analysis.
- Monitor competitor hiring in software engineering, DevOps, data, cloud, security, and IT roles.
- Create structured job feeds for dashboards, alerts, CRMs, spreadsheets, or data warehouses.
- Compare posted salary ranges and workplace signals across CWjobs search results.
- Save direct CWjobs job pages into a consistent dataset for later review.

### 🚀 How to run it

For the easiest first run:

1. Keep `Search keywords` set to `javascript`.
2. Keep `Location` set to `London`, or replace it with another UK location.
3. Set `Result limit` to a small number such as `25`.
4. Run the Actor and open the dataset.

You can also skip keyword search and paste URLs into `Start URLs`. Supported URL types include CWjobs search/listing pages, direct job pages, and supported public company pages. Direct job URLs normally produce one job item each.

### 🎛️ Input options

- `Search keywords`: one or more role, skill, or job terms to search on CWjobs.
- `Location`: optional UK location used with keyword searches.
- `Start URLs`: CWjobs listing pages, direct job pages, or supported public company URLs.
- `Result limit`: maximum number of jobs to save across the whole run.
- `Posted within`: optional freshness filter for keyword searches.
- `Remote only`: searches remote CWjobs roles when building URLs from keywords.
- `Minimum salary`: optional annual salary filter for keyword searches when CWjobs accepts it.
- `Resolve apply URLs`: best-effort apply link enrichment. Keep it disabled for the fastest first run.

You do not need CWjobs cookies, a CWjobs account, or a separate API key.

### 🧾 Output example

```json
{
  "jobId": "107456397",
  "jobUrl": "https://www.cwjobs.co.uk/job/full-stack-engineer-c-net-core-javascript/client-server-job107456397",
  "listingUrl": "https://www.cwjobs.co.uk/jobs/javascript/in-london/",
  "sourceSite": "cwjobs.co.uk",
  "title": "Full Stack Engineer C## .Net Core JavaScript",
  "employer": {
    "name": "Client Server",
    "url": "https://www.totaljobs.com/jobs/client-server?cmpId=1375834&cmp=1",
    "logoUrl": "https://www.totaljobs.com/CompanyLogos/0121bf260b964108866f00d1d5d91ae1.png"
  },
  "location": {
    "text": "St. Albans, Hertfordshire, AL1, GB",
    "locality": "St. Albans",
    "region": "Hertfordshire",
    "postalCode": "AL1",
    "country": "GB",
    "lat": 51.75388,
    "lng": -0.33066
  },
  "salary": {
    "rawText": "£100k",
    "min": 100000,
    "max": null,
    "currency": "GBP",
    "period": null,
    "confidence": "visible-text"
  },
  "extractionStatus": "complete"
}
````

### 💳 Pricing

This Actor uses pay-per-event pricing. You are charged for each CWjobs job saved to the dataset. Runs that find no matching jobs do not create paid job items.

### ⚠️ Limits and caveats

- CWjobs can omit fields such as salary, employment type, or apply URL on some postings.
- `applyUrl` is best-effort and may stay empty even when the job can be applied to on CWjobs.
- Company pages are supported only when their public job listings can be resolved without login.
- The Actor is built for public CWjobs data, not private recruiter dashboards or application workflows.
- Recoverable source issues are handled as skipped pages or empty results instead of placeholder dataset items.

### ❓ FAQ

#### 🔐 Can I scrape CWjobs without a login?

Yes. This Actor works with public CWjobs job pages and does not ask for CWjobs cookies, passwords, or API keys.

#### 🔗 Can I use direct job URLs?

Yes. Add direct CWjobs job URLs to `Start URLs`. Each valid direct job URL normally saves one job item.

#### 🔄 Does it support Totaljobs URLs?

Only for matching job URLs that can be normalized back to CWjobs. This Actor is not a broad Totaljobs or StepStone Group scraper.

#### 💷 Why is a salary field empty?

Some CWjobs postings do not show salary details. When salary text is visible, the Actor saves the raw text and tries to normalize numeric min and max values.

### 📝 Changelog

- 0.0: Initial release.

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~cwjobs-scraper/issues) and I'll fix or implement it in less than 24h 🫡

### 🔗 Other actors

- [Freelancer Scraper ↗](https://apify.com/maximedupre/freelancer-scraper) - Export public Freelancer.com projects and freelancer profiles.
- [LinkedIn Company Scraper ↗](https://apify.com/maximedupre/linkedin-company-scraper) - Collect public company profile data for lead lists and market research.
- [BusinessesForSale Scraper ↗](https://apify.com/maximedupre/businessesforsale-scraper) - Scrape public business-for-sale listings with prices, locations, and seller links.
- [Reddit Scraper ↗](https://apify.com/maximedupre/reddit-scraper) - Search Reddit posts and comments for topic and market monitoring.
- [Website URL Crawler ↗](https://apify.com/maximedupre/website-url-crawler) - Crawl public websites and export discovered links with source context.

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

## `searchQueries` (type: `array`):

Roles, skills, or job terms to search on CWjobs, such as JavaScript, DevOps, data engineer, or software developer. Leave empty when using only Start URLs.

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

Optional UK location for keyword searches, such as London, Manchester, Birmingham, Leeds, or Remote.

## `startUrls` (type: `array`):

CWjobs listing pages, direct job pages, or supported public company pages. Matching Totaljobs job URLs are normalized back to CWjobs when possible.

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

Maximum number of CWjobs jobs to save across the whole run. Use a small limit for the first run, then raise it when the output looks right.

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

Only search jobs posted within the selected time window when CWjobs supports the filter.

## `remoteOnly` (type: `boolean`):

Search remote CWjobs roles when building listing URLs from search keywords.

## `salaryMin` (type: `integer`):

Minimum annual salary to request for keyword searches when CWjobs accepts the filter.

## `resolveApplyUrls` (type: `boolean`):

Try to save apply URLs when they are visible and safe to resolve. Keep disabled for the fastest first run.

## Actor input object example

```json
{
  "searchQueries": [
    "javascript"
  ],
  "location": "London",
  "maxItems": 100,
  "postedWithin": "any",
  "remoteOnly": false,
  "resolveApplyUrls": false
}
```

# Actor output Schema

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

Open the dataset with CWjobs job titles, employers, locations, salaries, dates, apply metadata, source URLs, and scrape metadata.

# 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 = {
    "searchQueries": [
        "javascript"
    ],
    "location": "London"
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/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 = {
    "searchQueries": ["javascript"],
    "location": "London",
}

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

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

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

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "javascript"
  ],
  "location": "London"
}' |
apify call maximedupre/cwjobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=maximedupre/cwjobs-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "CWjobs Scraper",
        "description": "Scrape public CWjobs UK tech jobs from keywords, listing URLs, or direct job pages. Export titles, employers, locations, salaries, dates, descriptions, and source links.",
        "version": "0.0",
        "x-build-id": "6b5eZW5dPF88vHGUN"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/maximedupre~cwjobs-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-maximedupre-cwjobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/maximedupre~cwjobs-scraper/runs": {
            "post": {
                "operationId": "runs-sync-maximedupre-cwjobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/maximedupre~cwjobs-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-maximedupre-cwjobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "searchQueries": {
                        "title": "Search keywords",
                        "type": "array",
                        "description": "Roles, skills, or job terms to search on CWjobs, such as JavaScript, DevOps, data engineer, or software developer. Leave empty when using only Start URLs.",
                        "items": {
                            "type": "string",
                            "minLength": 1
                        }
                    },
                    "location": {
                        "title": "Location",
                        "type": "string",
                        "description": "Optional UK location for keyword searches, such as London, Manchester, Birmingham, Leeds, or Remote."
                    },
                    "startUrls": {
                        "title": "Start URLs",
                        "type": "array",
                        "description": "CWjobs listing pages, direct job pages, or supported public company pages. Matching Totaljobs job URLs are normalized back to CWjobs when possible.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL",
                                    "description": "CWjobs listing page, direct job page, or supported public company page URL."
                                }
                            }
                        }
                    },
                    "maxItems": {
                        "title": "Result limit",
                        "minimum": 1,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Maximum number of CWjobs jobs to save across the whole run. Use a small limit for the first run, then raise it when the output looks right.",
                        "default": 100
                    },
                    "postedWithin": {
                        "title": "Posted within",
                        "enum": [
                            "any",
                            "1",
                            "3",
                            "7",
                            "14"
                        ],
                        "type": "string",
                        "description": "Only search jobs posted within the selected time window when CWjobs supports the filter.",
                        "default": "any"
                    },
                    "remoteOnly": {
                        "title": "Remote only",
                        "type": "boolean",
                        "description": "Search remote CWjobs roles when building listing URLs from search keywords.",
                        "default": false
                    },
                    "salaryMin": {
                        "title": "Minimum salary",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Minimum annual salary to request for keyword searches when CWjobs accepts the filter."
                    },
                    "resolveApplyUrls": {
                        "title": "Resolve apply URLs",
                        "type": "boolean",
                        "description": "Try to save apply URLs when they are visible and safe to resolve. Keep disabled for the fastest first run.",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
