AcademicPositions Job Scraper — EU Academic Jobs avatar

AcademicPositions Job Scraper — EU Academic Jobs

Pricing

from $6.50 / 1,000 job results

Go to Apify Store
AcademicPositions Job Scraper — EU Academic Jobs

AcademicPositions Job Scraper — EU Academic Jobs

Scrape postdoc jobs, PhD positions & faculty jobs from academicpositions.com (EU, UK & Switzerland). A real browser via residential proxy passes Cloudflare. Returns title, institution, location, field, salary, posted date & apply URL — with delta mode for new-only scheduled runs.

Pricing

from $6.50 / 1,000 job results

Rating

0.0

(0)

Developer

Nomad Dev

Nomad Dev

Maintained by Community

Actor stats

0

Bookmarked

11

Total users

2

Monthly active users

2 days ago

Last modified

Categories

Share

Fetch postdoc, PhD, faculty and early-career-researcher openings from academicpositions.com, covering institutions across the EU, UK and Switzerland.

Claude / Codex skill to describe and setup this actor: SKILL.md

How it works

academicpositions.com sits behind Cloudflare's bot protection, so this Actor drives a real headless browser (Playwright/Chromium) through an Apify Residential proxy to load pages the same way a person's browser would. Each posting is parsed from the page's structured JobPosting data (schema.org JSON-LD), with a plain-HTML fallback for the rare page that omits it — so results stay accurate even as the site's front-end layout changes.

What data does this Actor extract?

Published postings follow nomad-agent-job-row-v1. The shared fields carry source identity, title and company, parsed locations, dates, the complete plain-text body when available, source markup when usable, and work type. Unavailable scalar facts are null; no parsed location is []. Source-only facts appear in versioned custom.data only when mapped there. See the Output example section for the exact dataset fields and diagnostic rows.

Fields come back as null (never an empty string) on the rare posting where the source doesn't provide that piece of data.

Every run also appends one uncharged control row after all job rows and writes the identical object to the default key-value store as RUN-SUMMARY. The row has recordType: "RUN-SUMMARY", schema nomad-agent-source-run-summary-v1, a status of complete, partial_rate_limited, empty_source, or listing_blocked, and reconciled listing/detail/proxy/job counters. Consumers should branch on recordType and must not parse the summary row as a job. A partial run can still contain valid complete job rows before its summary.

How to scrape academicpositions.com with this Actor

  1. Click Try for free / Run — the default input needs no changes.
  2. Optionally set a keyword, countryFilter or postedWithin to narrow results, and adjust maxItems.
  3. Run it and export the dataset as JSON, CSV or Excel, or read it over the API.

Run it from your own code:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("nomad-agent/academicpositions-scraper").call(
run_input={"maxItems": 30, "keyword": "machine learning postdoc"})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["title"], "—", item["company"], item["url"])

Or a single HTTP call that runs the Actor and returns items in one response:

curl -X POST \
"https://api.apify.com/v2/acts/nomad-agent~academicpositions-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"maxItems": 30}'

Input

FieldTypeDefaultNotes
keywordstring(empty)Free-text query appended to the listing URL (e.g. "postdoc", "computer science"). Leave empty to scrape the default listing.
countryFilterstring(empty)Case-insensitive substring match on the extracted location field (e.g. "Germany"). Applied client-side, after extraction. Leave empty to keep every location.
postedWithinstringanyKeep only jobs published inside this window. Use any for no date filter, or a duration — 1h, 24h, 3d, 2w, 6m. Jobs the source published no date for are kept rather than dropped. A date such as 2026-01-01 is still accepted for an absolute cutoff. Replaces postedSince, still accepted for existing integrations.
maxItemsinteger30Maximum number of postings to return. Each returned posting is billed as one dataset result — capped at 500 per run.
proxyConfigurationobjectApify Residential proxyProxy used to load the site. Keep the Residential group selected — academicpositions.com blocks non-residential IPs.
startUrl (Advanced)stringhttps://academicpositions.com/find-jobsThe listing page the Actor starts from and harvests job links from. Change only to start from a specific saved-search URL instead of the default listing.
detailUrlContains (Advanced)string/ad/Only links whose URL contains this text are treated as job-detail pages. Change only if the site's URL structure changes and the Actor stops finding postings.
cacheTtlSeconds (Advanced)integer900Persistent listing-card cache duration, bounded to 10–30 minutes. Used only when the fresh listing is classified blocked; the summary remains listing_blocked. Complete detail records are cached for 24 hours and can only serve URLs present in the current fresh/fallback listing. Set 0 to disable both caches.
useResidentialProxy (Advanced, deprecated)booleantrueSuperseded by proxyConfiguration above. Still honored when proxyConfiguration is left unset, for older integrations.

Output example

Every row follows nomad-agent-job-row-v1, the one shape shared by all of this fleet's job Actors. A row carries every field; null means the source did not publish that fact, and locations: [] means no usable location was parsed from the posting. Nothing is guessed.

{
"schemaVersion": "nomad-agent-job-row-v1",
"recordType": "posting",
"source": "academicpositions",
"id": "a1b2c3",
"url": "https://example.com/academicpositions/jobs/a1b2c3",
"title": "Senior Backend Engineer",
"company": "Example Company",
"locations": [
"Bilbao",
"Spain"
],
"postedAt": "2026-09-02T00:00:00Z",
"deadline": null,
"description": "The complete posting body as plain text, exactly as the source published it — never truncated.",
"descriptionHtml": "<p>The complete posting body as the source's own markup.</p>",
"workType": null,
"custom": {
"schemaId": "nomad-agent-job-custom-academicpositions-v1",
"data": {
"field": "…"
}
}
}
FieldMeaning
schemaVersionAlways "nomad-agent-job-row-v1".
recordType"posting" for a job, "diagnostic" for a row reporting something about the run itself.
sourceWhich job source the posting came from, from the collector registry's vocabulary — not the Actor name.
idStable identifier for the posting within source.
urlCanonical public URL of the posting on the source site.
titleJob title exactly as the source publishes it, untruncated.
companyEmployer name as published.
locationsPlaces the role is based, most specific first — e.g. ["Bilbao", "Spain"].
postedAtWhen the source published the posting, ISO-8601 UTC (YYYY-MM-DDTHH:MM:SSZ).
deadlineClosing date for applications as an ISO-8601 calendar date (YYYY-MM-DD).
descriptionThe complete posting body as plain text — never truncated, never summarised.
descriptionHtmlThe posting body as the source's own markup, preserving lists, headings and links.
workTypeWorking arrangement: "remote", "hybrid" or "onsite".
customFacts only this source publishes, as {"schemaId", "data"}.

A run also emits diagnostic rows — recordType: "diagnostic" with a warnings array — when it has something to report, such as a source returning nothing. They are never billed and are easy to filter out on recordType.

Pricing

Pay per event: $0.02 per Actor start and $0.008 per posting returned ($8 / 1,000 postings, less on higher Apify plans — down to $6.50 / 1,000). 100 postings ≈ $0.82. No subscription, no rental — you pay only for what you fetch. The price covers the real headed browser + residential proxy each run needs to pass Cloudflare.

This Actor also routes every request through Apify's Residential proxy group (required to get past academicpositions.com's bot protection), which is billed separately by Apify against your account's residential proxy GB allowance in addition to the per-event charges above.

Integrations

Export results as JSON, CSV or Excel, pull them via the Apify API or run-sync-get-dataset-items, wire this Actor into Make, Zapier or n8n, or call it from AI agents through the Apify MCP server.

Use cases

  • Academic job boards and alert bots for postdocs, PhD candidates and faculty
  • University career-services aggregation
  • Research on hiring trends across EU/UK/CH institutions
  • Building a searchable database of research openings

FAQ

Is it legal to scrape academicpositions.com? This Actor reads only publicly available job postings — data any visitor can see without logging in. Review the target site's terms and your local regulations for your specific use case.

Do I need an account on academicpositions.com? No. Postings are fetched from public pages — no login, cookies or session tokens required.

Why does this Actor need a residential proxy? academicpositions.com fronts its pages with Cloudflare Bot Fight Mode, which blocks datacenter IPs outright. A residential exit IP plus a real browser is what reliably gets through the challenge.

How fresh is the data? Every run fetches live listings directly from the site — there's no cache.

Something broken or missing? Open an issue on the Actor's Issues tab — it is monitored and reliability fixes ship fast.


From the maker of Oink — an open-source, AI-powered job-search bot for Telegram that runs on these Actors. Try the free bot, get a managed instance at oinkjobsearch.com, or browse the full catalog of 50+ Actors.