# UNGM UN Tenders Scraper | UN Procurement Notices (`automatic/ungm-tenders-scraper`) Actor

Search UNGM, the UN Global Marketplace, for procurement notices by keyword, agency, country or notice type. Each row has agency, reference, beneficiary country, publication date, deadline in its own GMT offset and links. Add description, contacts and UNSPSC codes. Export JSON, CSV or Excel.

- **URL**: https://apify.com/automatic/ungm-tenders-scraper.md
- **Developed by:** [Philip Kirkbride](https://apify.com/automatic) (community)
- **Categories:** Lead generation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 notices

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

## UNGM UN Tenders Scraper

Find procurement notices published on UNGM, the United Nations Global Marketplace: invitations to bid, requests for quotation, requests for proposal, calls for individual consultants, expressions of interest and related notice types from UNDP, UNICEF, WFP, UNOPS, UNHCR, WHO and the rest of the UN system.

Each row is one notice with its title, UN agency, reference number, beneficiary country, notice type, publication date and the bid deadline in the notice's own GMT offset. Turn on details to add the full notice text, the contact details the agency published, the published links and the UNSPSC codes UNGM assigned.

Built for companies and consultants selling to the UN, bid teams, and analysts tracking UN procurement. Filter by keywords, agency, country, notice type and date range, schedule a daily run, and send new notices to a spreadsheet, e-mail or Slack. Turn on **Only new or changed since the last run** and the Actor removes the duplicates for you.

**Price: $0.50 per 1,000 notices** ($0.0005 per notice) plus a $0.005 run start. Fifty notices cost about $0.030. Searches that match nothing cost only the start.

### Use cases

- **Bid teams:** one row per open notice with the agency, reference, beneficiary country and the deadline in its own GMT offset, so you can triage what closes first.
- **Tender-alert services:** a scheduled daily run by agency, country or notice type, with `onlyNewSinceLastRun` so each alert contains only new notices and changes.
- **Aggregators and marketplaces:** a UN-wide feed of invitations to bid, requests for quotation and calls for consultants, each row linking to the official UNGM notice.
- **Consultants and NGOs:** monitor calls for individual consultants and implementing partners without checking each agency's site.

### Quick start

1. Press **Start**. The form is already filled with this input, so the first run returns the 50 active notices that close first in under a minute:

```json
{"maxItems": 50}
```

2. Add your keywords, agencies, countries or notice types, then run again or save the input as a task.
3. Download the rows as JSON, CSV or Excel, or schedule the task for a daily feed.

### Example queries

UNICEF and UNDP quotations closing in the next two weeks, with full details:

```json
{
  "agencies": ["UNICEF", "UNDP"],
  "noticeTypes": ["RequestForQuotation"],
  "deadlineTo": "2026-11-18",
  "includeDetails": true,
  "maxItems": 100
}
```

Solar equipment anywhere in East Africa, published this month:

```json
{
  "keywords": "solar",
  "countries": ["Kenya", "Uganda", "Tanzania, United Republic of"],
  "publishedFrom": "2026-11-01"
}
```

### Input

| Field | Type | Default | What it does |
|---|---|---:|---|
| `keywords` | string | `""` | Words that must appear in the notice title. |
| `descriptionKeywords` | string | `""` | Words that must appear in the notice description. |
| `reference` | string | `""` | The buyer's reference number, e.g. `UNDP-SLE-00337`. |
| `agencies` | array | `[]` | UN agencies by name or abbreviation, e.g. `UNDP`, `UNICEF`, `WFP`, `UN Women`. |
| `countries` | array | `[]` | Beneficiary countries by name, e.g. `Kenya`, `Somalia`, `Viet Nam`. Common short names such as `USA`, `UK` or `South Korea` also work. |
| `noticeTypes` | array | `[]` | Notice types, e.g. `InvitationToBid`, `RequestForQuotation`, `IndividualConsultant`. Labels such as "Invitation to bid" are accepted too. |
| `publishedFrom` | date | none | Only notices published on or after this date (`YYYY-MM-DD`). |
| `publishedTo` | date | none | Only notices published on or before this date. |
| `deadlineFrom` | date | none | Only notices whose deadline falls on or after this date. |
| `deadlineTo` | date | none | Only notices whose deadline falls on or before this date. |
| `activeOnly` | boolean | `true` | On: only notices that are currently open. Off: include closed notices and older historical records. |
| `maxItems` | integer | `50` | Most notices to return, up to 5,000. Results are sorted by deadline, so the closest deadlines come first. |
| `includeDetails` | boolean | `false` | Add the description, published contacts, links and UNSPSC codes. Runs take longer with this on. |

All filters are optional and can be combined. An empty input returns the 50 active notices that close first.

### Use it as a daily feed

Turn on **Only new or changed since the last run** (`onlyNewSinceLastRun`) and run the
Actor on a schedule with the same input. It then returns only notices this search has not
delivered before, or whose watched fields changed since the last run, so there is nothing
to deduplicate on your side.

- Every returned row carries `changeType` (`new` or `changed`) and `changedFields`.
- The first run has no saved history, so it returns everything and reports
  `feed.firstRun` in the run's OUTPUT.
- The history lives in the named key-value store `ungm-tenders-scraper-feed-state`, one record per
  search. Leave `stateKey` empty to name the record from the input, so different searches
  keep separate histories; set a name to share one or to start a fresh feed.
- An entry leaves the history once its deadline is more than 90 days past and it has not been delivered in the last 90 days, so the history stays bounded.
- The watched fields are the title, the deadline, the status and the published links.

With the flag off (the default) the Actor returns every match, exactly as before.

### What it returns

Every row has `noticeId`, `url`, `title`, `reference`, `agency`, `noticeType`, `country`, `status`, `publishedOn`, `deadlineAt`, `deadlineText`, `deadlineDate`, `deadlineTime`, `deadlineOffset`, `registrationLevel`, `description`, `contacts`, `links`, `unspscCodes`, `unspscNames`, `detailsFetched`, `detailsError`, `sourceQuery`, `source` and `scrapedAt`.

- `deadlineAt` is ISO 8601 with the notice's own GMT offset, e.g. `2026-11-04T15:59:00-04:00`, so deadlines can be sorted across countries. `deadlineText` keeps the deadline exactly as UNGM shows it, and `deadlineTime` / `deadlineOffset` are its clock time and offset.
- `publishedOn` is a date (`YYYY-MM-DD`): UNGM publishes the day, not the time.
- `status` is `active` when the run kept "active notices only" on, and empty when closed notices were included.
- `country` is the beneficiary field exactly as UNGM publishes it. Some notices list several countries in that line; others say "Multiple destinations".
- `description`, `contacts`, `links`, `unspscCodes`, `unspscNames` and `registrationLevel` are filled only when `includeDetails` is on.
- `contacts` keeps the agency contact rows as published, with the source's own labels such as "Email address", "First name", "Surname" and "Telephone number".
- `links` lists the published Links tab: e-procurement pages, supplier registration and document pages.

### Output example

```json
{
  "noticeId": "319219",
  "url": "https://www.ungm.org/Public/Notice/319219",
  "title": "Biomedical Engineer for Laboratory Quality Assessment and Equipment Repair",
  "reference": "UNDP-SLE-00337",
  "agency": "UNDP",
  "noticeType": "Call for individual consultants",
  "country": "Sierra Leone",
  "status": "active",
  "publishedOn": "2026-10-21",
  "deadlineAt": "2026-11-04T15:59:00-04:00",
  "deadlineText": "04-Nov-2026 15:59 (GMT -4.00)",
  "deadlineDate": "2026-11-04",
  "deadlineTime": "15:59",
  "deadlineOffset": "-04:00",
  "registrationLevel": "Basic",
  "description": "Country: Sierra Leone\nDescription of the Assignment: National Consultant - Consultancy for Biomedical Engineer ...",
  "contacts": [
    {"label": "Email address", "value": "vendors.sle@undp.org"},
    {"label": "Surname", "value": "Procurement Unit"}
  ],
  "links": [
    {"url": "https://procurement-notices.undp.org/view_negotiation.cfm?nego_id=50948",
     "label": "UNDP Procurement Notices Ad"}
  ],
  "unspscCodes": ["80111500"],
  "unspscNames": ["Human resource development"],
  "detailsFetched": true,
  "detailsError": null,
  "sourceQuery": "activeOnly=true",
  "source": "ungm.org",
  "scrapedAt": "2026-11-04T19:30:00+00:00"
}
```

### Empty fields and why

- `description`, `contacts`, `links`, `unspscCodes`, `unspscNames` and `registrationLevel` are empty (`null`) unless `includeDetails` is on. With details on they are empty only when UNGM publishes nothing there.
- Bid attachments are not public: UNGM asks suppliers to sign in and express interest before it shows the tender documents. `links` therefore holds the public pages the notice quotes, such as the agency's e-submission or registration page.
- Lots are not a separate field on the UNGM notice page. Where a notice describes lots, the text stays in `description` as published.
- `reference` and `country` are empty when UNGM leaves them blank, which happens on some older notices.
- A notice page that cannot be read keeps its row and says why in `detailsError`.

### Limits

- Up to 5,000 notices per run. The search is paged; broad searches over closed notices can be very large, so narrow them by agency, country or date.
- Long runs: the default time limit is 30 minutes, enough for the full 5,000 notices with details off, or about 400 with details on. A run that reaches its time limit stops cleanly and keeps the notices saved so far; raise the timeout in the run options before asking for more than a few hundred notices with details.
- Runs deliver the notices closest to their deadline first. Use `deadlineFrom` and `deadlineTo` to focus on a bidding window.
- With details on, each notice needs extra page reads, so runs are slower: UNGM limits how fast notice pages can be read, so a long detail run settles at about four seconds per notice instead of a fraction of a second. The run waits whenever UNGM asks it to slow down and carries on.
- Results can be downloaded from the dataset in JSON, CSV or Excel. The run summary shows the delivered count and any unavailable notice pages.

### Published information

Everything UNGM shows publicly is returned as published. That includes the buyer's contact rows, names, e-mail addresses and telephone numbers, and the full notice text. Nothing is masked, shortened or rewritten.

### FAQ

**How do I get only new tenders each day?** Turn on `onlyNewSinceLastRun`, keep the same input and schedule it. The Actor keeps a history per search and returns only notices it has not delivered before, or whose deadline, status, title or published links changed. The first run returns everything and marks `feed.firstRun` in the run's OUTPUT. You can still use `publishedFrom` and deduplicate on `noticeId`; each run is then independent, so overlapping windows deliver the same notices again.

**What does a daily feed run cost?** The start event ($0.005 at 512 MB) plus $0.0005 for each notice it actually delivers. A day with no new notices costs $0.005. The 50-notice quick-start run costs about $0.030.

**Where are the bidding documents?** Open `url` and use the notice's own links. UNGM releases the attachments to signed-in suppliers who express interest.

**What happens when nothing matches?** The run succeeds with an empty dataset and a plain status message such as "No active UNGM notices match these filters".

**Does it include closed notices?** Only when `activeOnly` is turned off, and then also older historical records.

**Why is `country` sometimes a list or a phrase?** The field keeps UNGM's beneficiary line as published; some notices name several countries or say "Multiple destinations".

### Price

You pay per event through Apify's pay-per-event billing:

| Event | Price | When |
|---|---:|---|
| Run start | $0.005 per started GB of memory (minimum one event) | Once per run, even when nothing matches |
| Notice | $0.0005 | Once per notice row stored in the dataset |

Worked costs:

- The 50-notice quick-start run: $0.005 start + 50 × $0.0005 = **$0.030**.
- 1,000 notices: $0.005 + 1,000 × $0.0005 = **$0.505**.
- A daily feed run with no new notices: **$0.005**.

With `includeDetails` on, the same per-notice price applies; reading the notice page and its UNSPSC codes does not add an event. See the Pricing tab for the prices that apply to your plan. The run-start event is charged per GB of memory; the default 512 MB run is one start event. The start is charged even if the run saves no results.

### Support

For questions or issues, contact us from the Actor page.

# Actor input Schema

## `keywords` (type: `string`):

Words that must appear in the notice title, in any language. UNGM's own title search. Leave empty for all titles.

## `descriptionKeywords` (type: `string`):

Words that must appear in the notice description. Use this to search the full text, or combine it with title keywords to narrow both.

## `reference` (type: `string`):

The buyer's reference number, for example UNDP-SLE-00337. Leave empty to ignore it.

## `agencies` (type: `array`):

Agency names or abbreviations, for example UNDP, UNICEF, WFP, UNOPS or UNHCR. Leave empty for all agencies.

## `countries` (type: `array`):

Countries where the goods or services are needed, for example Kenya, Somalia or Viet Nam. Common short names such as USA, UK or South Korea are accepted. Leave empty for all countries.

## `noticeTypes` (type: `array`):

Procurement notice types, for example Invitation to bid or Request for quotation. Leave empty for all types.

## `publishedFrom` (type: `string`):

Only notices published on or after this date (YYYY-MM-DD).

## `publishedTo` (type: `string`):

Only notices published on or before this date (YYYY-MM-DD).

## `deadlineFrom` (type: `string`):

Only notices whose bid deadline falls on or after this date (YYYY-MM-DD).

## `deadlineTo` (type: `string`):

Only notices whose bid deadline falls on or before this date (YYYY-MM-DD).

## `activeOnly` (type: `boolean`):

On: only notices that are currently open. Off: also include notices that have closed, including older historical records.

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

Most notices to return. The search is sorted by deadline, so the notices closest to closing come first.

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

Reads each notice's page and adds the description as published, the published contact rows, the published links and the UNSPSC codes. Runs take longer with this on.

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

Return only tenders this search has not delivered before, or whose title, deadline, status, value or documents changed since the last run. Keep the same input and schedule to get a clean daily feed.

## `stateKey` (type: `string`):

Optional name for the saved feed state. Leave empty to derive it from the input, so different searches keep separate histories. Set a new name to start the feed over.

## Actor input object example

```json
{
  "keywords": "",
  "descriptionKeywords": "",
  "reference": "",
  "agencies": [],
  "countries": [],
  "noticeTypes": [],
  "activeOnly": true,
  "maxItems": 50,
  "includeDetails": false,
  "onlyNewSinceLastRun": false,
  "stateKey": ""
}
```

# Actor output Schema

## `results` (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 = {
    "keywords": "",
    "descriptionKeywords": "",
    "reference": "",
    "activeOnly": true,
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("automatic/ungm-tenders-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": "",
    "descriptionKeywords": "",
    "reference": "",
    "activeOnly": True,
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("automatic/ungm-tenders-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": "",
  "descriptionKeywords": "",
  "reference": "",
  "activeOnly": true,
  "maxItems": 50
}' |
apify call automatic/ungm-tenders-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automatic/ungm-tenders-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/Z2ZNO9iFs01JPAgOz/builds/53ME9hrk8IcMTQfWi/openapi.json
