Jobsite UK Scraper - Jobs, Salaries & Company Details
Pricing
from $1.00 / 1,000 results
Jobsite UK Scraper - Jobs, Salaries & Company Details
Scrape Jobsite.co.uk job listings at scale by keyword, location, filters or URL. Extract 35+ structured fields per job, including titles, salaries and company details. Optional enrichment adds full descriptions, GPS coordinates, employment types and company profiles.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
0
Monthly active users
10 days ago
Last modified
Categories
Features
Share
Jobsite.co.uk Scraper: UK Job Listings with Salary, Employer and Skills Data
Jobsite.co.uk Scraper turns Jobsite.co.uk search results into a clean UK jobs dataset. Get 35+ fields per listing straight from the search page, including parsed salary (minimum, maximum, currency, period), employer details, skills, posting source, and sponsorship flags, at no extra cost. Search by keyword, location, and filters, or paste search-result URLs directly, then switch on full detail enrichment for the complete job description, GPS coordinates, employment type, valid-through date, and a company profile. Export the dataset as JSON, CSV, or Excel, or read it straight through the API.
Why This Scraper?
- 35+ listing fields per job, straight from the search page: parsed salary (min, max, currency, period), employer, skills, cross-post count, and sponsorship flags, with no extra cost.
- Optional full-detail enrichment. Turn on
fetchDetailsto add the complete job description, GPS coordinates, employment type, valid-through date, apply type, and a company profile (benefits, industries, jobs count). - Two ways to find jobs. Build a search from keyword, location, and filters, or paste one or more Jobsite.co.uk search-result URLs (the run paginates them) or single job-page URLs (the run scrapes them).
- Server-side filters that match the site's own facets. Keyword, location, job type, advertiser type, minimum salary with its own period, posted-within window, and sort order.
- One cap that matters.
maxListingsis the sole limit on job count (default20,0for unlimited);maxPagesis an optional extra safety ceiling, off by default. - Resumable and checkpointed. Point
resumeFromRunIdat a previous run to collect only the delta, and a long unlimited run checkpoints its own progress so a platform restart continues instead of starting over. - Built for schedules. Turn on
incrementalModeto get only new and changed jobs on a recurring run, and a fully blocked run fails loudly instead of returning an empty dataset.
Use Cases
- Job board aggregators: mirror Jobsite.co.uk listings into your own site or app alongside other sources.
- Salary and market researchers: build a dataset of parsed salary ranges by role, location, and job type.
- Recruitment agencies and sales teams: track which companies are hiring, and for what, to prioritize outreach.
- Compliance and brand teams: monitor a company's own postings for accuracy and recruitment marketing.
- Job alert products: schedule the actor with Incremental mode on and pipe only new matching roles into your app.
Data You Get
Sample shape: values are illustrative placeholders, not from a live listing.
| Field | Example |
|---|---|
jobId | 500000001 |
title | Assistant Warehouse Operative |
jobUrl | https://www.jobsite.co.uk/job/assistant-warehouse-operative/sample-company-job500000001 |
sourceSite | Totaljobs.com |
employer.name | Sample Company |
employer.url | https://www.jobsite.co.uk/jobs/sample-company?cmpId=1000001&cmp=1 |
location.text | Sample Town, Sample County (BA0), BA0 0AA |
location.postalCode | BA0 0AA |
salary.rawText | £26,000 - £29,500 per annum |
salary.min / salary.max | 26000 / 29500 |
salary.currency / salary.period | GBP / year |
datePosted | 2026-08-14T09:00:00.000Z |
workFromHome | Hybrid |
skills | ["Sample Skill A", "Sample Skill B"] |
textSnippet | Short preview text pulled from the listing appears here. |
crossPostedCount | 1 |
isSponsored | false |
description (fetchDetails only) | <p>Full HTML job description...</p> |
employmentType (fetchDetails only) | ["FULL_TIME"] |
validThrough (fetchDetails only) | 2026-09-14T00:00:00.000Z |
location.latitude / location.longitude (fetchDetails only) | 51.1500 / -2.3300 |
company.jobsCount (fetchDetails only) | 6 |
contactPhones / contactEmails (fetchDetails only) | ["+44 7700 900123"] / [] |
Every record also carries a rowType, a harmonisedId, publishFromDate/publishToDate, the employer's id and isAnonymous flag, location.region/country/streetAddress, the isHighlighted/isTopJob/isTrafficFromPartner flags, a partnership block (isBackfilled, isCrossPosted, isPartnershipJob, sourceSiteFriendlyName), and travelTime when the search included a distance. With fetchDetails on, a job can also carry applicantLocationRequirements, externalId, contractType, workType, a detailFetched flag, and a company object (name, founded, size, industries, benefits, videos, images, description). contactPhones and contactEmails are a best-effort harvest of any contact details a recruiter typed into the job description or exposed as a click-to-call/email link; the platform does not publish structured company contact details, so most records return empty arrays. In Incremental mode, every record also carries changeType, changedFields, firstSeenAt, and lastSeenAt.
How to Use
- Pick a mode:
search(build from filters) orurl(paste search-result URLs or single job-page URLs). - Fill in your filters, and switch on
fetchDetailsif you need the full job description and company profile. - Set Max jobs to control run size and cost, then click Start.
- Download the dataset as JSON, CSV, or Excel, or read it through the API.
Basic search by location:
{"mode": "search","locations": ["London"],"maxListings": 20}
Keyword search with filters:
{"mode": "search","locations": ["Manchester"],"keywords": "nurse","jobType": "permanent","minSalary": 30000,"salaryType": "annual","postedWithin": "7","sortBy": "date","maxListings": 100}
Full details:
{"mode": "search","locations": ["London"],"keywords": "nurse","fetchDetails": true,"maxListings": 50,"proxy": { "useApifyProxy": true }}
Leave proxy at its default here; the actor escalates to Apify Residential with country GB automatically for listing pages and detail fetches, so you do not need to pick a group yourself.
Paginate search URLs directly (multiple supported):
{"mode": "url","urls": ["https://www.jobsite.co.uk/jobs/in-london","https://www.jobsite.co.uk/jobs/in-leeds"],"maxListings": 50}
URL shapes (URL mode)
Untouched URL mode scrapes one example of every shape below, so set your own urls before a real run.
| Shape | Example |
|---|---|
| Location search | https://www.jobsite.co.uk/jobs/in-london |
| Keyword and location | https://www.jobsite.co.uk/jobs/nurse/in-manchester |
| Keyword or category only | https://www.jobsite.co.uk/jobs/accountancy |
| Job type and location | https://www.jobsite.co.uk/jobs/permanent/in-london |
| Keyword, job type and location | https://www.jobsite.co.uk/jobs/nurse/permanent/in-london |
| Paginated (starts at that page) | https://www.jobsite.co.uk/jobs/in-london?page=2 |
| Filtered and sorted | https://www.jobsite.co.uk/jobs/in-london?salary=30000&salarytypeid=1&postedWithin=7&companytypes=2&sort=2 |
| Radius | https://www.jobsite.co.uk/jobs/in-london?radius=10 |
| Company jobs page | https://www.jobsite.co.uk/jobs/abbatt-property-recruitment?cmpId=692964&cmp=1 |
| Single job page (one row, full detail fields) | https://www.jobsite.co.uk/job/people-advisor/one-small-thing-job108272685 |
| Single job page, short forms | https://www.jobsite.co.uk/job/aster-group-job108273301 or https://www.jobsite.co.uk/job/108273301 |
| Plain http or bare host | http://jobsite.co.uk/jobs/in-london (normalised to https://www.) |
Not supported, skipped with a warning in the run log: the query form /jobs?q=...&l=... and the legacy /jobs-in-<location> path. A search whose location or keyword matches nothing logs a clear error instead of finishing silently empty. A single job page is charged the detail event only when fetchDetails is on.
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/jobsite-co-uk-scraper").call(run_input={"mode": "search", "locations": ["London"], "maxListings": 20})for job in client.dataset(run["defaultDatasetId"]).iterate_items():print(job["title"], job["employer"]["name"], job["salary"]["rawText"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/jobsite-co-uk-scraper').call({ mode: 'search', locations: ['London'], maxListings: 20 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
How maxListings and maxPages shape a run
maxListings is the one cap that matters for job count (default 20; set 0 for unlimited), and is split evenly across every location or URL in the run. maxPages is an optional extra safety ceiling on pages walked per search or URL (25 jobs per page); left at its default of 0 it does not limit the walk at all, the run instead stops on its own once the site's own reported page count is reached, a page comes back empty, or a page returns nothing that hasn't already been collected earlier in this same run. There is no artificial page-number ceiling built in, so raise or lower maxPages only when you want an extra stop of your own.
That last stop condition, a page returning nothing new, is shared across the whole run, not scoped to one search: the ids it checks against include everything any location or URL in this same run has already collected, not just this search's own earlier pages. With more than one location or URL configured, a later search whose page happens to overlap entirely with an earlier search's already-collected jobs can stop there, before reaching its own genuinely new jobs further down. Keep a run to a single location or URL if you need a guarantee of reaching every result.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one interrupted or capped run: paste its run or dataset id and this run skips every job already in that dataset, returning only the new ones (a delta); combine both datasets for the full set. This works cleanly when the search's result order hasn't shifted since the interrupted run. If new jobs pushed older ones down the page order in the meantime, the resumed walk can hit a page made entirely of already-collected jobs and stop there before reaching genuinely new jobs further down, the same early-stop condition described under "How maxListings and maxPages shape a run" above, so a resumed run is not guaranteed to reach every remaining job. A long unlimited run also checkpoints its own progress, so a platform restart or a manual retry of a failed run picks up where it left off; if that checkpoint restart happens with Incremental mode andemitExpiredboth on, be aware that jobs from searches the restarted run skips entirely (because the checkpoint had already finished them) can be markedEXPIREDeven though they were never re-read this run, since the run's completeness check does not currently distinguish "skipped because already done" from "genuinely scanned." - Incremental mode (
incrementalMode) is for a search you run on a recurring schedule. Each job is classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on; with it on they are returned and billed, including the detail-enrichment surcharge iffetchDetailsfetched their detail page),REAPPEARED, orEXPIRED. A job is only ever marked no-longer-present (the state that makes a later sightingREAPPEARED) once an earlier run hademitExpiredon and completed a full, uncapped scan of every search in scope; short of that, a job simply not reached this run keeps its last known state instead of being guessed at.EXPIREDrows themselves are only emitted, and billed, on a run that reaches that same natural, uncapped end, is not aresumeFromRunIdrun, and hasemitExpiredon. When you configure more than one location or URL in the same Incremental run, the shared early-stop condition described above means one search can occasionally end early because it overlapped with another search's results in this same run, and that early end is still counted as reaching the natural end, which can produce an incorrectEXPIREDrow for a job that was simply never reached rather than actually gone; keep each tracked scope to its own run if you rely onemitExpired.stateKeynames or shares the stored baseline; leave it empty to derive one automatically from your search filters. This is a separate mechanism from Resume: Resume finishes one specific run by id, Incremental mode tracks the same recurring search on its own between runs. Combining both is only meaningful once, to bootstrap a brand-new Incremental baseline from an existing Resume dataset; after that, astateKeywith saved state rejects a run that also setsresumeFromRunId, since resuming would prevent a full scan and make laterEXPIREDdetection unreliable. Note that a bootstrapped job from that first combined run has no prior fingerprint to compare against, so the very next ordinary Incremental run reports it asUPDATED(with every field listed as changed) rather thanUNCHANGED, and bills it again that once.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape; the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Dataset views
The Output tab offers four views: Jobs Overview, Full Details, Changes (Incremental mode) and Contacts.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search builds a URL from filters; url paginates pasted search-result URLs and scrapes pasted job pages. |
locations | array | prefill ["London"] | Towns, cities, counties, or regions (search mode). One search per location. Leaving it empty falls back to London, not all of the UK. |
keywords | string | (empty) | Job title, skill, or company to search for. Leave empty to list all jobs in the location. |
jobType | string | any | permanent, contract, temporary, part-time, or work-from-home. |
companyType | string | any | employer (direct) or agency. |
minSalary | integer | (empty) | Lowest salary to include, paired with salaryType. Leave empty for no minimum. |
salaryType | string | annual | Unit for minSalary: annual, daily, or hourly. |
postedWithin | string | "0" | Only jobs posted within 1, 3, 7, or 14 days. "0" = any time. |
sortBy | string | relevance | relevance, date, salary-desc, salary-asc, or distance. |
urls | array | one example per supported shape (12 URLs) | Jobsite.co.uk URLs (URL mode): result lists and single job pages. Multiple supported; filter fields above are ignored. Pagination starts at the page in the URL and walks forward. See the URL shapes table. |
fetchDetails | boolean | false | Open each job page for the full description, GPS coordinates, employment type, valid-through date, and company profile. Needs an Apify Residential GB connection. |
maxPages | integer | 0 | Optional safety limit on pages walked per location/URL (25 jobs per page). 0 = walk until the run's own stop conditions are met (see "How maxListings and maxPages shape a run" above). |
maxListings | integer | 20 | Total jobs to collect across all searches. The main limit. 0 = unlimited (still bounded by maxPages if set). |
radiusMiles | integer | (empty) | Search radius in miles (0-100) around each location. Empty = the site default. |
startUrls | array | (empty) | Alias of urls; when set it wins and switches to URL mode. |
maxItems | integer | (empty) | Alias of maxListings; when set it wins. |
maxPagesPerSearch | integer | (empty) | Alias of maxPages; when set it wins. |
advertiserType | string | (empty) | Alias of companyType; when set it wins. |
salaryPeriod | string | (empty) | Alias of salaryType; when set it wins. |
postedWithinDays | string | (empty) | Alias of postedWithin; when set it wins. any means no date filter. |
detailMode | string | (empty) | fullDetails fetches every job page (same as fetchDetails on); fastListings returns listing data only. When set it wins over fetchDetails. partTime / workFromHome are also accepted for jobType. |
includeContactHints | boolean | true | With details on, collect phone numbers and emails from the job page into contacts with the evidence for each. Off leaves them empty. |
maxResidentialRequests | integer | 0 | Cap on Residential connection requests this run may use, counted only for the actor's own automatic detail-page and listing-escalation connections, not for a Residential group you set yourself under proxy. 0 = unlimited. Once reached, detail enrichment stops for the rest of the run and listing continues on a backup connection where one is available. |
resumeFromRunId | string | (empty) | Optional id of a previous run (or dataset) of this actor. Jobs already in that dataset are skipped, so this run returns only new jobs (a delta). |
incrementalMode | boolean | false | Track a recurring search across runs; only NEW/UPDATED jobs are returned by default. See "Resume and recurring updates" above. |
stateKey | string | (empty) | Optional label for the Incremental mode baseline. Leave empty to derive it automatically from the search filters. |
emitUnchanged | boolean | false | Incremental mode only. Also return (and bill) jobs with no change (changeType: UNCHANGED). |
emitExpired | boolean | false | Incremental mode only. Emit changeType: EXPIRED rows for jobs no longer found, only when this run reaches a natural, uncapped end. |
ignoreFieldsForChanges | array | [] | Incremental mode only. Field names (dotted for nested, e.g. location.region) that never count as a change: a job differing only in these stays UNCHANGED and they are left out of changedFields, but they remain in the output. Changing the list does not re-bill the stored baseline. |
proxy | object | prefill (Apify proxy on) | Connection settings. Jobsite.co.uk refuses datacenter connections on search pages, so listing and fetchDetails both need Apify Residential with country GB; the actor escalates to it automatically. |
mcpConnectors | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which records are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. |
Output Example
Sample shape: values are illustrative placeholders, not from a live listing.
{"rowType": "job","sourceSearchUrl": "https://www.jobsite.co.uk/jobs/in-bristol","jobId": 500000001,"harmonisedId": "00000000-0000-0000-0000-000000000000","jobUrl": "https://www.jobsite.co.uk/job/assistant-warehouse-operative/sample-company-job500000001","sourceSite": "Totaljobs.com","title": "Assistant Warehouse Operative","datePosted": "2026-08-14T09:00:00.000Z","publishFromDate": "2026-08-14T09:00:00.000Z","publishToDate": "2026-09-11T09:00:00.000Z","employer": {"id": 1000001,"name": "Sample Company","url": "https://www.jobsite.co.uk/jobs/sample-company?cmpId=1000001&cmp=1","logoUrl": "https://www.jobsite.co.uk/CompanyLogos/00000000000000000000000000000000.png","isAnonymous": false},"location": {"text": "Sample Town, Sample County (BA0), BA0 0AA","postalCode": "BA0 0AA","locality": null,"region": null,"country": null,"latitude": null,"longitude": null,"streetAddress": null},"salary": { "rawText": "£26,000 - £29,500 per annum", "min": 26000, "max": 29500, "currency": "GBP", "period": "year" },"workFromHome": null,"skills": ["Sample Skill A", "Sample Skill B"],"textSnippet": "Short preview text pulled from the listing appears here.","crossPostedCount": 1,"isSponsored": false,"isHighlighted": false,"isTopJob": false,"isTrafficFromPartner": false,"partnership": { "isBackfilled": true, "isCrossPosted": false, "isPartnershipJob": true, "sourceSiteFriendlyName": "Totaljobs.com" },"travelTime": null,"description": null,"descriptionText": null,"validThrough": null,"employmentType": null,"industry": null,"directApply": null,"applyType": null,"jobLocationType": null,"applicantLocationRequirements": null,"externalId": "00000000-0000-0000-0000-000000000000","contractType": null,"workType": null,"company": null,"contactPhones": [],"contactEmails": [],"detailFetched": false}
Plan Requirement
Jobsite.co.uk refuses datacenter connections on its search pages (the site answers with a plain "Access Denied" page), so the default proxy: { "useApifyProxy": true } connection alone does not return listings. Listing pages and fetchDetails both need an Apify Residential connection with country GB. The actor starts on your proxy setting, then escalates to Residential GB on its own when the first connection is refused; that residential traffic is your proxy cost, and maxResidentialRequests caps it (0 = unlimited). If your Apify plan has no residential access (free plans do not), the run logs a notice and continues on a backup connection, where some pages may still be unavailable; if nothing loads it stops with a clear message rather than returning an empty dataset. Setting a Residential group yourself under proxy is also fine, but that usage is not counted by maxResidentialRequests. If you supply your own proxy URLs, the actor uses only those and adds no backup connection.
FAQ
How much does it cost?
You pay per record returned, plus a detail-enrichment surcharge, charged one row at a time as each job is pushed, only for jobs whose detail page was actually fetched and delivered with fetchDetails on. It is never charged for rows Incremental mode suppresses (UNCHANGED rows while emitUnchanged is off) or for failed or deduplicated rows; with emitUnchanged on, returned UNCHANGED rows that were detail-enriched are charged the surcharge too. The Pricing tab shows the current rates. Use Max jobs to cap the cost of any run.
Is it legal to scrape Jobsite.co.uk?
This actor collects only publicly available job listings. You are responsible for how you use the data: follow Jobsite.co.uk's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution, especially for any personal or contact details a posting happens to include.
Can I get only new or changed jobs on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated jobs, and unchanged ones are not returned or billed unless you turn on emitUnchanged.
Why did my run return nothing or fail on a free plan?
Jobsite.co.uk refuses datacenter connections, so listing needs Residential GB, which Apify free plans do not include. In that case the run logs: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." If no page loads, the run fails with a message such as "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Jobsite pages." or, if the proxy itself was refused, "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required". A search that genuinely matches nothing still succeeds, with a status message saying so. A message that says "Could not load any results page (the requests failed)" is a connection problem, not an empty search: try again in a few minutes.
Why did my fetchDetails run only return listing fields?
fetchDetails also needs Residential GB, which the actor sets up for itself. If your account has no residential access, the run logs a warning, turns detail enrichment off for that run, and returns the listing fields only instead of failing.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear, or Airtable.
🔗 Want more jobs data?
Pair this actor with these related scrapers from the same team:
| 💼 Reed.co.uk Scraper Scrape Reed.co.uk jobs, companies, courses and reviews by keyword, location, filters or... | 💼 Totaljobs Scraper Scrape UK job listings from Totaljobs.com by keyword, location, filters, or URL. Extract... |
| 💼 CWJobs UK Scraper Scrape CWJobs.co.uk tech and IT job listings by keyword, location, salary, work type... | 🏨 Caterer.com Scraper Scrape UK hospitality jobs from Caterer.com, including chef, hotel, restaurant, bar, and... |
| 💼 NHS UK Scrape nhs.uk Jobs listings into a flat dataset. Extract 50+ fields, including pay band... | 💼 SimplyHired Scraper Scrape active SimplyHired jobs across the US, UK, Canada, Australia, Ireland and India... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.