# UN Jobs Scraper — unjobs.org UN & NGO Vacancies (`nomad-agent/unjobs-scraper`) Actor

Scrape UN and NGO vacancies from unjobs.org — 143 agencies (UNICEF, WFP, UNDP, UNHCR…) in one run. Get title, organisation, duty station, closing date, full description and apply URL. Delta mode returns only new postings, ideal for job-alert bots. $5/1,000 results, no API key needed.

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

## Pricing

from $3.50 / 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/platform/actors/running/actors-in-store#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

## UN Jobs Scraper — unjobs.org UN & NGO Vacancies

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

Fetch current vacancies from **unjobs.org** — the classic UN jobs aggregator covering UN agencies, funds and programmes plus adjacent international organisations, updated continuously throughout the day.

No API key, no login. A **proxy is required** — unjobs.org blocks Apify's own IPs, so the Actor routes every request through the proxy you select (see [Proxy](#proxy)). **$5 per 1,000 results** (plus a $0.005 start fee), with volume discounts down to $3.50/1,000.

### What unjobs.org data does this scraper extract?

Each result is one flat JSON record per vacancy:

| Field | Meaning |
|---|---|
| `id` | Stable source-side identifier (unjobs.org posting id) |
| `title` | Job title as listed (unjobs.org appends the duty station: "Role, City, Country") |
| `company` | Hiring organisation line (e.g. "UNICEF - United Nations Children's Fund") |
| `location` | Duty station, when the title carries a real one (see below) |
| `url` | Direct link to the vacancy on unjobs.org |
| `updatedAt` | ISO timestamp of when unjobs.org last updated the posting |
| `snippet` | Organisation — location, joined as a short summary line |
| `source` | Always `"unjobs"` |

With **Include full details** (`includeDetails`) on, each record also carries the fields the listing page simply does not have:

| Field | Meaning |
|---|---|
| `closingDate` | **Application deadline** (ISO 8601) |
| `description` | **Full job description** text, up to 5,000 characters |
| `country` / `city` | Structured location as the organisation itself published it |
| `office` | Hiring office, e.g. "UNICEF in Amman" |

This costs one extra event per vacancy enriched, and it also *corrects* `location`: unjobs.org publishes no location field on the listing, so it can only be read off the end of the job title — and a title like *"Chair, Department of Biological and Biomedical Sciences"* has a comma but no location. Listing-only records leave `location` empty rather than fill it with a fragment of the title; detail mode replaces the guess with the real country and city.

### Choosing a feed

**Organisation** and **Duty station** override **Feed** when set.

| Feed | What you get | Use it for |
|---|---|---|
| `latest` *(default)* | The 25 newest postings site-wide | Job alerts, monitoring — a new vacancy lands here first |
| `closing` | ~300 vacancies whose deadline is near | Deadline-driven search |
| `allOrganizations` | Every one of the ~143 organisation feeds, deduped | A full snapshot of what unjobs.org currently lists |
| `organization` | One organisation, e.g. `unicef`, `wfp`, `undp` ([browse](https://unjobs.org/organizations)) | Tracking one agency |
| `dutyStation` | One duty station, e.g. `gva` for Geneva ([browse](https://unjobs.org/duty_stations)) | Tracking one location |

Note that `latest` and `closing` are **disjoint** — a posting published today with a deadline two months out is not in the closing-soon feed. If you are watching for new vacancies, use `latest` (the default) rather than `closing`.

Add a **Keyword filter** (`keywordFilter`) on top of any feed to keep only postings whose title or organisation matches; filtered-out postings are never billed.

### Built for job-alert bots: delta mode

Turn on **Only new since last run** (`onlyNewSinceLastRun`) and every scheduled run returns only postings it has never delivered before — already-seen jobs are dropped before they're pushed or billed, so a monitoring schedule pays only for genuinely new openings. Seen ids persist in the Actor's key-value store across runs; **Reset delta state** (`resetSeenJobs`) starts over. No other UN/NGO-sector scraper on Apify offers an incremental mode.

Pair it with the `latest` feed and a schedule (hourly keeps up comfortably: unjobs.org posts roughly 10 vacancies an hour) and you have a complete new-vacancy stream that bills you only for jobs you have never seen.

### How to scrape unjobs.org with this Actor

1. Click **Try for free** / **Run** — no login to the target site, no cookies. The **Proxy configuration** input defaults to Apify Proxy in automatic (datacenter) mode; see [Proxy](#proxy) before your first run.
2. Adjust the input (organisation, duty station, keyword, `maxItems`) or keep the defaults.
3. Run it and export the dataset as JSON, CSV or Excel, or read it over the [API](https://docs.apify.com/api/v2).

Run it from your own code:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("nomad-agent/unjobs-scraper").call(run_input={"organization": "unicef"})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["url"])
```

### Proxy

unjobs.org sits behind Cloudflare and answers Apify's un-proxied platform IPs with **HTTP 403 on every listing page**, so this Actor needs a proxy for every request — it is not an optimisation you can switch off. **Proxy configuration** in the input controls it:

- **Apify Proxy, automatic mode** (the default) — datacenter exits, available on every Apify plan, no residential bandwidth to pay for. It worked when last measured (2026-08-01: 35/35 HTTP 200 across the latest, closing-soon, organisation and duty-station feeds). Cloudflare rules change, so if you start seeing 403s, switch to one of the two below.
- **Apify Residential** — the fallback when datacenter exits get refused. Needs a paid Apify plan, and its bandwidth is billed on top of this Actor's own price (see [Pricing](#pricing)).
- **Your own proxy URLs** — supported through the same field; the Actor uses exactly what you set, with no automatic fallback.

If every request is blocked, the run reports SUCCEEDED with one uncharged diagnostic row explaining why, rather than failing.

### Pricing

Pay per event — you are billed for results, not for runtime:

| Event | Price |
|---|---|
| Run start | $0.005 |
| Each vacancy returned | $0.005 ($5 / 1,000; volume tiers down to $3.50 / 1,000) |
| Each vacancy enriched with full details | extra, and only when `includeDetails` is on |

The default proxy (Apify Proxy, automatic/datacenter mode) adds no bandwidth charge. If you switch to the Residential group, Apify bills residential proxy bandwidth (GB) separately per your plan's proxy rates.

Delta mode and the keyword filter both reduce what you pay — postings dropped by either are never pushed and never billed, and a vacancy page that fails to load is never charged the detail event.

A full `allOrganizations` snapshot is around 900 vacancies, so it bills roughly $4.50. Every run also carries a **maximum total charge** (Apify sets a default one from your plan). If a snapshot would exceed it, the Actor delivers everything that fits inside your budget and stops cleanly — it never overruns the cap. Raise the run's max total charge to take the rest.

### Related actors — the full UN / NGO jobs stack

unjobs.org is one lens on the impact-sector job market. These siblings cover the rest, all with the same flat record shape and delta mode:

- [Impactpool Jobs Scraper](https://apify.com/nomad-agent/impactpool-scraper) — Impactpool.org: UN agencies, development banks, INGOs, EU institutions.
- [UN Careers Scraper](https://apify.com/nomad-agent/un-careers-scraper) — careers.un.org: the UN Secretariat's own vacancy board.
- [ReliefWeb Jobs Scraper](https://apify.com/nomad-agent/reliefweb-scraper) — reliefweb.int: humanitarian and emergency-response roles.
- [Devex Jobs Scraper](https://apify.com/nomad-agent/devex-jobs-scraper) — devex.com: international-development jobs and consultancies.
- [Researcher Jobs Bundle](https://apify.com/nomad-agent/researcher-bundle) — one run across all of the above plus academic sources.

### Is it legal to scrape unjobs.org?

This Actor reads only publicly listed vacancy summaries (title, organisation, location, link) — no personal data, no login-protected content. Review the target site's terms and your local regulations before large-scale use.

***

**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

## `feed` (type: `string`):

Which listing to scrape. <b>Latest</b> = the newest postings site-wide — the right feed for job alerts and delta mode, because a brand-new vacancy appears here first. <b>Closing soon</b> = vacancies whose deadline is near (a posting with a distant deadline is <i>not</i> in this feed). <b>All organisations</b> = every one of the ~143 organisation feeds, deduped into one full snapshot — the widest coverage unjobs.org allows. Ignored when <b>Organisation</b> or <b>Duty station</b> is set.

## `includeDetails` (type: `boolean`):

Open each vacancy page to add the fields the listing does not carry: <b>closing date</b>, the <b>full job description</b>, and the organisation's own structured <b>country/city</b> (more accurate than the location inferred from the title). Slower, and billed as an extra event per vacancy enriched — a vacancy page that fails to load costs nothing.

## `organization` (type: `string`):

Scrape one organisation's vacancies. Use the slug from its unjobs.org URL, e.g. <code>unicef</code>, <code>wfp</code>, <code>undp</code> (see <a href='https://unjobs.org/organizations'>unjobs.org/organizations</a>). Takes precedence over the duty-station feed and over <b>Feed</b>. Leave empty to use the selected feed.

## `dutyStation` (type: `string`):

Scrape one duty station's vacancies. Use the station code from its unjobs.org URL, e.g. <code>gva</code> for Geneva (see <a href='https://unjobs.org/duty_stations'>unjobs.org/duty\_stations</a>). Ignored when <b>Organisation</b> is set.

## `keywordFilter` (type: `string`):

Only keep postings whose title or organisation contains this text (case-insensitive). Applied after fetching; filtered-out postings are never billed.

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

Maximum number of vacancies to return; you're billed per result returned. The <b>Latest</b> feed serves 25, <b>Closing soon</b> ~300, and <b>All organisations</b> a few thousand — raise this to take the full snapshot. Set 0 to keep everything the feed returns. Values outside 0–5000 are clamped, never rejected.

## `onlyNewSinceLastRun` (type: `boolean`):

Return only postings not already delivered by a previous run of this Actor. Ideal for scheduled monitoring / job-alert bots: already-seen jobs are dropped before they're pushed or billed, so you pay only for genuinely new openings. Seen job ids are remembered in the Actor's key-value store across runs.

## `resetSeenJobs` (type: `boolean`):

Clear the remembered set of already-seen jobs before this run, so delta mode starts from a clean slate (every current posting counts as new). Only meaningful together with <b>Only new since last run</b>.

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

Proxy used for every request to unjobs.org. A proxy is <b>required</b>: unjobs.org answers Apify's own un-proxied IPs with a Cloudflare 403 on every listing page. The default is Apify Proxy in <b>automatic</b> mode (datacenter — available on every plan, no residential bandwidth to pay for), which answered 35/35 HTTP 200 when last measured (2026-08-01, re-checked 9/10 the same day). Cloudflare rules change, so if you start seeing 403s, select the Apify <b>Residential</b> group (paid Apify plans only — Apify bills residential bandwidth separately per your plan's proxy rates) or your own third-party proxy URLs. Whatever you pick here is what the Actor uses — there is no automatic fallback.

## `cacheTtlSeconds` (type: `integer`):

Reuse the last fetched page for this many seconds instead of contacting unjobs.org again, so quick repeat runs are faster and cheaper. Set 0 (or any negative value) to always fetch a fresh page.

## Actor input object example

```json
{
  "feed": "latest",
  "includeDetails": false,
  "organization": "unicef",
  "dutyStation": "gva",
  "keywordFilter": "health",
  "maxItems": 40,
  "onlyNewSinceLastRun": false,
  "resetSeenJobs": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "cacheTtlSeconds": 1800
}
```

# 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/unjobs-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/unjobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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/unjobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=nomad-agent/unjobs-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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