# EU Tender Supplier Fit & Requirements Review (`prixodik/tender-supplier-fit`) Actor

Review procurement lots against your company profile with source quotes, deadline checks, unknowns and optional Anthropic analysis.

- **URL**: https://apify.com/prixodik/tender-supplier-fit.md
- **Developed by:** [Milchenko Oleksii](https://apify.com/prixodik) (community)
- **Categories:** Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 57.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 assessed procurement lots

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

## EU Tender Supplier Fit & Requirements Review

Review procurement **lots** against your company profile before investing time in a bid. Get source conditions, exact quotations, deadline exclusions, relevance components, conflicts with supplied company facts, and explicit unknowns.

### What the Actor does

- Searches the official EU TED API, or accepts explicit lot records from your own system or another Actor.
- Reads official XML when available and keeps lot-specific requirements, classifications, locations and deadlines separate. Procedure-level exclusion declarations are labelled separately.
- Excludes award/result notices, passed submission deadlines and explicitly conflicting place-of-performance countries.
- Calculates a transparent **relevance** score: 40 for an exact supplied CPV match, 40 for a supplied service phrase found in this lot's title/description, 20 for a supplied performance-country match. Empty or missing values earn no points. This score does not measure legal eligibility or probability of winning.
- Optionally uses Anthropic to compare multilingual source conditions with your supplied company facts. Every accepted source and company quotation is checked against its actual input text.
- Supports snapshots and changes, including deadline expiry without edits to the source.

Every non-excluded result remains `review-required`. `legalEligibilityConfirmed` and `documentsComplete` are always false. A company fact is a self-reported declaration, not an independently verified certificate.

### Start without an AI key

```json
{
  "companyProfile": {
    "name": "Example security integrator",
    "services": ["camera installation", "access control"],
    "cpvCodes": ["35120000", "32323500"],
    "countries": [],
    "facts": [
      { "id": "services", "text": "We install and maintain video surveillance systems." }
    ]
  },
  "maxNotices": 5,
  "maxXmlDocuments": 3,
  "maxLots": 20
}
```

Replace the example profile with your company's facts. Search defaults to the last thirty days and selected security-related CPV codes. Set `cpvCodes` to your desired search categories or an empty array for no category restriction. Search categories and profile relevance categories are separate. `noticeIds` selects exact TED publication numbers and replaces date/category discovery.

`companyProfile.countries` restricts **place of performance**, using three-letter country codes. It does not claim that a company is eligible based on its nationality. Buyer countries are returned separately.

### Anthropic semantic review

Set `analysisMode` to `anthropic`, enter your own encrypted `anthropicApiKey`, choose a compatible model, and supply explicit `companyProfile.facts` with unique IDs. All generated titles and reasoning are English; original source quotations retain their source language.

Requirement statuses:

| Status | Meaning |
| --- | --- |
| `profile-supported` | A supplied company fact supports the condition, with an exact fact quotation. Not independently verified eligibility. |
| `conflict` | An explicit supplied fact conflicts with the source condition. Review the evidence. |
| `unknown` | Missing, ambiguous, unreviewed or unverifiable evidence. Missing facts do not become negative facts. |

The model receives up to ten conditions per lot, up to 2500 characters per condition, and at most thirty supplied facts. Selection conditions and lot-specific conditions are prioritized, with lexical relevance to the supplied facts as a tie-breaker. The full returned source conditions remain available; unreviewed conditions stay unknown. The Actor validates identity and literal quotations, not the correctness of every semantic or legal interpretation. Source instructions are treated as data. Review important decisions yourself.

`maxAiLots` and `maxAiBudgetUsd` bound requests using the token prices you configure. The cost estimate uses a conservative pre-request allowance, accounts for rejected/incomplete replies, and reserves the allowance when usage is unknown. **It is not a hard Anthropic account spending cap.** Set correct prices and your provider's own spending controls. There are no automatic provider retries.

### Use another Actor's data

Set `sourceMode` to `provided-notices`. Each notice contains `id`, `sourceUrl`, `noticeType` and an explicit `lots` array. A lot contains:

```json
{
  "id": "LOT-0001",
  "titleOriginal": "Camera installation",
  "descriptionOriginal": "Installation and maintenance of cameras.",
  "cpvCodes": ["35120000"],
  "countryCodes": ["ROU"],
  "deadlineDateOriginal": "2030-12-01+02:00",
  "deadlineTimeOriginal": "15:00:00+02:00",
  "requirements": [
    { "id": "authorization", "textOriginal": "The bidder must hold an ANRE C1A authorization." }
  ]
}
```

This is an **illustration, not a real tender**. See `example-provided-input.json` for a complete sample. The Actor does not fetch arbitrary provided URLs or launch paid child Actors. The caller obtains data with appropriate permissions and supplies the lot structure. A flattened feed of parallel arrays cannot establish lot-to-deadline or lot-to-requirement relationships.

### Coverage and missing XML

TED search is a bounded window, not exhaustive coverage of all procurement portals. XML may be unavailable. With exactly one explicit lot identifier, the Actor can retain that lot's API description, source criteria and a single published date, labelled `single-lot-api`. Exact time, attachments and criteria completeness remain unverified. With multiple or missing lot identifiers it returns an unverified scope, unknown deadline and no inferred detailed requirements. It never joins parallel arrays by position. Missing fields do not mean no legal conditions exist. Procurement attachments, documents on other portals, amendments, cancelled procedures and company documents require separate verification. This version does not download PDFs or promise full UK coverage. API fallbacks do not replace a verified XML baseline in change mode.

Limits: up to fifty source notices, ten search pages, ten XML downloads and one hundred assessed lots. The summary reports search-window coverage, XML attempts, delivered rows and AI usage. Source failures do not produce a false successful full-source baseline.

### Changes and state

Use `stateStoreId` for a dedicated workflow. First run with `mode: "snapshot"`, then use `mode: "changes"`. Only delivered, sufficiently scoped and successfully reviewed rows advance state. Unverified XML fallback or failed/incomplete AI reviews remain retryable. Billing limits cannot mark undelivered records as delivered. Company profile and model changes use a different state namespace.

Run the same state **serially**. A pre-write check detects observed concurrent changes, but the storage API does not provide an atomic distributed lock. Do not overlap runs sharing a store. A failure after dataset delivery but before state persistence can cause redelivery; downstream consumers should deduplicate by `id` and `sourceHash`. No deletion or withdrawal inference is made from absence in a bounded search.

### Pricing

Proposed initial price: **$0.01 per start + $0.02 per delivered lot review**. Excluded lots and explicitly unverified-scope results are assessments and are charged when delivered. A run that delivers zero rows still incurs the start fee. Platform usage is included in this proposed Actor price. Pricing is active only when configured in Apify; check the Store pricing panel for current terms.

Examples excluding external AI: zero rows $0.01; ten rows $0.21; one hundred rows $2.01; one thousand rows across multiple bounded runs incur each run's start fee.

**Anthropic usage is optional, paid directly through your own key, and is additional to Actor charges.** This Actor does not purchase upstream Actor outputs for you.

### Outputs

The default dataset contains one assessment per lot or one explicitly unverified fallback scope. `SUMMARY` in the default key-value store describes bounds, source coverage, delivered count and estimated AI cost. Original titles and quotations preserve source language. All keys, generated titles, explanations and runtime messages are English.

This is preliminary decision support, not legal advice, procurement eligibility certification, an assurance of completeness, or a guarantee of winning a contract. Always verify the official notice and procurement documents.

Sources: [TED Search API](https://docs.ted.europa.eu/api/latest/search.html), [TED legal notice](https://ted.europa.eu/en/legal-notice). Rights in documents on third-party portals must be checked separately.

# Actor input Schema

## `sourceMode` (type: `string`):

Read the official TED API or process explicit lots supplied by you. Never fetches arbitrary provided URLs.

## `companyProfile` (type: `object`):

Name, services, eight-digit CPV codes, optional three-letter place-of-performance countries, and facts with unique id and text. Company facts are self-reported, not verified certificates.

## `notices` (type: `array`):

Array with id, sourceUrl, noticeType, and explicit lots. Each lot has id, titleOriginal, cpvCodes, countryCodes, deadlineDateOriginal, deadlineTimeOriginal and requirements with id/textOriginal. Used only in provided-notices mode.

## `noticeIds` (type: `array`):

Optional exact publication numbers, such as 675040-2026. Replaces date and category search.

## `cpvCodes` (type: `array`):

Eight-digit exact categories used to discover TED notices. Separate from profile relevance codes.

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

Optional inclusive YYYY-MM-DD. Empty uses the last thirty days.

## `maxNotices` (type: `integer`):

Maximum source notices examined; one notice may contain several lots.

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

TED notices per request.

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

Bounded source search pages.

## `maxLots` (type: `integer`):

Maximum assessed lot records, including exclusions and unknown cases.

## `maxXmlDocuments` (type: `integer`):

Bounded official XML downloads. Missing XML produces an unverified-scope record, not a positive eligibility decision.

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

Snapshot or new/changed results only. Shared state must not be used by overlapping runs.

## `stateStoreId` (type: `string`):

Required for changes mode. One workflow per store, serialized runs only. Failed or incomplete reviews do not advance the baseline.

## `analysisMode` (type: `string`):

Rules extract source conditions and relevance. Anthropic optionally compares multilingual requirements with supplied company facts.

## `anthropicApiKey` (type: `string`):

Your own key, encrypted by Apify. External Anthropic charges are paid directly by you and are separate from Actor charges.

## `anthropicModel` (type: `string`):

Your model must support JSON schema structured outputs.

## `maxAiLots` (type: `integer`):

At most one call per reviewed lot; up to ten source conditions and 2500 characters per condition per call.

## `maxAiBudgetUsd` (type: `number`):

Pre-request estimate using your configured token prices. Unknown usage reserves the full estimated allowance. This is not an Anthropic account spending cap.

## `inputUsdPerMillion` (type: `number`):

Set your actual Anthropic input-token price; used only for estimated budget control.

## `outputUsdPerMillion` (type: `number`):

Set your actual Anthropic output-token price; used only for estimated budget control.

## Actor input object example

```json
{
  "sourceMode": "ted-search",
  "companyProfile": {
    "name": "Example security integrator",
    "services": [
      "camera installation",
      "access control"
    ],
    "cpvCodes": [
      "35120000",
      "32323500"
    ],
    "countries": [],
    "facts": [
      {
        "id": "services",
        "text": "We install and maintain video surveillance systems."
      },
      {
        "id": "authorization",
        "text": "We do not hold an ANRE C1A authorization."
      }
    ]
  },
  "notices": [],
  "noticeIds": [],
  "cpvCodes": [
    "35120000",
    "32323500"
  ],
  "publishedFrom": "",
  "maxNotices": 10,
  "pageSize": 10,
  "maxPages": 2,
  "maxLots": 30,
  "maxXmlDocuments": 5,
  "mode": "snapshot",
  "stateStoreId": "",
  "analysisMode": "rules",
  "anthropicModel": "claude-sonnet-5-5",
  "maxAiLots": 3,
  "maxAiBudgetUsd": 0.25,
  "inputUsdPerMillion": 2,
  "outputUsdPerMillion": 10
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "companyProfile": {
        "name": "Example security integrator",
        "services": [
            "camera installation",
            "access control"
        ],
        "cpvCodes": [
            "35120000",
            "32323500"
        ],
        "countries": [],
        "facts": [
            {
                "id": "services",
                "text": "We install and maintain video surveillance systems."
            },
            {
                "id": "authorization",
                "text": "We do not hold an ANRE C1A authorization."
            }
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("prixodik/tender-supplier-fit").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 = { "companyProfile": {
        "name": "Example security integrator",
        "services": [
            "camera installation",
            "access control",
        ],
        "cpvCodes": [
            "35120000",
            "32323500",
        ],
        "countries": [],
        "facts": [
            {
                "id": "services",
                "text": "We install and maintain video surveillance systems.",
            },
            {
                "id": "authorization",
                "text": "We do not hold an ANRE C1A authorization.",
            },
        ],
    } }

# Run the Actor and wait for it to finish
run = client.actor("prixodik/tender-supplier-fit").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 '{
  "companyProfile": {
    "name": "Example security integrator",
    "services": [
      "camera installation",
      "access control"
    ],
    "cpvCodes": [
      "35120000",
      "32323500"
    ],
    "countries": [],
    "facts": [
      {
        "id": "services",
        "text": "We install and maintain video surveillance systems."
      },
      {
        "id": "authorization",
        "text": "We do not hold an ANRE C1A authorization."
      }
    ]
  }
}' |
apify call prixodik/tender-supplier-fit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,prixodik/tender-supplier-fit"
        }
    }
}
```

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/RDuP6BCGeHGvmepcr/builds/X5Fip6uwiHOLmaOPU/openapi.json
