# AcademicTransfer Scraper — Dutch PhD, Postdoc & University Jobs (`yugenox/academictransfer-jobs-scraper`) Actor

Scrape AcademicTransfer, the Netherlands' academic job board: PhD, postdoc, lecturer, professor and university staff vacancies with salary scale, hours, contract, education level, research fields, deadline, employer and the full ad. Complete results and only-new mode. No login.

- **URL**: https://apify.com/yugenox/academictransfer-jobs-scraper.md
- **Developed by:** [Yugenox Corp](https://apify.com/yugenox) (community)
- **Categories:** Jobs, Automation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 vacancies

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## AcademicTransfer Scraper: Dutch PhD, postdoc and university jobs

Scrape **AcademicTransfer**, the job board of the Dutch universities, university medical centres and research institutes. Get PhD positions, postdocs, lecturer and professor jobs, and university staff vacancies with the **salary scale, hours per week, contract type and duration, required education level, research fields, deadline, employer, department and the complete ad** (description, requirements, conditions of employment, about the employer). Search by keyword and city, filter by job type, field, education level and contract, get **all** matches with **Complete results**, and run it daily with **Only new vacancies**. No login, no cookies, no account.

### What you get

| | |
|---|---|
| **Vacancy** | Title, AcademicTransfer URL and number, the employer's own vacancy number, published date, application deadline, number of positions, ad language |
| **Employer** | University / institute name, type (university, UMC, research centre, university of applied sciences …), department, website, AcademicTransfer profile, logo |
| **Place** | City, street address, postal code, country |
| **Job** | Job types (PhD, postdoc, assistant professor, lecturer, R\&D, IT, policy …), academic or not, scientific fields, research fields (400+ specific fields such as "Computer science - Programming") |
| **Terms** | Salary scale (min / max gross per month, EUR), hours per week, permanent or temporary, contract duration, required education level |
| **The ad** | Summary, description, requirements, conditions of employment, about the employer and department, additional information — as text and as one HTML document; keywords |
| **Apply** | The employer's application URL |

### How to use it

1. Enter one or more **keywords**, e.g. `postdoc`, `machine learning`, `assistant professor`, `promovendus`, or leave them empty to get all vacancies.
2. Optionally add a **city** (`Amsterdam`, `Delft`, `Utrecht` …) and filters: **Job types**, **Fields**, **Education level**, **Contract**, **Minimum salary**, **Ad language**, **Employers**.
3. Set **Max vacancies** and run.

#### The newest 10 or everything

AcademicTransfer's jobs page shows the **newest 10 matches** of a search; that's what a normal run returns, in seconds. Turn on **Complete results** to get **all** matches: the run then reads every open vacancy from the site's vacancy list (about 700) and keeps the ones that match. AcademicTransfer asks crawlers to wait 10 seconds between requests and this scraper respects that, so checking the whole site takes about 2 hours. With **Only new vacancies** on, later runs only read vacancies they haven't seen yet, so a daily scheduled run takes a few minutes.

#### Input examples

The 10 newest postdoc vacancies:

```json
{
  "keywords": ["postdoc"],
  "maxItems": 10
}
```

All PhD positions in engineering, in English, every day only the new ones (schedule it daily):

```json
{
  "jobTypes": ["PHD"],
  "scientificFields": ["ENGINEERING"],
  "adLanguages": ["en"],
  "completeResults": true,
  "onlyNew": true,
  "maxItems": 0
}
```

Assistant / associate professor jobs in Amsterdam and Utrecht, permanent contracts only:

```json
{
  "jobTypes": ["PROFESSOR_LECTURER"],
  "locations": ["Amsterdam", "Utrecht"],
  "contractType": ["PERMANENT"],
  "maxItems": 50
}
```

Every open vacancy on AcademicTransfer:

```json
{
  "completeResults": true,
  "maxItems": 0
}
```

You can also paste AcademicTransfer URLs into **AcademicTransfer URLs**: vacancy URLs (`academictransfer.com/en/jobs/360110/…`) or search URLs (`academictransfer.com/en/jobs/?q=…`, the filters in the URL are kept).

### Output example

```json
{
  "site": "academictransfer",
  "jobId": "364404",
  "title": "PhD position on materials development for electrically-driven water treatment processes",
  "organisation": "University of Groningen",
  "department": "Faculty of Science and Engineering",
  "organisationType": "University",
  "organisationUrl": "http://www.rug.nl/",
  "city": "Groningen",
  "address": "Broerstraat 5",
  "postalCode": "9712 CP",
  "countryCode": "NL",
  "jobTypes": ["PhD"],
  "researchFields": ["Engineering", "Engineering - Materials engineering", "Engineering - Chemical engineering", "Environmental science", "Environmental science - Water science"],
  "educationLevel": "University graduate",
  "contractType": "Temporary",
  "salaryMin": 3204,
  "salaryMax": 4051,
  "salaryCurrency": "EUR",
  "salaryPeriod": "month",
  "hoursMin": 38,
  "hoursMax": 38,
  "availablePositions": 1,
  "publishedAt": "2026-10-01T13:30:21.239Z",
  "deadline": "2026-10-29T23:00:00.000Z",
  "language": "en",
  "description": "In the Engineering and Technology Institute Groningen (ENTEG), we are looking for a talented and motivated PhD student …",
  "requirements": "• A Master’s degree (or an equivalent university degree) in Chemical Engineering, Materials Science, Chemistry, or related …",
  "contractTerms": "What can you expect from us?\n\n• 232 vacation hours per year, based on a 38-hour workweek …",
  "additionalInfo": "Questions about the content of the job?\n[name removed] ([name removed]): [email removed] …",
  "keywords": ["PhD position", "materials development", "electrochemical water purification", "water treatment"],
  "vacancyCode": "V26.2486-EN",
  "applyUrl": "https://werkenbij.rug.nl/vacature/phd-position-on-materials-development-for-electrically-driven-water-treatment-processes-2486/?lang=en",
  "url": "https://www.academictransfer.com/en/jobs/364404/phd-position-on-materials-development-for-electrically-driven-water-treatment-processes/",
  "isAcademic": true,
  "searchQuery": "all vacancies",
  "scrapedAt": "2026-10-01T22:35:45.824Z"
}
```

Every row has the same columns; a value the ad doesn't state is `null`.

### Use cases

- **PhD and postdoc job alerts**: a daily run with **Only new vacancies** for your field.
- **Academic job boards and newsletters**: feed every Dutch academic vacancy into your own site.
- **Research and HR analytics**: salary scales, contract types and hiring volume by university, field and job type.
- **Recruitment and career services**: find which institutes are hiring in your discipline.

### FAQ

**Do I need an AcademicTransfer account, cookies or a login?** No. The scraper reads the same public pages any visitor sees. It never logs in, never uses the "My AcademicTransfer" features and never applies to anything.

**Why do I get only 10 vacancies?** AcademicTransfer's jobs page shows the newest 10 matches of a search. When there are more, the status message says so. Turn on **Complete results** to get all of them.

**Why is Complete results slow?** AcademicTransfer asks crawlers to wait 10 seconds between requests (robots.txt), and this scraper respects that. Reading every open vacancy takes about 2 hours; a run with **Only new vacancies** only reads what's new since the last run. The default run timeout is 2.5 hours; if a run is cut short you keep everything saved so far.

**What does a run cost?** You pay per vacancy saved, a small fee per search, and a small fee per run. With **Complete results**, every vacancy page that was read but didn't match your search is charged as "checked" (it costs a 10-second request); vacancies skipped by **Only new vacancies** are never read or charged. See the Pricing tab.

**Are the salaries monthly?** Yes. AcademicTransfer shows the gross monthly salary scale (for a full-time position) in euros; `salaryPeriod` says `month`.

**Which proxy should I use?** Keep the default (Apify Proxy, datacenter). It comes with every Apify plan, including the free one. If AcademicTransfer refuses datacenter IPs for a while, the run switches to residential proxies when your account has them.

**Is it legal to scrape AcademicTransfer?** This Actor only collects publicly available data: job vacancies that anyone can see on AcademicTransfer without an account, such as the title, employer, location, salary scale and the ad itself. Collecting publicly available data is generally legal, but you're responsible for how you use it. Vacancies describe roles, not people, but an ad often names a contact person; the scraper removes personal e-mail addresses and phone numbers, and the contact person's name next to them, from the ad. You must follow privacy laws such as GDPR, PIPEDA and CCPA, as well as AcademicTransfer's terms. If you're unsure, check with a lawyer. More on this: [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

**Does it access any private data?** No. Everything comes from pages AcademicTransfer shows to any visitor without logging in. It never touches accounts, saved searches or applications, and it follows the site's robots.txt, including its 10-second crawl delay.

*This Actor is not affiliated with, endorsed by or sponsored by AcademicTransfer.*

# Actor input Schema

## `keywords` (type: `array`):

Words to search for, e.g. "postdoc", "machine learning", "assistant professor", "promovendus". Each keyword is a separate search. Leave empty to get all vacancies (optionally narrowed by the filters below).

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

Optional: a Dutch city, e.g. "Amsterdam", "Delft", "Utrecht". Added to the search the same way AcademicTransfer's own city filter does.

## `locations` (type: `array`):

Run the same keywords in several cities.

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

Maximum number of vacancies for the whole run, shared fairly between your searches. 0 = everything.

## `completeResults` (type: `boolean`):

AcademicTransfer's jobs page shows the newest 10 matches of a search. Turn this on to get ALL matches: the run then reads every open vacancy page from the site's vacancy list (about 700) and keeps the ones matching your search. Slow by design — AcademicTransfer asks crawlers to wait 10 seconds between requests, so checking the whole site takes about 2 hours. Pages that don't match are charged as "checked" (see Pricing). Combine with "Only new vacancies" for cheap daily runs.

## `onlyNew` (type: `boolean`):

Skip vacancies that an earlier run with the same input already saved (they are not fetched and not charged). Ideal for a daily or weekly schedule.

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

Optional: vacancy URLs (academictransfer.com/en/jobs/360110/…) or jobs-search URLs (academictransfer.com/en/jobs/?q=…, filters in the URL are kept).

## `vacancyType` (type: `string`):

AcademicTransfer's own split: academic jobs (PhD, postdoc, lecturer/professor, research, education) or non-academic jobs (support, IT, policy, medical …).

## `jobTypes` (type: `array`):

AcademicTransfer's job type filter. Empty = all.

## `scientificFields` (type: `array`):

Scientific field. Empty = all.

## `educationLevels` (type: `array`):

Required education level. Empty = all.

## `contractType` (type: `array`):

Permanent and/or temporary (fixed-term). Empty = both.

## `minSalary` (type: `integer`):

Only vacancies whose salary scale reaches at least this gross monthly amount. Vacancies without a salary are left out when this is set.

## `adLanguages` (type: `array`):

Only ads written in these languages. Empty = all.

## `organisations` (type: `array`):

Only vacancies of these employers, e.g. "TU Delft", "Utrecht University", "Radboudumc".

## `postedWithinDays` (type: `integer`):

Only vacancies published in the last N days.

## `language` (type: `string`):

Language of the labels (job type, field, education level, contract). The ads themselves stay in the language they were written in.

## `proxyConfiguration` (type: `object`):

Apify Proxy (datacenter) is the default and comes with every Apify plan. If AcademicTransfer refuses datacenter IPs for a while, the run switches to residential proxies when your account has them.

## Actor input object example

```json
{
  "keywords": [
    "postdoc"
  ],
  "maxItems": 10,
  "completeResults": false,
  "onlyNew": false,
  "vacancyType": "all",
  "language": "en",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped vacancies.

## `run` (type: `string`):

Status and statistics for this run.

## `report` (type: `string`):

RUN_REPORT: how the run ended, counts, requests and proxy use.

# 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 = {
    "keywords": [
        "postdoc"
    ],
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yugenox/academictransfer-jobs-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 = {
    "keywords": ["postdoc"],
    "maxItems": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

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

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

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

```

## CLI example

```bash
echo '{
  "keywords": [
    "postdoc"
  ],
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call yugenox/academictransfer-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yugenox/academictransfer-jobs-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ndQYpj6GVrtMv85tH/builds/UauXsBHraYWG7rsO1/openapi.json
