# Universitat de Barcelona Jobs: PhD, Postdoc & Research (`nomad-agent/ub-doctoral-scraper`) Actor

Scrape currently-open vacancies from Universitat de Barcelona's official vacancy board — PhD/predoctoral contracts by default, or postdoc, research, teaching and staff jobs via keyword. No API key needed. Incremental new-only runs for scheduled alerts.

- **URL**: https://apify.com/nomad-agent/ub-doctoral-scraper.md
- **Developed by:** [Nomad Dev](https://apify.com/nomad-agent) (community)
- **Categories:** Jobs
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.10 / 1,000 job results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Universitat de Barcelona Jobs Scraper — PhD, Postdoc & Research

> **Claude / Codex skill to describe and setup this actor: [SKILL.md](https://github.com/Exdenta/OinkAIJobSearch/blob/main/skill/ub-doctoral-scraper/SKILL.md)**

Scrapes **currently-open vacancies** from Universitat de Barcelona's official public-vacancy board (the UB *seu electrònica* → *Ofertes de feina*) — the same board UB itself publishes to.

**No API key. No proxy. No AI credits.** Just run it.

By default it returns open **PhD / predoctoral research contracts**. Change one field and it returns postdoc, research, teaching, technical or administrative vacancies instead — or all of them at once.

### What you get

One flat JSON record per vacancy:

| Field | Meaning |
|---|---|
| `id` | UB's own numeric vacancy id — stable across runs |
| `title` | Full convocatòria title (UB states the department, funding project and contract type inline) |
| `company` | Always `"Universitat de Barcelona"` |
| `location` | Always `"Barcelona, Spain"` |
| `url` | Link to the official vacancy page, where the bases and application form are attached |
| `postedAt` | Publication date (`YYYY-MM-DD`), or `null` if UB didn't state one |
| `deadline` | Explicit application deadline from an attached PDF (`YYYY-MM-DD`), or `null` |
| `deadlineEvidence` | PDF URL, title and exact labelled deadline excerpt, or `null` |
| `snippet` | Short description excerpt |
| `source` | Always `"ub_doctoral"` |

**Deadline policy:** UB publishes application deadlines only *inside the PDF bases* attached to each vacancy — never on the listing or detail HTML. The Actor accepts a date only when PDF text explicitly labels it as the application deadline and returns that excerpt in `deadlineEvidence`. Otherwise `deadline` remains `null`; publication and modification dates are never substituted.

Every run also emits one uncharged `RUN-SUMMARY` row using schema `nomad-agent-source-run-summary-v1`. Its status distinguishes a genuinely empty source from complete, partial, or failed document enrichment, and its counters reconcile listings, detail attempts, attachment failures, and complete jobs.

### Input

| Field | Default | What it does |
|---|---|---|
| `keyword` | `predoctoral` | Full-text search against UB's own board. Try `postdoctoral`, `investigador`, `Física`, a project name… **Set it to an empty string to get every open UB vacancy.** |
| `maxItems` | `12` | Cap on vacancies returned (`0` = no limit). Each one is billed as one result. |
| `postedSince` | — | Only vacancies published on or after this date. |

Only currently-**open** vacancies are ever returned — closed and in-progress calls are filtered out by UB server-side, so you never pay for a result you can't apply to.

### Run it from your own code

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

run = client.actor("nomad-agent/ub-doctoral-scraper").call(run_input={
    "keyword": "predoctoral",   # "" for every open UB vacancy
    "maxItems": 50,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["postedAt"], item["title"], item["url"])
```

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

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/nomad-agent~ub-doctoral-scraper/run-sync-get-dataset-items?token=%3CYOUR_APIFY_TOKEN%3E" \
  -H "Content-Type: application/json" \
  -d '{"keyword": "predoctoral", "maxItems": 50}'
```

Export the dataset as JSON, CSV, Excel or XML, or read it over the [Apify API](https://docs.apify.com/api/v2).

### Pricing

Pay per event: a small actor-start fee plus a per-vacancy result fee. Your total is the start fee plus the number of vacancies returned.

### Notes & limits

- Covers Universitat de Barcelona only. For EU-wide academic vacancies, see our [EURAXESS](https://apify.com/nomad-agent/euraxess-enrich-translate-normalize-scraper) and [jobs.ac.uk](https://apify.com/nomad-agent/jobs-ac-uk-scraper) scrapers.
- UB publishes vacancy titles mostly in Catalan (some Spanish/English). `keyword` matches whatever UB actually wrote, so Catalan terms (`predoctoral`, `investigador`, `professorat`) match best.
- If a run can't reach UB or a populated listing collapses during PDF enrichment, it still finishes as a **succeeded** run with a `failed_enrichment` RUN-SUMMARY that explains the loss. A real zero-listing result is reported as `empty_source`.

Found a bug, or want another Spanish university covered? Open an issue on the Actor — and if it works for you, a review helps a lot.

***

**From the maker of [Oink](https://github.com/Exdenta/OinkAIJobSearch)** — an open-source, AI-powered job-search bot for Telegram that runs on these Actors. [Try the free bot](https://t.me/job_search_everyday_bot), get a managed instance at [oinkjobsearch.com](https://oinkjobsearch.com), or browse the [full catalog of 50+ Actors](https://apify.com/nomad-agent).

# Actor input Schema

## `keyword` (type: `string`):

Full-text search run against UB's own vacancy board. Defaults to <code>predoctoral</code> (open PhD / predoctoral research contracts). Set it to <code>postdoctoral</code>, <code>investigador</code>, a department name, or any other term to retarget the search. <b>Set it to an empty string to return every currently-open UB vacancy</b> (research, teaching, technical and administrative).

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

Maximum number of vacancies to return (0 = no limit). Each returned vacancy is billed as one Apify result.

## `postedSince` (type: `string`):

Only return vacancies published on or after this date. Vacancies whose publication date can't be read are still included, not silently dropped. Leave empty to disable the filter.

## Actor input object example

```json
{
  "keyword": "predoctoral",
  "maxItems": 12
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("nomad-agent/ub-doctoral-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("nomad-agent/ub-doctoral-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 '{}' |
apify call nomad-agent/ub-doctoral-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nomad-agent/ub-doctoral-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/ugSECzLDDlEw716Or/builds/3yVLjRCCCtEyg5ATl/openapi.json
