# EU Procurement Buyer Intelligence (`atlas-data/eu-procurement-buyer-intelligence`) Actor

Track TED award notices from the buyer side with resolved or explicitly notice-level buyer identity, location, and classification evidence.

- **URL**: https://apify.com/atlas-data/eu-procurement-buyer-intelligence.md
- **Developed by:** [Atlas](https://apify.com/atlas-data) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

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

## EU Procurement Buyer Intelligence

Turn official EU Tenders Electronic Daily (TED) award notices into a normalized,
change-tracked stream of **buyer-side** intelligence: who is buying, where they sit, how they
are classified, what they awarded, and to whom.

This Actor is built for public-sector sales teams, bid-intelligence analysts, market
researchers, and policy teams who need to answer questions such as:

- Which public buyers in a country, NUTS region, or activity sector are awarding contracts?
- What does one buyer's award history, CPV mix, and spend profile look like?
- Which buyer types — local authorities, bodies governed by public law, health, education —
  are active in a category?
- Which buyers changed a published award, and what changed?
- Which award notices and buyer records should be loaded into a CRM, warehouse, or territory plan?

The reason to pay is not access to public TED data. It is the production workflow around it:
safe filtering, bounded exhaustive traversal, deterministic normalization, **evidence-based
buyer attribution**, stable IDs, deduplication, edit detection, replay-safe incremental state,
rate limits, retry handling, and concurrency protection.

### Output

Each dataset row is **one award notice**, with output `schemaVersion` **1.0.0**:

- stable `recordId` (SHA-256) derived only from the TED publication number, so it survives
  notice versions and content corrections;
- `contentDigest`, which changes when normalized source content changes and is separate from
  logical identity;
- TED `noticeId`, `noticeVersion`, canonical notice URL, title, language, form/notice type,
  and publication date;
- a `buyer` object holding all buyer evidence — see [Buyer attribution](#buyer-attribution);
- winner names, identifiers, countries, and selection statuses as a notice-level set;
- award dates, split into `decisionDates` (`winner-decision-date`) and `conclusionDates`
  (`contract-conclusion-date`), plus their union in `awardDates`;
- CPV codes, result-lot identifiers with stable `awardId` values, and contract identifiers;
- notice-result and lot-result amount/currency sets without amount-to-currency or
  value-to-lot joins;
- procedure type and identifier;
- buyer/supplier/performance geography, including buyer NUTS codes;
- `associationCompleteness` and `associationWarnings`;
- explainable relevance score/signals;
- `changeType`, `changedFields`, `fetchedAt`, and explicit TED attribution.

Logical IDs are deterministic, source-namespaced, and independent of output schema version,
notice version, and mutable content. They are not identifiers issued by TED.

### Buyer attribution

TED's Search API flattens nested eForms structures, publishing each buyer attribute as its
own independent array. Those arrays **cannot be paired by position**. Measured across 2,500
consecutive live award notices (full method and figures in [EVIDENCE.md](EVIDENCE.md)):

- 97.4% of notices published exactly one buyer name;
- among multi-buyer notices, 55% had at least one buyer facet whose length differed from the
  buyer-name count — including a notice with **20 buyer names against 40 buyer identifiers**,
  and one with **2 buyer names against 3 main-activity codes and a single legal type**.

`buyer-name` is a multilingual map, so buyers are counted as the organization names published
in the one selected output language. A single buyer whose name TED publishes in French, Dutch,
and English is one buyer, not three, and still resolves.

So the Actor emits one record per award notice and reports buyer evidence with an explicit
attribution level, never a guessed join:

| `buyer.attribution` | Meaning |
|---|---|
| `sole-buyer-resolved` | TED published exactly one buyer name in the selected language **and** at most one value in every other buyer facet. Each attribute has only one organization it can belong to, so `buyer.resolved` is emitted with name, identifier, country, city, post code, NUTS code, legal type, main activity, profile URL, and internet address. `associationCompleteness` is `buyer-resolved`. |
| `notice-level-unpaired` | The facets provably cannot be paired. No `resolved` object is emitted. Every facet remains an independent notice-level set. `associationCompleteness` is `notice-level-only`. |

`buyer.facetCounts` always publishes the raw per-facet value counts TED returned, so you can
audit exactly why a record did or did not resolve. Set `requireResolvedBuyer: true` to keep
only provably attributable records.

Note that a single buyer name is **not** on its own sufficient: 221 of the 2,436 sampled
single-name notices published more than one buyer identifier. Those do not resolve.

`main-activity` and `authority-main-activity` are parallel encodings of the same concept and
are merged into `mainActivities`; a notice whose two activity fields disagree does not resolve.

Winner sets, lot identifiers, contract identifiers, and value sets are likewise preserved as
separate notice-level sets and never joined to each other or to a buyer. This is intentionally
conservative: for market intelligence, a fabricated buyer-to-contract link is worse than an
explicit "cannot be determined".

### Run modes

`full` emits every matching award notice as `snapshot`.

`incremental` emits only `new` and `updated` notices. It overlaps the last completed
publication watermark (three days by default) so later TED edits are detected.

`diff` tracks the same durable state but also emits `unchanged` notices for reconciliation
and warehouse snapshots.

The change digest is keyed by TED notice ID. A changed buyer attribute, winner, value set,
award date, lot identifier, procedure, URL, or association warning is reported as an update.
`changedFields` identifies the changed top-level normalized fields, so a buyer edit surfaces
as `["buyer"]`. The stable `recordId` never changes while `contentDigest` does.

### Safe filtering

Buyer-side filters:

- buyer/notice keywords, matched against buyer names and the notice title;
- buyer countries (alpha-3);
- buyer NUTS prefixes, matched against `buyer-country-sub` — `DE` matches all German regions;
- buyer legal types, for example `la` or `body-pl`;
- buyer main activities, for example `gen-pub`, `health`, `education`;
- `requireResolvedBuyer` to keep only provably attributable buyer records.

Award-side filters:

- winner/supplier keywords and countries;
- CPV prefixes and performance countries;
- inclusive publication and award-date ranges, with one award date required to satisfy both bounds;
- minimum award value in one exact currency;
- TED result notice types;
- optional inclusion of award notices exposing no winner name;
- preferred output language with English and deterministic fallback.

Keyword groups use OR semantics; different filter groups combine with AND. `minimumValue` and
`minimumValueCurrency` must be supplied together and no FX conversion is performed. Because
TED values are flattened, the value filter is intentionally strict: only a scope containing
exactly one amount and exactly one currency can satisfy it.

User strings are never inserted into TED expert-query syntax. The server-side query contains
only strictly validated calendar dates plus the fixed, live-verified `form-type = result`
predicate. All variable business filtering happens against normalized rows inside the Actor.

An optional relevance profile adds or subtracts weights across `title`, `buyer`,
`buyerActivity`, `buyerLegalType`, `winner`, `cpv`, `country`, `nuts`, `place`, and
`procedure`. Every contribution is emitted in `relevance.signals`.

### Example input

```json
{
  "mode": "incremental",
  "buyerCountries": ["DEU", "FRA"],
  "buyerNutsPrefixes": ["DE3"],
  "buyerMainActivities": ["gen-pub", "health"],
  "buyerLegalTypes": ["la"],
  "requireResolvedBuyer": true,
  "cpvPrefixes": ["72"],
  "publicationFrom": "2026-01-01",
  "publicationTo": "2026-01-05",
  "minimumValue": 250000,
  "minimumValueCurrency": "EUR",
  "language": "eng",
  "maxItems": 500,
  "relevance": {
    "include": [
      { "term": "cloud", "fields": ["title"], "weight": 15 },
      { "term": "health", "fields": ["buyerActivity"], "weight": 8 }
    ],
    "exclude": [],
    "minimumScore": 0,
    "requireIncludeMatch": false
  }
}
```

The publication window is kept to five days on purpose: every window must hold at most 6,800
TED result notices, and local filters do not shrink it. See below.

### Bounded publication windows

**One publication window must contain at most 6,800 TED result notices.** This is a hard
contract, not a soft cap. Before any dataset row is written the Actor reads the window's
`totalNoticeCount` from TED's first page and fails the run with an explicit message if the
window is larger, on a newly opened window and on every resumed traversal of the same window.

The reason is durable state. A window is only replay-safe while its **complete** source ledger
— what stops an already handled notice from hiding an unprocessed one after an iteration token
expires — and the change digests it produces both fit the 3.5 MB state budget at the same time.
Worst case each source notice costs one of each: 460 bytes of digest and 40 bytes of ledger.
With 64 KiB reserved for the inbound token, configuration skeletons, and JSON scaffolding:

```
65,536 + N x (460 + 40) <= 3,500,000   =>   N <= 6,868   =>   6,800 rounded down
```

TED publishes about 1,075 result notices per day EU-wide, so 6,800 is roughly a six-day window.
Server-side filtering is date plus `form-type = result` only — buyer, CPV, and country filters
run locally and do **not** reduce the source rows a window must traverse — so this bound applies
to every configuration equally.

What this means in practice:

- **Split long date ranges.** Run 2026-01-01…2026-01-05, then 2026-01-06…2026-01-10, and so on,
  rather than a single quarter or year. Each run is independent and the dataset is additive.
- **The default window is deliberately short.** With no `publicationFrom`, the Actor looks back
  5 days from `publicationTo` (default: today). Inclusive of both endpoints that is six days of
  publications, about 6,450 notices at the evidenced rate — inside the bound, with headroom. A
  6-day default would not be: seven days is roughly 7,525 notices.
- **Widen the window only with filters you have measured.** Business filters run locally and do
  not shrink the traversal, so a longer range needs splitting, not narrower keywords. Set
  `publicationFrom` and `publicationTo` explicitly and keep each run inside the bound.
- **Schedule incremental runs often enough** that the watermark overlap plus the gap since the
  last successful run stays under the bound — roughly every three days at current volumes.

The retained-digest count per configuration is fixed at 6,800 for the same reason: exactly the
per-window maximum, so a configuration can never prune away a digest belonging to the window it
is traversing and re-emit an unchanged notice as `new`. It is an internal constant rather than
an input — there is no value a caller could supply that is both safe and different.

### Traversal, caps, and resume

The Actor uses TED `ITERATION` mode, with a maximum of 250 notices per page and 10,000
requested fields per page. The shipped field set is 33 fields, so the default page size of 250
stays inside the cap; a test asserts this bound. Traversal within a window is exhaustive; the
window itself is bounded as described above.

`maxItems` counts matched award notices, not source rows. Sparse searches continue behind
rejected notices. Cost and forward progress are independently bounded by `maxPages` and
`maxScannedItems`. Duplicate-only pages and rows revisited after an expired token do not
consume those forward budgets; replay has separate hard page/row ceilings to stop malformed
endless loops.

If a cap lands inside a page, durable state records the exact query window, run mode, original
page size, inbound opaque token, row offset, and compact source ledger. The next run completes
that same window even if the date, page size, or operational caps changed. Expired or invalid
iteration tokens replay the same window from its start; the ledger prevents already handled
rows from hiding an unprocessed row. Switching an unfinished incremental cursor to `diff`
restarts that window without the incremental source ledger so previously handled rows are
emitted as `unchanged`. Repeated tokens, replay ceilings, oversized pages, conflicting
duplicate notice versions, malformed envelopes, and `timedOut: true` fail closed.

HTTP 429, 5xx, and network errors receive bounded exponential-backoff retries. Requests are
sequential and conservatively rate-limited. A failure after partial dataset output never
advances durable state.

### Write ordering and recovery

Before contacting TED, the Actor reserves whole-document space for the **complete** replay
ledger of the bounded window it is about to traverse — not merely for the rows one run may add —
so a window that starts is a window later runs can finish. If admission cannot fit the 3.5 MB
durable-state budget, source processing and dataset writes do not start.

After admission, the write order is strict:

1. dataset rows;
2. default key-value-store `OUTPUT` summary;
3. durable cursor/digest state last.

A source, dataset, summary, lease, or state failure before step 3 leaves the work replayable.
Replays may duplicate already written dataset rows. Consumers should upsert by stable
`recordId`; use `contentDigest`, `changeType`, and `fetchedAt` to distinguish revisions. Do not
use `noticeVersion` or `contentDigest` as the logical upsert key.

The `OUTPUT` record includes scan/match/emission/duplicate counts, replayed page/row counts,
the `resolvedBuyers` / `unpairedBuyers` attribution split, change counts, stop reason, status
(`succeeded` or `partial`), query window, configuration hash, dataset ID, timestamp, and attribution.

### Isolated bounded state and concurrency

Business state is keyed by a SHA-256 fingerprint of all selection, language, value, buyer, and
relevance settings. Operational caps do not fork state. Full-snapshot and change-tracking
resume cursors are separate.

This Actor uses its own resources, disjoint from any sibling Actor:

- named state store: `eu-procurement-buyer-intelligence-state-v1`;
- state key namespace: `EU_PROCUREMENT_BUYER_STATE_V1`;
- lock queue: `eu-procurement-buyer-intelligence-lock-v1`.

State commits are immutable uniquely keyed snapshots. A persistent Request Queue entry provides
exclusive writer leases, periodic prolongation, ownership checks, and server-side expiry.
Snapshot selection compares semantic document revisions before key ordering so a stale writer
cannot win merely through a later random key. Release is best-effort after a successful commit.

Each configuration keeps compact digests for 180 days by default, configurable from 7–730 days,
and for a fixed, non-configurable 6,800 notices — exactly the per-window source maximum, so the
window being traversed can never lose its own digests. The complete state document is measured against a
conservative 3.5 MB budget; old completed configurations and then other configurations' old
digests are pruned. The active configuration's ledger and digests are never eviction
candidates: if they cannot be committed within the budget the save fails closed instead.
Before a new run is admitted, old ledgers in other saved configurations or resume slots
that would prevent a safe commit are converted to small recovery cursors: their exact saved
windows remain, while token, offset, and source ledger are cleared so they safely restart from
row zero when next selected. The selected active resume is never discarded for admission; if it
cannot grow safely, the run fails before reading TED or writing dataset rows. Snapshot
listing/cleanup is bounded and tolerant of eventual consistency; any repeatedly unreadable
listed candidate that could be newest makes loading fail closed.

Schedule non-overlapping runs where possible. Separate dataset, `OUTPUT`, and state writes are
not one cloud transaction, so overlapping runs can still create replayed dataset rows even
though stale state commits are rejected.

### Cost expectations

The default memory is **1024 MB**, which consumes 1 compute unit (CU) per hour of runtime. At an
example rate of **$0.20/CU**, a 10-minute run costs about **$0.033** in compute and a one-hour
run about **$0.20**, before storage operations and data transfer.

These are planning examples, not quoted prices. Actual rates depend on the Apify plan, discounts,
pricing model, memory override, storage, transfer, source volume, and run duration. Start with a
short publication window — it must hold at most 6,800 result notices in any case — and
conservative caps, then inspect the run Usage details.

### Limitations

- A single publication window may hold at most 6,800 TED result notices, about six days of
  EU-wide awards. Broader ranges are refused before any row is written and must be split across
  runs; local filters do not relax this, because filtering happens after traversal.
- Coverage and quality are limited to what the official TED Search API exposes. Source omissions
  and incorrect source data remain omissions/incorrect data.
- Roughly 2.6% of live award notices list more than one buyer. Those records are deliberately not
  attributed to an individual buyer, so per-buyer aggregates built only from
  `sole-buyer-resolved` records will exclude them. Use `buyer.facetCounts` and
  `buyer.attribution` to decide how to treat them.
- Notice-level award values belong to the notice, not to any one buyer or lot. Summing them per
  buyer across a multi-buyer notice would double count.
- Buyer identifiers may be national IDs, European IDs, UUID-like internal values, or other source
  strings. The Actor preserves them without relabeling their type, and does not resolve one
  organization across notices or countries.
- Buyer names are language-tagged source strings; the same organization may appear under
  different names or languages across notices. Buyer counting uses the selected language only,
  so translations of one name are never mistaken for several buyers.
- Values are not converted between currencies or inflation-adjusted.
- Stable record/award IDs do not change with notice versions or mutable content. A corrected
  result-lot identifier is a different logical lot and receives a different `awardId`.
- This Actor is not legal, procurement, sanctions, or supplier-due-diligence advice.

### Local development and release checks

Requires Node.js 22.

```bash
npm ci
npm run lint
npm run typecheck
npm run build
npm test
npm run smoke:local
npm run smoke:live
npm audit --omit=dev
npm audit
apify validate-schema
```

`npm run smoke:live` boundedly scans up to 500 recent production TED award notices and asserts
the buyer-attribution invariants against real data — no `notice-level-unpaired` record may carry
resolved attributes, every `sole-buyer-resolved` record must come from a single published buyer
name — then validates both record shapes against the dataset schema. The normal
fixture/adversarial suite needs no network, cloud storage, credentials, or secrets.

The production Docker image builds TypeScript in an isolated Node 22 stage and installs only
runtime dependencies in the final Apify Node 22 image.

### Data source and attribution

Source: [TED — Tenders Electronic Daily](https://ted.europa.eu/) via the anonymous
[TED Search API v3](https://api.ted.europa.eu/v3/notices/search).

The API is officially documented as anonymously accessible for published-notice reuse. Preserve
the attribution included with every row and review the current
[TED legal notice](https://ted.europa.eu/en/legal-notice) for your use case. This Actor does not
claim ownership of TED source data.

# Actor input Schema

## `mode` (type: `string`):

Full emits a snapshot; incremental emits new/updated award notices; diff also emits unchanged ones.

## `buyerKeywords` (type: `array`):

OR match against normalized buyer names and the notice title.

## `buyerCountries` (type: `array`):

TED/ISO alpha-3 country codes of the contracting buyer.

## `buyerNutsPrefixes` (type: `array`):

Prefix match against the buyer NUTS region from buyer-country-sub, for example DE or FRK.

## `buyerLegalTypes` (type: `array`):

TED buyer-legal-type codes, for example la for local authority or body-pl.

## `buyerMainActivities` (type: `array`):

TED main-activity codes, for example gen-pub, health, or education.

## `requireResolvedBuyer` (type: `boolean`):

Keep only award notices where TED published one buyer and at most one value per buyer facet, so every buyer attribute is provably attributable.

## `winnerKeywords` (type: `array`):

OR match against normalized winning supplier names.

## `winnerCountries` (type: `array`):

TED/ISO alpha-3 country codes for winning suppliers.

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

Two to eight digits; 72 matches the complete IT family.

## `placeCountries` (type: `array`):

TED/ISO alpha-3 place-of-performance country codes.

## `publicationFrom` (type: `string`):

Inclusive YYYY-MM-DD; defaults to 5 days before publicationTo, which keeps an unfiltered default run inside the per-window maximum at current EU-wide volume. One publication window must hold at most 6800 TED result notices; a broader window is refused before any dataset row is written, so split long date ranges into several narrower runs.

## `publicationTo` (type: `string`):

Inclusive YYYY-MM-DD; defaults to today. One publication window must hold at most 6800 TED result notices; a broader window is refused before any dataset row is written, so split long date ranges into several narrower runs.

## `awardDateFrom` (type: `string`):

Inclusive lower bound. The same award date must also satisfy awardDateTo when both are set.

## `awardDateTo` (type: `string`):

Inclusive upper bound. The same award date must also satisfy awardDateFrom when both are set.

## `minimumValue` (type: `number`):

Conservatively compared only when one value scope has exactly one amount and one currency; no FX conversion.

## `minimumValueCurrency` (type: `string`):

Exact three-letter currency used with minimumValue; no FX conversion.

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

TED result notice type codes, for example can-standard.

## `includeWithoutWinner` (type: `boolean`):

Keep award notices even when TED exposes no winner name.

## `language` (type: `string`):

Falls back to English and then the first available language.

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

Matched award notices, not raw TED rows.

## `relevance` (type: `object`):

Optional explainable weights across normalized buyer and award fields.

## `pageSize` (type: `integer`):

Advanced. Official maximum is 250 notices per page.

## `maxPages` (type: `integer`):

Stop after this many TED pages containing new rows. Expired-token duplicate-only replay has a separate hard ceiling.

## `maxScannedItems` (type: `integer`):

Bound newly handled source rows independently of matches. Expired-token duplicates do not consume this budget. One publication window is separately capped at 6800 source notices.

## `overlapDays` (type: `integer`):

Recheck recently published award notices to detect edits. Keep the overlap plus the gap since the last run inside the 6800-notice per-window maximum.

## `stateRetentionDays` (type: `integer`):

Forget completed change digests older than this many days. The retained-digest count per configuration is not an input: it is fixed internally at 6800, exactly the per-window source-notice maximum, so a window's own digests are never pruned away and its unchanged notices are never re-emitted as new.

## Actor input object example

```json
{
  "mode": "incremental",
  "buyerKeywords": [],
  "buyerCountries": [],
  "buyerNutsPrefixes": [],
  "buyerLegalTypes": [],
  "buyerMainActivities": [],
  "requireResolvedBuyer": false,
  "winnerKeywords": [],
  "winnerCountries": [],
  "cpvPrefixes": [],
  "placeCountries": [],
  "noticeTypes": [],
  "includeWithoutWinner": false,
  "language": "eng",
  "maxItems": 1000,
  "relevance": {
    "include": [],
    "exclude": [],
    "minimumScore": 0,
    "requireIncludeMatch": false
  },
  "pageSize": 250,
  "maxPages": 100,
  "maxScannedItems": 10000,
  "overlapDays": 3,
  "stateRetentionDays": 180
}
```

# Actor output Schema

## `dataset` (type: `string`):

One normalized record per TED award notice with resolved or notice-level buyer evidence.

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

Counts, buyer attribution split, status, stop reason, window, configuration hash, and attribution.

# 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("atlas-data/eu-procurement-buyer-intelligence").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("atlas-data/eu-procurement-buyer-intelligence").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 atlas-data/eu-procurement-buyer-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=atlas-data/eu-procurement-buyer-intelligence",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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