# Poland BZP Procurement Notices - Below-EU, Per Notice (`nexgensignal/pl-bzp-procurement-notice-records`) Actor

Poland BZP (ezamowienia.gov.pl) below-EU-threshold procurement notices by month as clean per-record data - number, date, order object, CPV, offer deadline and contracting organisation. Cursor-walks past the 500-row cap. htmlBody/contractors dropped. $0.05 per record.

- **URL**: https://apify.com/nexgensignal/pl-bzp-procurement-notice-records.md
- **Developed by:** [NexGen Signal](https://apify.com/nexgensignal) (community)
- **Categories:** Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $33.50 / 1,000 bzp notice records

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

## Poland BZP Procurement Notices - Below-EU, Per Notice

Poland's public-procurement notices from the **BZP** (Biuletyn Zamowien Publicznych) as clean, per-notice
records, for the month you choose - notice number, publication date, subject, CPV, offer deadline and the
contracting organisation. **Below-EU-threshold national notices; no notice HTML, no contractors block.**

### What one record represents

The source is the **BZP** public API at `ezamowienia.gov.pl`. Each record is **one contract notice**: its notice
and BZP number, notice/order/tender type, whether the amount is below the EU threshold, the publication date,
the order object (subject), the main CPV, the deadline for submitting offers, the procedure result, and the
contracting organisation (name, city, province, country and national id).

### Coverage and volume, and the 500-row cap

The BZP notice API caps a single query at **500 rows and ignores its paging parameter** beyond that - a query
for a busy day returns only the first 500. To retrieve the full month, this Actor **walks by publication-date
cursor**: it queries the window, advances the cursor to the last publication date seen, and repeats, deduping on
the notice object id, until a window returns fewer than 500. This provably clears the cap - a dry-test over a
busy three-day stretch recovered 1,582 distinct notices where a day-by-day query truncated at 1,500.

**Sol's Wave-4 index put this door at 12,171+ ContractNotices for Aug-2026 and flagged the 500-row cap as a
build risk; the cursor walk clears the cap and retrieves the full month - the capacity is the walked count.**

Set the **Publication month** input (`YYYY-MM`) to choose the month.

### Licence and attribution

Per the portal Regulamin, the public API is free to use and the API URL is named in the terms. The full notice
travels on every record:

> BZP (Biuletyn Zamowien Publicznych), ezamowienia.gov.pl. Per the portal Regulamin (s.11), the public API is free to use and the API URL is named in the terms. Notice metadata only; the full notice HTML (htmlBody) and the contractors block are never delivered.

### Person-data policy

The full notice HTML (`htmlBody`) and the `contractors[]` block - which can carry natural-person winners - are
**never in the query and never delivered**. A per-record assertion rejects either (verified with a
planted-field test). The contracting organisation is delivered as an organisation. No natural-person data is
processed.

### Interpretation caveat

One record per Polish BZP contract notice for the selected month: notice number, publication date, order object, main CPV, offer deadline, procedure result and the contracting organisation. Below-EU-threshold national notices (0% overlap with EU TED). htmlBody and the contractors\[] block are never delivered. The API caps a query at 500 rows and ignores paging, so the Actor walks the month by publication-date cursor.

Values are reproduced verbatim from the API; the Actor never rewrites a field. `is_below_eu_threshold` is a real
boolean; these are national below-threshold notices and do not appear on EU TED.

### Provenance and compliance

Every run reads the door host's `robots.txt` at runtime; the gate result (URL, status, byte length and, where a
policy is served, its SHA-256) is written to the run's `RUN_RECEIPT`. Where the host serves no applicable
robots rule, the gate records that (flagged) and proceeds on the licence, which grants re-use. The endpoint is
keyless. The Actor never bypasses a block or fetches through a mirror.

### Data quality and freshness

Boolean columns are delivered as real booleans and numeric columns as real numbers. Delivery is keyed on a
stable id, so the dataset is safe to diff, deduplicate or upsert. Every run re-reads the live door, so the data
is as fresh as the source publishes, and each record's `observed_at` stamp dates the snapshot. The run's
`RUN_RECEIPT` records the source URL and how many records were delivered and charged, and confirms
`charge_equals_delivered`.

### Billing, delivery and joins

Pricing is per record: you are billed only for records the Actor actually delivers, with the charge raised after
each record is pushed (push-then-charge), so a failed or empty run costs nothing. The **Maximum records** cap
bounds every run, so you control spend precisely - sample cheaply, then raise it. Every record is a flat, typed
object keyed on a stable id, so the data loads without a cleaning pass, diffs cleanly between runs, and upserts
into a table you maintain over time; re-running keeps that table current without re-paying for rows you already
hold, and each receipt reconciles delivered against charged. Because the source's own identifiers are preserved
verbatim, the dataset joins cleanly onto other sources keyed on the same identifier.

### Scaling and scheduling

Set **Maximum records** low to sample the shape of the data cheaply, then raise it once the cell fits your use.
The Actor delivers incrementally and streams its source, so memory stays flat regardless of how many records you
request, and you are billed only for what is delivered. Because the source republishes on its own cadence, a
scheduled run keeps a downstream table current: new and changed records upsert over the old ones on the stable
key, and the `observed_at` stamp on every record tells you when each was last seen live. There is no
subscription and no minimum - the per-record price and the record cap together mean the spend on any run is
known in advance and matched exactly to the data you receive.

### Inputs

- **Publication month** (`month`, required) - the month to walk, as `YYYY-MM`. Prefilled with `2026-08`.
- **Maximum records** (`maxRecords`) - hard cap on notices delivered and billed within the month.

### Output

Records land in the Actor's default dataset and export as JSON, CSV, Excel or via the Apify API. A tabular
overview surfaces notice number, publication date, order object, organisation, CPV, offer deadline and the
below-threshold flag.

### Fields in detail

The record leads with `notice_number`, `bzp_number`, the type fields, `is_below_eu_threshold`,
`publication_date`, `order_object`, `cpv_code`, `submitting_offers_date`, `procedure_result`, and the
organisation block (`organization_name`, `organization_city`, `organization_province`, `organization_country`,
`organization_national_id`, `organization_id`), plus `tender_id` and `object_id`. The provenance block closes
every record.

### Typical uses

Polish bid teams use this cell to track the national below-threshold market that never reaches EU TED - who is
buying what, by CPV and deadline - as a flat table keyed on the notice id. Because the CPV and organisation are
first-class fields, a filter surfaces every notice in a CPV family this month, and the national id joins to a
supplier or buyer registry. A scheduled monthly run keeps a downstream tender feed current.

### How the cursor walk works

The walk is the whole trick to this door. Because the API returns at most 500 rows per query and ignores its
page number, a naive pull loses every notice past the 500th on a busy day. This Actor instead treats the
publication date as a cursor: it requests the window, delivers the batch, then sets the next request's start to
the last publication date it saw and requests again, deduplicating on the notice's object id so the one
boundary-overlap row is not counted twice. It repeats until a window returns fewer than 500 rows, which means
the month is exhausted. The run receipt records how many windows were walked and how many distinct notices were
retrieved, so the completeness of the walk is auditable on every run.

### Sibling Actors

It sits beside the fleet-2 EU TED tender monitor (EU-level, 0% overlap - these are below-threshold national notices) and the fleet's other national procurement cells (Netherlands, Germany, France). It shares its engineering with the fleet's other records Actors.

# Actor input Schema

## `month` (type: `string`):

Which month of BZP contract notices to walk (YYYY-MM). The API caps a query at 500 rows and ignores paging, so the Actor walks the month by publication-date cursor to retrieve the full set. 2026-08 holds 12,000+ notices.

## `maxRecords` (type: `integer`):

Maximum notices delivered and billed within the month. You are billed only for records delivered.

## Actor input object example

```json
{
  "month": "2026-08",
  "maxRecords": 500
}
```

# Actor output Schema

## `results` (type: `string`):

The delivered Poland BZP procurement notice record.

# 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 = {
    "month": "2026-08",
    "maxRecords": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("nexgensignal/pl-bzp-procurement-notice-records").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 = {
    "month": "2026-08",
    "maxRecords": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("nexgensignal/pl-bzp-procurement-notice-records").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 '{
  "month": "2026-08",
  "maxRecords": 500
}' |
apify call nexgensignal/pl-bzp-procurement-notice-records --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nexgensignal/pl-bzp-procurement-notice-records"
        }
    }
}
```

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/Og2CxTf0m4TYVTNMU/builds/ehI1LMGuwed90uFay/openapi.json
