jobs.ac.uk Academic Jobs Scraper - UK University Jobs
Pricing
from $1.30 / 1,000 per vacancy returneds
jobs.ac.uk Academic Jobs Scraper - UK University Jobs
Scrape every live jobs.ac.uk vacancy - UK and overseas university, research and professional-services roles - with the employer's own ATS apply link, parsed salary, contact email, department and closing date. Filter by discipline, location, contract, salary band. New-vacancy monitor, no start fee.
Pricing
from $1.30 / 1,000 per vacancy returneds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
7 days ago
Last modified
Categories
Share
🎓 jobs.ac.uk Academic Jobs Scraper — every UK university vacancy, as structured rows
Turn jobs.ac.uk — the board UK higher education actually recruits on — into clean JSON / CSV / Excel rows: job title, institution, department, location, salary range, hours, contract type, closing date, the named contact email, and the one field that matters most to a recruiter or a supplier — the employer's own application URL, the deep link straight into their ATS.
No login. No API key. No CAPTCHA. Datacenter proxies are enough, and there is no per-run start fee — you pay for rows, and nothing else.
📊 What you are scraping (measured live 2026-09-04)
| Live vacancies on the board | 2,240 |
| New vacancies per weekday (counted off Date Placed) | ~135 |
| Fri 04 Sep · Thu 03 Sep · Wed 02 Sep · Tue 01 Sep | 141 · 142 · 130 · 131 |
| Mon 31 Aug (UK summer bank holiday) · Sun 30 Aug | 3 · 1 |
| United Kingdom | 1,984 |
| Overseas posts carried on the board | 256 (China 68, Ireland 41, Denmark 25, New Zealand 20, Hong Kong 18, Macao 17 …) |
| Hiring universities / HEIs | 2,106 of 2,240 adverts |
| Research organisations · public sector · commercial · FE · charity | 44 · 26 · 25 · 24 · 15 |
That is roughly 2,700 new adverts a month, which is what makes this a monitoring feed rather than a one-off download.
🚀 What does jobs.ac.uk Academic Jobs Scraper do?
- 🔎 Searches exactly the way the site does. Keywords plus all twelve of the site's own facets — academic discipline (21 values), sub-discipline (89), professional-services discipline (20), studentship type / qualification / funding, location (hierarchical: continent → country → region → city), workplace, salary band, hours, contract type and employer sector. Every one of them was verified against the live board to genuinely narrow the result set, not just relabel it.
- 🔗 Returns the employer's own apply URL.
applyUrlis the link out of jobs.ac.uk and into the institution's own recruitment system —jobs.bradford.ac.uk/HR0203329-2,manmetjobs.mmu.ac.uk/jobs/vacancy/9313/…. 100% fill on standard adverts across 60 measured pages, spread over 39 distinct ATS domains. That is the field a staffing agency, an HR-tech SDR or an academic-services supplier actually buys on. - 📧 Harvests the named contact.
contactEmailspicks up the hiring manager or department administrator the advert names (53% of adverts carry at least one).onlyWithContactEmailturns that into a hard lead-quality gate. - 💷 Parses the salary.
salaryMin/salaryMax/salaryCurrency/salaryPeriodfrom the advert's schema.org block — 85% of standard adverts publish a numeric range — plus the rawsalaryTextfor the 15% that say "Competitive" or attach a grade note. - 🛡️ Survives the site's random JavaScript interstitial. jobs.ac.uk answers a slice of requests with a ~2.8 KB packed-JS page at HTTP 200 instead of the real one. A naive scraper pushes short or empty pages and still reports SUCCESS. This actor classifies every response and re-requests on a brand-new proxy session. Measured on a 600-row crawl: 22 interstitials in 108 requests, 0 lost pages, 0 lost rows.
- 🧾 Never double-bills. Rows are de-duplicated on the site's own job ID across every search term, facet and page before anything is charged. Verified on a 600-row contiguous crawl: 600 rows, 600 unique job IDs, 600 unique advert IDs, 600 unique URLs, 0 duplicates.
- 📡 Monitors for free. Monitor mode remembers every job ID it has seen in a NAMED key-value store and returns only what is new, with webhook / Slack / email alerts. There is no monitor-run charge and no per-alert charge on this actor — you pay only for the new rows.
- 🧰 Filters before it bills. Title and employer allow/deny lists, salary floor and ceiling, posted-within / posted-after / closing-before windows, UK-only or overseas-only, contact-email required. A row you filter out is never charged.
- 📋 Paste any jobs.ac.uk search URL. Build the search in the browser, paste the address bar, and every parameter is honoured verbatim — including facet values this schema does not list.
📦 Output — measured fill rates
Search-row fields, measured on a 600-row contiguous crawl of the whole live board (2026-09-04), 600 of 600 unique:
| Field | Fill |
|---|---|
jobId (the site's own advert code, the dedupe key) | 100% |
advertId (numeric row id, 1:1 with jobId) | 100% |
url · title · employer | 100% |
location · salaryText | 100% |
datePlaced ("04 Sep") · datePosted (ISO) | 100% |
closingDate ("18 Sep") · validThrough (ISO) | 100% |
highlighted (paid-promotion flag — 73 of 600 rows) | 100% |
department | 82% |
Advert-page fields (turn on Scrape job details), measured on 60 standard advert pages from the same crawl:
| Field | Fill |
|---|---|
applyUrl (deep link into the employer's own ATS) · applyDomain | 100% |
datePosted · validThrough (ISO 8601) | 100% |
employmentType · hours · placedOn · closes | 100% |
city · country · employer · employerLogo | 100% |
descriptionText / descriptionHtml | 100% |
contractType | 98% |
region | 95% |
department | 85% |
salaryMin · salaryMax · salaryCurrency · salaryPeriod | 85% |
employerWebsite (the institution's own domain) | 78% |
employerJobRef (the institution's internal ATS reference) | 77% |
contactEmails / contactEmail | 53% |
advertTable (the raw label→value table, incl. studentship rows: Qualification Type, Funding for, Funding amount) | 100% |
Plus listingType, detailFetched, isNew, firstSeenAt, searchLabel, scrapedAt, and the
untouched schema.org rawJsonLd object on request.
Sample row
{"jobId": "DSV280","advertId": "1086981","url": "https://www.jobs.ac.uk/job/DSV280/early-years-practitioner","title": "Early Years Practitioner","employer": "University of Bradford","department": "Professional Services - Directorate of People and Culture","location": "Bradford","city": "Bradford", "region": "England", "country": "United Kingdom","salaryText": "£25,354 per annum till 19/04/2027","salaryMin": 25354, "salaryMax": 25354,"salaryCurrency": "GBP", "salaryPeriod": "YEAR","hours": "Full Time","contractType": "Fixed-Term/Contract","employmentType": "Full Time,Fixed-Term/Contract","datePlaced": "04 Sep", "datePosted": "2026-09-04","closingDate": "18 Sep", "validThrough": "2026-09-18","employerJobRef": "HR0203329-2","employerWebsite": "https://www.bradford.ac.uk/external/","applyUrl": "https://jobs.bradford.ac.uk/HR0203329-2","applyDomain": "jobs.bradford.ac.uk","listingType": "standard"}
💷 Pricing — pay per event, no start fee
| Event | Price | When it fires |
|---|---|---|
Per vacancy returned (job-scraped) | $0.0013 | Once per unique vacancy delivered to your dataset. |
Per advert page enriched (job-detail-enriched) | $0.0015 | Only when Scrape job details is on, and only for adverts that actually returned structured data. |
| Actor start | $0.00 | Removed. Every rival in this lane charges one. |
- The whole live board (2,240 vacancies) costs about $2.91, or $6.27 with full advert-page enrichment.
- A daily new-vacancy monitor bills about $0.18/day (~135 new adverts) — $5.30 a month — because monitoring, alerting and de-duplication are free.
- Rows dropped by your own filters, duplicate rows, and the ~4% of adverts that are employer campaign microsites (no structured data) are never charged the enrichment fee.
⚙️ How this actor reads the site (and the traps it is built around)
Everything below was measured against real bytes on 2026-09-04, not read off documentation.
- The list surface is server-rendered HTML.
GET /search/?…&sortOrder=1&pageSize=25&startIndex=N. No JSON endpoint exists. Rows live atdiv.j-search-result__result; the corpus size atstrong.job-count. pageSizeis fixed at 25.pageSize=100returns a byte-identical 25-row page. Page counts therefore always move in steps of 25.- Paging is complete — there is no depth ceiling.
startIndex=2101→ 25 rows,startIndex=2226→ 15,startIndex=2251→ 0. The whole 2,240-row corpus is reachable. - An empty page past the end is NORMAL, not a failure. It still carries the job count — which is exactly how this actor tells "end of results" apart from the interstitial.
- The random interstitial. A slice of requests answers HTTP 200 with a packed-JS page
(
eval(function(p,a,c,k,e,d)…) instead of the real one. It is session-scoped and random, not a per-URL wall, and it is not an anti-abuse control being defeated: the actor simply discards the response and asks again on a fresh proxy session, and the site serves normally. Measured rates: a light crawl was 100% clean over 37 requests; a heavy 600-row crawl saw 22 interstitials in 108 requests and recovered all of them. Leave Max retries at 4 or more. - Salary and dates are read structurally, never by a page-wide regex. "Date Placed" and the salary string each appear 25 times on a results page; a whole-page regex grabs the wrong one.
- The free-text
location=/distance=parameters do not work.location=London&distance=20returned the full unfiltered 2,240. OnlylocationFacet[]filters. This actor never sends the free-text pair. - Advert pages are fetched with zero redirects — measured across 110 detail fetches — so the
crawler never lands on the robots-Disallowed
/enhanced/fp/path. - Three advert page classes. Standard (48 of 50 sampled) carries the schema.org
JobPostingblock and the advert table. Enhanced (2 of 50, ~4%) is an employer campaign microsite with no structured block and no standard version of the same advert — the actor still salvages its title, apply link, emails and ad copy, marks itlistingType: "enhanced", and never charges the enrichment fee for it. Challenge is the interstitial, and is retried. - The description comes from the structured block, not the page node. The on-page
#job-descriptioncontainer also holds the apply-button markup. - If the site's markup changes, the run fails loudly and charges nothing. A results page that
reports a job count but yields zero parsed rows triggers
Actor.fail()— a silently empty dataset is the one failure mode a "green" run can otherwise hide.
❓ FAQ
Does this need a login, an API key or a CAPTCHA solver? No. Both surfaces are public server-rendered pages. Nothing is logged into, no token is minted, no anti-abuse control is bypassed.
Are Apify datacenter proxies enough? Yes — that is the default, and it is what every measurement in this README was taken on. The residential pool with country GB is the documented fallback if you ever see persistent failures.
Why do I sometimes see "interstitial retried on a fresh session" in the log? That is the actor doing its job. jobs.ac.uk randomly serves a JavaScript holding page at HTTP 200; the actor detects it and re-requests. You never see a short page or a missing row.
Can I filter by date? Yes, but on this side of the wire — jobs.ac.uk exposes no date query parameter at all. Use Posted within (days), Posted after/before and Closing after/before, and keep the default "Date placed" sort so the newest adverts come first. Rows filtered out are never billed.
How do I get a daily feed of new vacancies? Turn on Monitor mode, give the run a Monitor state store name of its own, and schedule it. Every run returns only job IDs it has not seen before. Monitoring is not billed.
What is the difference between salaryText and salaryMin/salaryMax?
salaryText is exactly what the advert says ("£38,784 to £46,049 Grade 7, per annum") and is 100%
filled. The numeric pair comes from the advert's structured block and is filled on 85% of standard
adverts — the rest genuinely do not publish a number.
Why is department only 82% filled?
Because 18% of adverts do not state one. It is not a parsing gap: the field is simply absent from
the search row and from the structured block on those adverts.
Does it cover PhD and Masters studentships?
Yes. Use Studentship type (PhDs 98 live, Masters 9), Qualification type and Studentship
funding. Studentship adverts also carry qualificationType, fundingFor and fundingAmount.
Does it cover jobs outside the UK? Yes — 256 of the 2,240 live adverts were overseas posts on 2026-09-04. Filter with the Location facet (server-side, cheapest) or Country (needs advert-page scraping).
Will overlapping searches charge me twice? No. De-duplication happens on the job ID before billing, across every search term, facet and page. Choose Advert ID or Employer+title instead if you prefer.
What happens if I mistype a facet value? The run stops with a clear error, returns zero rows and charges nothing. jobs.ac.uk answers an unknown facet value with zero results, and running the search unfiltered instead would hand you (and bill you for) the whole board.
What happens on a search that legitimately matches nothing? The run finishes SUCCEEDED with an empty dataset and a status message saying why. It never fails for having found nothing.
Can I limit what I spend? Yes, three ways: Max vacancies to return (a hard row cap, default 50), Max advert pages to fetch, and Apify's own per-run maximum charge — rows are pushed and billed atomically, so hitting the cap can never leave you paying for rows you did not receive.
⚖️ Legal and fair use
https://www.jobs.ac.uk/robots.txt (read 2026-09-04) is a single open group:
User-agent: *Disallow: /job/feedback/Disallow: /enhanced/fp/Sitemap: https://www.jobs.ac.uk/sitemapindex.xml
This actor requests only /search/ and /job/<ID>/<slug> — both explicitly crawlable — and the
site publishes sitemapindex.xml. Advert pages are fetched with redirects measured at zero, so the
crawler never lands on /enhanced/fp/. There is no login, no CAPTCHA, no signature forgery and no
anti-abuse control involved anywhere in this actor.
Personal data is your responsibility. contactEmails are the addresses of named individuals
published in a job advert. Using them — in particular for direct marketing — makes you the data
controller under the UK GDPR and PECR. Have a lawful basis, honour opt-outs, and do not process
them for a purpose the advert did not contemplate. This actor gives you a switch
(Extract contact emails) to leave them out entirely.
You are responsible for complying with jobs.ac.uk's terms of use and for how you use the data. Vacancy text is the copyright of the advertising institution; scrape the facts, not their prose, if you intend to republish.