# Prozorro Tenders Scraper (`yurij-data/prozorro-tenders-scraper`) Actor

Ukraine public procurement tenders from the official Prozorro API: filter by date, keywords, CPV codes, region, status and value. Buyer, contacts, deadlines, items, documents. Pay per tender.

- **URL**: https://apify.com/yurij-data/prozorro-tenders-scraper.md
- **Developed by:** [Yurij](https://apify.com/yurij-data) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 tenders

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

## Prozorro Tenders Scraper

Extract public procurement tenders from **Prozorro**, Ukraine's national e-procurement system, through its official open API. Filter by modification date, keywords, CPV (ДК 021:2015) codes, buyer region, status and budget, and get clean JSON, CSV or Excel with the buyer, expected value, deadlines, items and document links.

Ukraine publishes every public tender above the reporting threshold on Prozorro: roughly 1–2 million procedures a year across central government, municipalities, hospitals, schools, utilities and state companies. This Actor turns that feed into a filtered, structured dataset you can schedule, export or pipe into Google Sheets, a CRM or a Slack alert.

### What you get

One record per matching tender:

| Field | Example |
|---|---|
| `tenderID` | `UA-2026-11-09-001234-a` |
| `title` | Закупівля ноутбуків для закладів освіти |
| `status` | `active.tendering` |
| `procurementMethodType` | `belowThreshold`, `aboveThreshold`, `reporting`, … |
| `valueAmount`, `valueCurrency` | `1250000`, `UAH` |
| `buyerName`, `buyerEDRPOU`, `buyerRegion`, `buyerLocality` | buyer organisation, tax ID, region, city |
| `contactName`, `contactEmail`, `contactPhone` | the buyer's published contact point |
| `tenderStartDate`, `tenderEndDate`, `enquiryEnd` | key deadlines |
| `cpvCodes`, `cpvDescriptions` | all ДК 021 codes on the tender |
| `items[]` | description, CPV, quantity, unit, delivery region |
| `documents[]` (optional) | title, URL, format of each attached document |
| `url` | link to the tender on prozorro.gov.ua |

### Input

| Input | Meaning |
|---|---|
| **Modified since / until** | The feed is ordered by modification time; this bounds how much is scanned. Default: last 24 hours. |
| **Keywords** | Any keyword in the title, description or item descriptions keeps the tender (case-insensitive, Ukrainian or English). |
| **CPV prefixes** | e.g. `30` computers, `33` medical, `45` construction, `72` IT services, `09` fuel. |
| **Buyer regions** | Substring match on the buyer's region, e.g. `Київ`, `Львівська`. |
| **Statuses** | Default: `active.enquiries`, `active.tendering` (open for bids). |
| **Min / max value** | Expected value in UAH. |
| **Max tenders / max scanned** | Output cap and request cap. |
| **Include document links** | Adds the `documents[]` array. |

#### Example: open IT tenders in Kyiv above 500k UAH, last 3 days

```json
{
  "dateFrom": "2026-11-06",
  "cpvPrefixes": ["30", "48", "72"],
  "regions": ["Київ"],
  "statuses": ["active.enquiries", "active.tendering"],
  "minValue": 500000,
  "maxItems": 500
}
```

#### Example: daily alert for medical supplies nationwide

Schedule the Actor daily with `keywords: ["рукавички", "шприц", "маск"]` and `cpvPrefixes: ["33"]`, then connect an Apify integration (Google Sheets, Slack, email or webhook) to the dataset.

### How it works

1. Reads the public tender feed `/api/2.5/tenders` from the given date forward (100 tenders per page).
2. Fetches each tender's full record in parallel (default 8 concurrent requests, polite retries with backoff).
3. Applies your filters and pushes normalized records to the dataset.
4. Stops at `maxItems`, `maxScanned` or the end of the feed, and writes a `SUMMARY` record to the key-value store.

Only public data from the official API is used. No login, no scraping of the website UI, no bypassing of any protection.

### Pricing

Pay per event: $2.00 per 1,000 tenders saved to the dataset plus $0.01 per run; platform usage is included. You only pay for matching tenders, not for everything scanned, so a daily alert with a few dozen matches costs about 10 cents. The free Apify plan credit covers roughly 2,000 tenders a month.

### Tips

- Narrow **Modified since** first. A week of the full feed is tens of thousands of tenders; a keyword or CPV filter does not reduce scanning, only output.
- Statuses: use `active.enquiries` + `active.tendering` to see what you can still bid on; add `complete` and `active.awarded` for market analysis of who won what.
- CPV codes are hierarchical. `3321` matches `33210000-9` and everything under it.
- For historical analysis across months, raise `maxScanned` and run with a larger memory setting, or split the period into several scheduled runs.

### Related data

Looking for Polish, Romanian or EU-wide tenders (BZP, SEAP, TED), or for supplier/company registries? Check the other Actors by this author or contact me through Apify for a custom source.

### Changelog

- 0.1: initial release — feed walking, filters, normalized output, optional document links, pay-per-event.

# Actor input Schema

## `dateFrom` (type: `string`):

Only tenders modified on or after this date (YYYY-MM-DD). Default: 1 day before the run. The Prozorro feed is ordered by modification time, so this is the main way to limit how much is scanned.

## `dateTo` (type: `string`):

Optional upper bound (YYYY-MM-DD, inclusive).

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

Case-insensitive words or phrases. A tender matches if ANY keyword appears in its title or item descriptions. Ukrainian and English both work (titles are usually Ukrainian).

## `cpvPrefixes` (type: `array`):

Keep tenders whose items have a classification code starting with any of these prefixes, e.g. "30" (computers), "33" (medical), "45" (construction), "72" (IT services).

## `regions` (type: `array`):

Substring match on the buyer's address region, e.g. "Київ", "Львівська". Leave empty for all of Ukraine.

## `statuses` (type: `array`):

Prozorro statuses to keep. Common: active.enquiries, active.tendering, active.auction, active.qualification, active.awarded, complete, cancelled, unsuccessful.

## `procurementMethodTypes` (type: `array`):

Optional filter, e.g. aboveThreshold, belowThreshold, reporting, negotiation, priceQuotation. Leave empty for all.

## `minValue` (type: `integer`):

Skip tenders with an expected value below this amount.

## `maxValue` (type: `integer`):

Skip tenders with an expected value above this amount.

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

Stop after this many matching tenders have been saved.

## `maxScanned` (type: `integer`):

Safety limit on how many tender records are fetched from the feed (each one is an API request). Raise it for wide date ranges.

## `includeDocuments` (type: `boolean`):

Add the list of tender documents (title, URL, format) to each record.

## `concurrency` (type: `integer`):

Parallel requests to the Prozorro API. 8 is safe; higher values may get rate-limited.

## Actor input object example

```json
{
  "keywords": [
    "ноутбук",
    "сервер"
  ],
  "statuses": [
    "active.enquiries",
    "active.tendering"
  ],
  "maxItems": 200,
  "maxScanned": 5000,
  "includeDocuments": false,
  "concurrency": 8
}
```

# Actor output Schema

## `tenders` (type: `string`):

All matching tenders as JSON (add ?format=csv or ?format=xlsx for other formats).

## `summary` (type: `string`):

Counts of scanned and saved tenders and the date range used.

# 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": [
        "ноутбук",
        "сервер"
    ],
    "statuses": [
        "active.enquiries",
        "active.tendering"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yurij-data/prozorro-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": [
        "ноутбук",
        "сервер",
    ],
    "statuses": [
        "active.enquiries",
        "active.tendering",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("yurij-data/prozorro-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": [
    "ноутбук",
    "сервер"
  ],
  "statuses": [
    "active.enquiries",
    "active.tendering"
  ]
}' |
apify call yurij-data/prozorro-tenders-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yurij-data/prozorro-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/nnmNRnLSgd4oS6aWP/builds/0nroj18Xrhad1ekHx/openapi.json
