# Ukraine ProZorro Tenders — Buyers, Suppliers & Values (`foxlabs/ukraine-prozorro-tenders`) Actor

Search Ukraine's ProZorro public procurement — tenders, buyers, awarded suppliers, values, items (CPV) and status from the official OpenProcurement API. Filter by keyword, category, value and date; ideal for reconstruction-contract monitoring. Open data, no key.

- **URL**: https://apify.com/foxlabs/ukraine-prozorro-tenders.md
- **Developed by:** [Berkan Kaplan](https://apify.com/foxlabs) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.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/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

<p align="center"><a href="https://apify.com/foxlabs/ukraine-prozorro-tenders"><img src="https://data.foxlabs.com.tr/img/ukraine-prozorro-tenders-banner.svg" alt="Ukraine ProZorro Tenders" width="100%" /></a></p>

## Ukraine ProZorro Tenders — Buyers, Suppliers & Values

Track **Ukraine's public procurement** — every tender, who's buying, who's winning, for how much, and **who to call about it**. This Actor reads Ukraine's official **ProZorro / OpenProcurement** system (the country's transparent e-procurement platform) and returns clean, structured tender records: title, status, category, the **buying entity with its procurement officer's name, e-mail and phone**, the **awarded supplier(s)**, values, items with **CPV codes** and dates. Ideal for **reconstruction-contract monitoring**, supplier intelligence and B2G market research.

Built on the **official OpenProcurement API** (OCDS open-contracting standard) — **open data, no API key, no login**. Filter by keyword, category, region, value and date; results stream newest-first.

- ☎️ **A contact on almost every tender** — the buying entity's officer name, e-mail and phone, **filled on 92% of records** (measured on a 400-tender run; narrower windows have run as high as 98%). Don't just watch tenders — reach the person running them.
- 🇺🇦 **All Ukrainian public tenders** — from the official ProZorro CDB
- 🏗️ **Reconstruction-ready** — filter to `works` (construction) tenders by region and value
- 🤝 **Buyers *and* suppliers** — the procuring entity and the awarded winner (with EDRPOU codes and award amounts)
- 🏷️ **CPV-coded items** — and the keyword search reads item descriptions and CPV codes, not just titles
- 🆓 **Open data (OCDS)** — reuse and redistribute, no key

### Quick start (API)

Recent construction (`works`) tenders over ₴1M in the Kyiv region:

```bash
curl -X POST "https://api.apify.com/v2/acts/foxlabs~ukraine-prozorro-tenders/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "category": "works", "region": "Київ", "minValue": 1000000, "datePreset": "last_30_days", "maxResults": 200 }'
```

Prefer no code? Open the **Input** tab, set your filters, and click **Start** — then download the results.

### What you get

One clean record per tender:

| Field | Type | Description |
|---|---|---|
| `tenderID` | string | Public ProZorro tender ID (e.g. `UA-2026-06-19-012201-a`) |
| `title` | string | Tender title (Ukrainian) |
| `status` | string | `active.*` (open) / `complete` / `cancelled` / `unsuccessful` |
| `procurementMethod` | string | e.g. `aboveThreshold`, `belowThreshold`, `reporting` |
| `category` | string | `goods` / `works` / `services` |
| `buyerName` / `buyerId` | string | Procuring entity + its EDRPOU code |
| **`buyerContactName`** | string | **The procurement officer named on the tender** |
| **`buyerContactEmail`** | string | **Their e-mail** — 92% filled |
| **`buyerContactPhone`** | string | **Their phone** — 92% filled |
| `buyerKind` | string | Entity type: `general`, `special`, `defense`, `social`, `authority` |
| `buyerRegion` / `buyerLocality` | string | Buyer location |
| `buyerAddress` / `buyerPostalCode` | string | Street address and postcode |
| `valueAmount` / `valueCurrency` | number / string | Expected value (usually UAH) |
| `vatIncluded` | bool | Whether the value includes VAT |
| `awardCriteria` | string | How the winner is picked (e.g. `lowestCost`) |
| `cpvMain` | string | Primary CPV code — the fastest way to filter a market |
| `items` | array | `{ description, cpv, quantity, unit }` per lot |
| `awards` | array | Awarded **suppliers** — `{ supplier, supplierId, awardValue, awardCurrency, awardStatus, awardDate }` |
| `bidCount` | number | Bids received — `null` while ProZorro still withholds them (see Data quality) |
| `lotCount` / `documentCount` / `contractCount` | number | Lots, attached documents, signed contracts |
| `dateCreated` / `noticePublishedAt` | string | Created / officially published |
| `enquiryPeriodEnd` | string | **Deadline for asking the buyer questions** |
| `tenderPeriodStart` / `tenderPeriodEnd` | string | **Bidding opens / closes** |
| `dateModified` | string | Last change |
| `tenderUrl` / `source` | string | Public ProZorro page + provenance |

Missing values come back as `null`, never a guess — and never a misleading `0`.

#### Sample output

Real record — an elevator capital-repair tender in Kyiv (items & award abbreviated; the contact block is shown with a placeholder e-mail rather than reprinting a named official's address here — live runs return the real one from the register):

```json
{
  "tenderID": "UA-2026-06-19-012201-a",
  "title": "Капітальний ремонт/заміна ліфта … в Оболонському районі м. Києва (CPV 45453000-7)",
  "status": "active.qualification",
  "procurementMethod": "aboveThreshold",
  "category": "works",
  "buyerName": "УПРАВЛІННЯ ЖИТЛОВО-КОМУНАЛЬНОГО ГОСПОДАРСТВА ОБОЛОНСЬКОЇ РАЙОННОЇ … АДМІНІСТРАЦІЇ",
  "buyerId": "37445395",
  "buyerKind": "general",
  "buyerContactName": "Філатова Анастасія Володимирівна",
  "buyerContactEmail": "tender.obolon@example.kyiv.ua",
  "buyerContactPhone": "380971925137",
  "buyerRegion": "м. Київ",
  "buyerAddress": "ВУЛИЦЯ МАРШАЛА ТИМОШЕНКА, будинок 16",
  "buyerPostalCode": "04205",
  "valueAmount": 2195225,
  "valueCurrency": "UAH",
  "vatIncluded": true,
  "awardCriteria": "lowestCost",
  "cpvMain": "45453000-7",
  "items": [ { "description": "Капітальний ремонт/заміна ліфта …", "cpv": "45453000-7", "quantity": 1, "unit": "робота" } ],
  "awards": [ { "supplier": "МАЛЕ ПІДПРИЄМСТВО \"ПРОМКОМПЛЕКС\" …", "supplierId": "21615705", "awardValue": 2051018.19, "awardCurrency": "UAH", "awardStatus": "pending", "awardDate": "2026-07-06" } ],
  "bidCount": 3,
  "documentCount": 12,
  "dateCreated": "2026-06-19",
  "enquiryPeriodEnd": "2026-06-26",
  "tenderPeriodEnd": "2026-07-06",
  "tenderUrl": "https://prozorro.gov.ua/tender/UA-2026-06-19-012201-a",
  "source": "ProZorro / OpenProcurement — Ukraine public procurement (open data)"
}
```

### Example inputs (copy & paste)

```jsonc
// 1) Reconstruction works over ₴1M, last 30 days
{ "category": "works", "minValue": 1000000, "datePreset": "last_30_days", "maxResults": 500 }

// 2) Keyword — road / дорога — tenders
{ "searchTerm": "дорога", "datePreset": "last_90_days" }

// 3) Active tenders in a region
{ "status": "active", "region": "Львівська", "maxResults": 300 }

// 4) Completed contracts (to see awarded suppliers)
{ "status": "complete", "category": "works", "datePreset": "last_30_days" }

// 5) Custom date range
{ "datePreset": "custom", "dateFrom": "2026-01-01", "dateTo": "2026-03-31", "searchTerm": "школа" }
```

### Input & filters

- **Keyword** (`searchTerm`) — matches the title, description, **every item description and every CPV code**. Paste a CPV like `45453000` to pull a whole market segment, or a product name to find who's buying it.
- **Status** (`status`) — `active`, `complete`, `cancelled`, `unsuccessful`, or `any`.
- **Category** (`category`) — `goods`, `works`, `services`.
- **Buyer region** (`region`) — substring match on the buyer's region.
- **Value** (`minValue` / `maxValue`) — expected value bounds (UAH).
- **Date range** (`datePreset` + `dateFrom`/`dateTo`) — how far back to read (by last-modified date); newest first.
- **Max results** (`maxResults`) and **Max scan** (`maxScan`) — see Performance below.

### Use cases

- **Reconstruction-contract monitoring.** Filter `works` by region and value to track rebuild tenders as they're published — with the buying authority and (once awarded) the winning contractor.
- **Supplier & competitor intelligence.** See which companies win which contracts and for how much (award value vs expected value).
- **B2G market entry.** Size a category or region, find active tenders to bid on, and map the buyers.
- **Transparency & research.** Ukraine's ProZorro is a global open-procurement benchmark; analyse spend, methods and outcomes.

### Performance & throughput

The Actor reads the newest-first tender feed and fetches each tender's full detail (8 in parallel) to apply your filters. A **short date range + broad filter** is fast; a **narrow filter over a long range** must read more tenders to find matches, bounded by **`maxScan`** (default 20,000) — raise it for rare filters, and the run logs when the cap is hit (nothing is silently truncated). No proxies, no keys.

### Integrations

Works with **JavaScript / Python `apify-client`**, **Make / n8n / Zapier**, scheduled runs, webhooks, and the **Apify MCP server**. Example (JS):

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('foxlabs/ukraine-prozorro-tenders').call({
  category: 'works', minValue: 1000000, datePreset: 'last_30_days', maxResults: 200,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### Data quality (measured 2026-08-01)

Field fill rates on a **400-tender run** over the last 7 days:

| Field | Filled |
|---|---|
| `buyerContactName`, `buyerContactEmail`, `buyerContactPhone` | **92%** |
| `tenderID`, `buyerName`, `buyerId`, `buyerKind`, `status`, `category`, `valueAmount`, `cpvMain`, `items`, `tenderUrl` | **100%** |
| `documentCount` | 97% |
| `vatIncluded` | 94% |
| `buyerAddress` | 71% |
| `awards` (winning supplier) | 68% — only awarded tenders have one |
| `tenderPeriodEnd`, `awardCriteria` | 70% |
| `enquiryPeriodEnd` | 56% |
| `description` | 25% — most ProZorro tenders leave it blank; the detail is in `items` |

Fill rates move with the window and the mix of procurement methods in it: a narrower 120-tender run measured **98.3%** contacts, the wider 400-tender run **92%**. The table above quotes the lower, larger-sample figure.

Verified against the source: **10 of 10** sampled records matched the OpenProcurement API exactly on title, buyer e-mail and value. E-mail syntax valid on **118/118** rows that had one. Unique tenders **400/400**. **No column was empty across every row.**

Searching by CPV code was checked end-to-end: a run for `45453000` returned **100/100** rows that genuinely carry that code.

- **`bidCount` is `null`, not `0`, until bids are disclosed.** ProZorro withholds bids while a tender is open, so "no bids array" means *not published yet*, not *nobody bid* — reporting `0` there would be a lie. Measured: 88 of 120 rows were `null`, and **zero rows** reported a false `0`. The same rule applies to `lotCount`, `documentCount` and `contractCount`.
- **Contacts come from the official public register.** ProZorro requires each buying entity to publish a contact so suppliers can ask questions; that is the field this Actor returns, unchanged. Some are personal-looking addresses because that is what the officer registered. Use them for procurement enquiries — the purpose they were published for.
- **Phone formats are left raw.** 110 of 118 start with Ukraine's `380` country code; the rest are in local format. Nothing is rewritten.
- **Repeat buyers are normal.** 76 unique e-mails across 118 rows — a single authority runs many tenders.
- **Suppliers appear once a tender is awarded** — `awards` is empty for still-open tenders; filter `status: "complete"` to see winners.
- **Text is Ukrainian** (titles, buyer/supplier names) — as published by ProZorro; use Ukrainian keywords for `searchTerm`.
- **Date filtering is by last-modified date** (the feed's ordering) — a good proxy for "recent activity", not the original publication date.
- Nothing is fabricated — a missing value is `null`.

### Pricing

**$0.004 per tender ($4 per 1,000)** — pay only for results. You are charged per tender actually returned — tenders scanned but filtered out are never billed.

**Pay per result** — billed per tender returned. There's an Apify **free tier** to evaluate. No proxies, no third-party API costs — ProZorro's OpenProcurement API is free and open.

### FAQ

#### Where does the data come from?

Ukraine's official **ProZorro** system via the **OpenProcurement API** (OCDS).

#### Can I use it commercially / resell it?

Yes — ProZorro is open public-procurement data, published for reuse.

#### Do I get the winning supplier?

Yes, once a tender is awarded (`awards` with the supplier, its EDRPOU code and the award amount).

#### Is it only reconstruction tenders?

No — it's all Ukrainian public procurement; filter `category: "works"` (+ region/value) for reconstruction-type contracts.

#### Do I get a contact for the buyer?

Yes — the officer's name, e-mail and phone, on ~92% of tenders (measured). It's the contact the buying entity publishes in the official register precisely so suppliers can reach them.

#### Do I need an API key for ProZorro?

No. No key, no login.

### Troubleshooting

- **Few results for a narrow filter** → raise `maxScan`, or widen the date range / filters.
- **No `awards` on a tender** → it's still open; awarded suppliers appear on `complete` tenders.
- **Keyword returns nothing** → use the Ukrainian term (e.g. `дорога` not `road`).

### Support

Questions, a field you'd like added, or a custom build? Open the **Issues** tab, or email **info@foxlabs.com.tr**. We reply fast.

*If this Actor saves you time, a ⭐ review really helps.*

### Changelog

#### 0.2 — 2026-08-01

- **Buyer contacts added** — `buyerContactName`, `buyerContactEmail`, `buyerContactPhone` (measured 92% filled on a 400-tender run). They were already in the API response the Actor fetched and were being discarded, so this costs no extra requests and no extra runtime.
- **More of the tender exposed:** `buyerKind`, `buyerAddress`, `buyerPostalCode`, `vatIncluded`, `awardCriteria`, `cpvMain`, `enquiryPeriodEnd`, `tenderPeriodStart`, `noticePublishedAt`, `bidCount`, `lotCount`, `documentCount`, `contractCount`.
- **Keyword search now reads item descriptions and CPV codes**, not just the tender title — a supplier searching for their product finds tenders whose title never names it.
- **Counts stay `null` when undisclosed** rather than reporting a misleading `0` (ProZorro withholds bids until a tender closes).
- New **Buyer contacts** output view in the Console.

#### 0.1 — 2026-07-09

- Initial release. Ukraine ProZorro / OpenProcurement tenders with buyers, awarded suppliers, values, CPV-coded items and status. Filter by keyword, status, category, region, value and date. Open OCDS data, no key.

***

Part of the **[foXLabs data platform](https://data.foxlabs.com.tr/)** — official public-data company, contact, ownership, charity, procurement, location & AI-search intelligence scrapers. Browse the full suite at **[data.foxlabs.com.tr](https://data.foxlabs.com.tr/)**.

# Actor input Schema

## `searchTerm` (type: `string`):

Keep only tenders whose title or description contains this text (case-insensitive). Works in Ukrainian or transliterated terms, e.g. "дорога" (road), "школа" (school). Leave empty to browse all recent tenders.

## `status` (type: `string`):

Tender status. Active = currently open; Complete = concluded; Cancelled / Unsuccessful as named.

## `category` (type: `string`):

Main procurement category.

## `region` (type: `string`):

Keep only tenders whose buyer is in a region containing this text (e.g. "Київ", "Львівська"). Leave empty for all of Ukraine.

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

Keep only tenders with an expected value of at least this amount (in Ukrainian hryvnia). Leave empty for no minimum.

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

Keep only tenders with an expected value of at most this amount. Leave empty for no maximum.

## `datePreset` (type: `string`):

How far back to crawl (by last-modified date). Newest tenders are read first, so a shorter range is faster.

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

Only with Date range = Custom. Earliest date, YYYY-MM-DD.

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

Only with Date range = Custom. Latest date, YYYY-MM-DD.

## `maxResults` (type: `integer`):

Maximum number of matching tenders to return.

## `maxScan` (type: `integer`):

Safety cap: the Actor fetches each recent tender's details to apply your filters, so with a narrow filter it may scan many to find matches. This bounds how many it will read. Increase for rare filters.

## Actor input object example

```json
{
  "status": "any",
  "category": "any",
  "datePreset": "last_30_days",
  "maxResults": 500,
  "maxScan": 20000
}
```

# 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 = {
    "searchTerm": "",
    "region": "",
    "maxResults": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("foxlabs/ukraine-prozorro-tenders").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 = {
    "searchTerm": "",
    "region": "",
    "maxResults": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("foxlabs/ukraine-prozorro-tenders").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 '{
  "searchTerm": "",
  "region": "",
  "maxResults": 500
}' |
apify call foxlabs/ukraine-prozorro-tenders --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,foxlabs/ukraine-prozorro-tenders"
        }
    }
}

```

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/vIjhjBrQrSFCc8bw9/builds/fgKJuYWilR6ap17Rb/openapi.json
