TotalJobs UK [only $2/1k] Scraper · Jobs/Salary/Geo (/w EMAILS) avatar

TotalJobs UK [only $2/1k] Scraper · Jobs/Salary/Geo (/w EMAILS)

Pricing

from $2.00 / 1,000 results

Go to Apify Store
TotalJobs UK [only $2/1k] Scraper · Jobs/Salary/Geo (/w EMAILS)

TotalJobs UK [only $2/1k] Scraper · Jobs/Salary/Geo (/w EMAILS)

[only $2] Map the UK job market from TotalJobs.com — 44,490+ postings across 1,780+ listing pages. Each row carries title, employer + logo, location + lat/lng, parsed salary band (min/max/currency/period), dates, and employment type. One outputrecord per job.

Pricing

from $2.00 / 1,000 results

Rating

5.0

(1)

Developer

Muhamed Didovic

Muhamed Didovic

Maintained by Community

Actor stats

0

Bookmarked

13

Total users

12

Monthly active users

a day ago

Last modified

Share

TotalJobs UK Scraper

Scrape job postings from TotalJobs.com (UK) — title, employer + logo, location with lat/lng, parsed salary band (min / max / currency / period), datePosted, validThrough, employment type, industry, and directApply flag. One flat row per job from rich JobPosting JSON-LD.

How TotalJobs UK Scraper works

Why this actor

TotalJobs has 119,000+ live UK job postings — one of the largest UK job boards by inventory. This actor delivers clean structured rows fast:

  • Efficient JSON listing pagination at 50 items per page, with totalItems reported up-front so we know exactly when we've reached the end of a result set
  • Server-side filterspostedWithin (1 / 3 / 7 / 14 days), companyTypes (direct employer / agency), salary (minimum), and jobType (permanent / contract / temporary / part-time / work-from-home) all honoured server-side, identical to the web page
  • Apify Residential GB for the per-job detail fetch — the only proxy pool that consistently returns 200 OK on UK job-board detail pages
  • JobPosting JSON-LD parsing — every row carries the full 14-field JSON-LD detail plus structured salary metadata (min/max/currency/period) so you don't have to regex it client-side
  • applyUrl populated when directApply: true (≈ 60-70 % of jobs) — the candidate-facing apply URL, not just the search-page URL
  • Mixed input — listing URLs auto-paginate + emit one row per detail; direct detail URLs scrape one row each

Use cases

  • Recruitment market intelligence — salary benchmarking by region/role, employer activity tracking
  • Sales prospecting — find companies hiring in your target verticals + locations
  • HR competitive analysis — compare your salary bands against TotalJobs market signal
  • ATS / job-aggregator integration — clean structured input for downstream pipelines
  • Geospatial analytics — every row carries location.lat + location.lng from JSON-LD PostalAddress.geo

Input

FieldTypeRequiredNotes
startUrlsstring[]yesMix of listing URLs (https://www.totaljobs.com/jobs/in-london, /jobs/{keyword}/in-{location}) and direct detail URLs (/job/{title-slug}/{org-slug}-job{id}). Filters supported in the query string: ?postedWithin=1|3|7|14, ?companytypes=1|2, ?salary={int}, ?jobType=permanent|contract|temporary|partTime|workFromHome.
maxItemsintegernoMaximum job rows emitted per listing URL. 3 listings × maxItems: 100 → up to 300 total rows. Direct detail URLs always emit 1 row each. Each row = one paid dataset item. Default 1000. Free-tier users have a hidden global ceiling of 100 rows.
maxConcurrencyintegernoParallel HTTP requests for detail-page fetches. Sweet spot 3–5 via Apify Residential GB. Default 4.
maxRequestRetriesintegernoPer-URL retry budget on proxy CONNECT failures, HTTP/2 stream resets, and network errors. Each retry rotates the proxy session with mild exponential backoff. Default 6.
proxyobjectnoApify Residential GB required for the detail-page fetch (/job-ad/{id}). Default is wired correctly — don't override unless you know what you're doing.

Example input

{
"startUrls": [
"https://www.totaljobs.com/jobs/software-engineer/in-london",
"https://www.totaljobs.com/jobs/in-manchester"
],
"maxItems": 200,
"maxConcurrency": 4,
"proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "GB" }
}

Output schema

Every row has rowType: "job". 14 fields from JSON-LD + parsed salary band + structured location.

{
"rowType": "job",
"sourceSearchUrl": "https://www.totaljobs.com/jobs/in-london", // the search/listing page this row came from (renamed from listingUrl in v0.1)
"jobId": "107245246", // numeric — stable identifier
"jobUrl": "https://www.totaljobs.com/job/regional-optimization-lead/bp-energy-job107245246", // canonical detail page
"title": "Regional Optimization Lead",
// ── JobPosting JSON-LD ──
"description": "<p>Entity: Supply, Trading & Shipping…</p>", // HTML
"datePosted": "2026-05-07T02:51:32.477Z", // ISO 8601
"validThrough": "2026-06-18T02:51:32.477Z",
"employmentType": "FULL_TIME", // or null
"industry": "Management, Management-Area Management",
"directApply": true,
"jobLocationType": null, // "TELECOMMUTE" for remote
"applicantLocationRequirements": [], // populated when remote-friendly
// ── Employer (from JSON-LD hiringOrganization) ──
"employer": {
"name": "BP Energy",
"url": "https://www.totaljobs.com/jobs/bp-energy?cmpId=1428985&cmp=1",
"logoUrl": "https://www.totaljobs.com/CompanyLogos/2e66e5e85ad5408380e06078af3eb663.png"
},
// ── Location (from JSON-LD jobLocation.address + geo) ──
"location": {
"text": "St James, London, WC2N 5DU, GB",
"locality": "St James",
"region": "London",
"postalCode": "WC2N 5DU",
"country": "GB",
"lat": 51.50445,
"lng": -0.13601
},
// ── Salary (parsed from body text via regex — not in JSON-LD) ──
"salary": {
"rawText": "£70,967 to £83,926 per annum",
"min": 70967,
"max": 83926,
"currency": "GBP", // ISO code (GBP/USD/EUR)
"period": "annum" // "annum" / "hour" / "day" / "week" / "month"
},
// ── Apply flow ──
"applyUrl": "https://www.totaljobs.com/job/regional-optimization-lead/bp-energy-job107245246", // = jobUrl when directApply=true; null when directApply=false (external recruiter)
"applyType": "internal", // "internal" (apply on TotalJobs) / "external" (external recruiter) / "unknown"
"scrapedAt": "2026-05-15T06:25:31.012Z"
}

How it works

  1. Classify input — listings (/jobs/...) vs. details (/job/{slug}-job{id}). Listings auto-paginate; details scrape one row each.
  2. Fetch via Apify Residential GBimpit with Firefox TLS fingerprint. Direct + Evomi residential get 403 Akamai blocks; Apify GB returns clean 200 OK.
  3. For listings: collect detail-URL anchors per page, follow rel="next" until empty or cap. Then concurrent detail fetches via sliding window.
  4. For each detail: parse JobPosting JSON-LD for 14 fields. Run salary regex on body text. Emit one flat row.

Apply flow — what applyUrl and applyType mean

Every row carries two apply-flow fields derived from the JSON-LD directApply flag:

directApplyapplyTypeapplyUrlMeaning
true"internal"= jobUrlTotalJobs hosts a one-click apply form on the job page itself. Send the candidate to applyUrl (which is the same as jobUrl) and they can apply without leaving TotalJobs.
false"external"nullThe actual apply destination is an external recruiter ATS (e.g. Workday, Greenhouse). TotalJobs only resolves that URL after a click via async XHR. Open jobUrl in a browser and click Apply to be redirected.
missing"unknown"nullJSON-LD didn't surface the flag. Treat the same as "external" — open jobUrl to apply.

Why isn't applyUrl always the external URL? TotalJobs renders the apply button server-side as a disabled placeholder (<button aria-label="apply-button-placeholder" disabled>). The real external URL loads via XHR after click — we'd need a headless browser to capture it, which would 5–10× the per-row cost and trip Akamai. We tested 15 standard apply-API REST endpoints (e.g. /api/applicationredirect/{id}, /api/v1/listings/{id}/apply) — all 404. The data in __PRELOADED_STATE__.applyNowSection contains only listingId/listingGlobalId, not the external URL.

Practical rule of thumb: ≈ 60–70 % of TotalJobs listings are directApply: true (apply on TotalJobs), so for that majority you get a usable applyUrl directly. For the remainder, applyType="external" is the signal — open jobUrl in a browser to follow the recruiter redirect.

Notes & limitations

  • Apify Residential GB is mandatory. Direct connections from non-UK IPs get 403 Akamai blocks. Evomi residential (any country) also gets blocked. Apify Residential GB returns clean 200 OK on every probe we tested.
  • Listing discovery uses an efficient JSON pagination path — 50 items per page, with totalItems reported up-front. The paginator walks offset until either maxItems is reached or the response returns 0 items.
  • employmentType fill ≈ 70%. Not every JobPosting JSON-LD declares it. We don't synthesize when missing.
  • jobLocationType only set for remote/hybrid roles. Non-remote jobs leave it null — matches JSON-LD semantics.
  • Salary fill ≈ 100% when surfaced. When a job has a published salary it parses cleanly; when it doesn't (small fraction of jobs) the salary field is null.
  • /jobs/{company-slug}-jobs URLs work for company-specific listings (vs the /jobs/in-{location} keyword listings).

FAQ

Which TotalJobs URLs work? Two types: listing URLs (/jobs/in-london, /jobs/software-engineer/in-manchester, /jobs/{company-slug}-jobs) which auto-paginate and emit one row per linked job, and direct detail URLs (/job/{title-slug}/{org-slug}-job{id}) which scrape one row each. You can mix both in the same startUrls array.

Why do I need Apify Residential GB? TotalJobs sits behind Akamai Bot Manager with strict country rules. Direct connections, datacenter proxies, and non-GB residential (we tested Evomi IN/US/EU) all return 403 "Access Denied" from Akamai's edge. Apify Residential GB is the only pool we found that returns clean 200 OK on every probe.

What's the difference between sourceSearchUrl, jobUrl, and applyUrl? Three distinct things:

  • sourceSearchUrl — the search/listing page you pasted as input (e.g. /jobs/in-edinburgh?postedWithin=1). It's the breadcrumb back to "where this row came from in your batch", not a URL anyone clicks to view the job. Renamed from listingUrl in v0.1 because customers kept reading it as "apply URL".
  • jobUrl — the per-job detail page (e.g. /job/regional-optimization-lead/bp-energy-job107245246). Use this to view the full listing in a browser.
  • applyUrlwhere the candidate clicks to apply. Equal to jobUrl when directApply=true (TotalJobs hosts the form); null when directApply=false (external recruiter — TotalJobs resolves the URL only after JS-driven click). See the Apply flow table above.

When is applyUrl set vs null? Set to the jobUrl whenever directApply=true in JSON-LD (≈ 60–70 % of TotalJobs jobs — those with a one-click apply form on TotalJobs itself). null otherwise, with applyType="external" as the signal that the apply destination is on a recruiter ATS the browser-side click resolves dynamically. See the "Apply flow" section above for the full breakdown.

What does each dataset-item charge cover? One job row with all 14 JSON-LD fields plus the parsed salary band (min/max/currency/period) and structured location (lat/lng). maxItems is per listing URL, so a maxItems: 100 run with 2 listings = up to 200 charges. The Apify Store pricing event is apify-default-dataset-item — Apify auto-charges per row written to the default dataset.

Can the parsed salary handle annual / hourly / ranges? Yes. The regex catches £70,967 to £83,926 per annum, £50k - 70k per year, £15 per hour, and single values. The period field normalizes to annum/hour/day/week/month. When a job has no published salary, salary is null — we don't synthesize.

Why does one listing page sometimes return 7 jobs and another 10? Listing discovery uses a JSON-based pagination path — 50 items per offset step until you hit totalItems or your maxItems budget. Per-page variance in the old web-pagination sense is gone.

My run returned fewer rows than the TotalJobs page shows — why? Three possibilities, ranked by likelihood: (1) Akamai blocked a mid-stream page. As of v0.1 the pagination loop skip-ahead on the first failed page and only gives up after two consecutive blocked pages — you'll see a listing page=N blocked, skipping ahead to page=N+1 line in the log. If you see two such lines back-to-back, that listing's tail was unrecoverable on this run; just rerun and the proxy pool will give you a fresh session. (2) TotalJobs paginates beyond what's shown. The "1000+ jobs" badge at the top of a search is often the total filterable corpus, not the count for your current filter combo. Page-7+ on narrow filters (e.g. postedWithin=1 + companytypes=2) genuinely runs out of results. (3) maxItems cap. It's per-listing-URL — if you set maxItems: 100 and pasted one URL, that's the ceiling.

Support

  • Bugs / feature requests — open an issue on the GitHub repo
  • Custom exports / tailored fields — drop a note via the Apify Store contact form
  • Other actors — see my Apify Store profile for the rest of the catalog

⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by TotalJobs.com, StepStone Group, or any of their subsidiaries. All trademarks mentioned are the property of their respective owners.

The scraper extracts only publicly visible job postings rendered server-side by TotalJobs — no login, no CAPTCHA solving, no API-key forgery, no gateway.totaljobs.com (VPC-internal) probing. The actor honours robots.txt and rate-limits via concurrency cap (default 4) to avoid burdening TotalJobs's infrastructure.

Users are responsible for:

  • Complying with TotalJobs.com's Terms of Service
  • Following UK GDPR + your jurisdiction's data-protection laws when storing or processing scraped postings
  • Not contacting candidates listed by employers in scraped postings
  • Not republishing scraped data in a way that competes commercially with TotalJobs

SEO Keywords

totaljobs scraper, scrape totaljobs, totaljobs uk scraper, totaljobs.com scraper, totaljobs api, Apify totaljobs, uk jobs scraper, uk job board scraping, jobpostings api, jobposting json-ld scraper, uk recruitment api, recruitment scraper uk, uk salary data, salary band extraction, uk job market data, hiring intelligence uk, employer hiring data, b2b sales prospecting uk, london jobs scraping, manchester jobs scraper, edinburgh jobs scraper, akamai bot manager bypass, apify residential gb