# Tender Relevance Scorer – AI Bid/No-Bid for EU Tenders (`nebulosa/tender-relevance-scorer`) Actor

Describe what you sell (keywords, CPV codes, countries, budget) and get public tenders from TED (EU), BOAMP (France) and BZP (Poland) ranked 0-100 by fit, with plain-language reasons. Rules + AI. Can also score any tender dataset.

- **URL**: https://apify.com/nebulosa/tender-relevance-scorer.md
- **Developed by:** [Nebulosa Data](https://apify.com/nebulosa) (community)
- **Categories:** Business, AI, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 results

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

## Tender Relevance Scorer – AI Bid/No-Bid for EU Tenders

Describe what your company sells (keywords, CPV codes, countries, budget) and this actor returns current public tenders from TED, BOAMP and Polish BZP ranked 0–100 by how well they fit, each with a plain-language reason. It turns hundreds of notices into a short bid/no-bid list; it can also score any tender dataset you already have.

Stop reading hundreds of tender notices by hand. Describe what your company sells (keywords, **CPV codes**, countries, budget) and this actor pulls fresh public tenders from **TED (Tenders Electronic Daily, all EU/EEA)**, **BOAMP (France)** and **BZP / e-Zamówienia (Poland)**, then ranks every notice **0–100 by fit**, with plain-language reasons you can check.

Built for SMEs, bid managers, tender consultants and B2G sales teams who want a short list, not a firehose.

### How the score works

Each notice gets a transparent **rule score** that always works, with no AI needed:

| Signal | Points |
|---|---|
| Your keywords in the title / description | up to 40 |
| CPV code: exact match / shared prefix | up to 30 |
| Buyer country in your list | 10 |
| Estimated value inside your budget | 10 |
| Time left before the deadline | up to 10 |
| Exclude keyword found | −40 |

When AI scoring is on, the best candidates also go to **TypeSafe Jev**, a decision model that returns a calibrated relevance score and a bid probability. The final score is 40% rules and 60% AI, and the output shows both, so you can always see why a notice ranks where it does.

### Output (one row per notice, best first)

```json
{
  "rank": 1, "score": 95, "verdict": "strong fit",
  "reasons": ["AI relevance 100/100 (confidence 100%)", "Keyword in title: catering, comedor escolar", "CPV match 55524000 (profile 55520000)", "23 days to deadline"],
  "source": "TED", "title": "Spain – School catering services – Servicio de comedor escolar",
  "buyerName": "…", "country": "ESP", "cpvCodes": ["55524000"], "estimatedValue": 1080000, "currency": "EUR",
  "deadline": "2026-11-12", "ruleScore": 95, "aiRelevance": 100, "aiBidProbability": 0.81, "url": "https://ted.europa.eu/…"
}
```

### Example input

```json
{
  "buyerDescription": "Spanish catering company running school and hospital canteens.",
  "keywords": ["catering", "comedor escolar", "restauration scolaire", "żywienie"],
  "cpvCodes": ["55520000", "555"],
  "countries": ["ESP", "FRA", "POL"],
  "budgetMax": 5000000,
  "sources": ["TED", "BOAMP", "BZP"],
  "daysBack": 14,
  "minScore": 50
}
```

**Already have tender data?** Set `datasetId` to the dataset of any tender scraper (for example our *EU Tenders Scraper (TED)*, *BOAMP Scraper* or *Przetargi BZP Scraper*) and the actor scores those notices instead of searching.

### Daily bid/no-bid alerts

Schedule the actor daily with `daysBack: 1` and `minScore: 60`, then connect Slack, email, Google Sheets, Make, Zapier or n8n. You get only the tenders worth a look.

### Use cases

- **Bid managers** getting a short, ranked list instead of hundreds of raw notices.
- **IT, consulting, construction SMEs** screening EU tenders every morning.
- **Tender consultants** running one task per client profile.

### Pricing

**$20 per 1,000 scored notices** saved (pay-per-event, from 4 Nov 2026), plus a tiny per-run start fee. Notices below your `minScore` are not saved and not charged.

### FAQ

**Which countries?** TED covers above-threshold tenders from all 27 EU countries plus EEA. BOAMP adds French national tenders and BZP adds Polish ones.

**Is the data official?** Yes. It comes from the official public APIs: TED API v3 (Publications Office of the EU), BOAMP open data (DILA, Licence Ouverte) and e-Zamówienia (UZP). Personal e-mails and phone numbers in descriptions are removed.

**Can I trust the AI?** Every row shows the rule-based reasons and the AI confidence. With AI switched off, the ranking is fully deterministic.

### More questions

**How is relevance computed?**
AI relevance to your description + keyword hits in title/description + days to deadline. Every row has human-readable reasons.

**Which sources?**
TED (EU) and BZP (Poland) live APIs.

**Can I turn AI off?**
Yes, set useAi: false for keyword-only scoring.

### Pairs with

- [ted-eu-tenders](https://apify.com/nebulosa/ted-eu-tenders)
- [boamp-france-tenders](https://apify.com/nebulosa/boamp-france-tenders)
- [poland-bzp-tenders](https://apify.com/nebulosa/poland-bzp-tenders)
- [boe-auction-triage](https://apify.com/nebulosa/boe-auction-triage)

### Reports from Revunda

Need the results as a ready dataset or a one-off report instead of running the actor? See [Revunda Data](https://revunda.xyz/data/) ([ES](https://revunda.xyz/data/es/), [RU](https://revunda.xyz/data/ru/)) for prices and sample outputs.

### More questions (FAQ)

#### How do I find relevant public tenders for my company?

Enter your offer as keywords and CPV codes, choose countries and a budget range, and sort the output by score.

#### Can it score tenders from another scraper?

Yes. Pass a dataset ID with tender rows and the actor scores those instead of fetching new ones.

#### Is the score a guarantee of winning?

No. It ranks fit with your description so you read the best notices first; eligibility and bid decisions are yours.

# Actor input Schema

## `buyerDescription` (type: `string`):

Plain-language description of your company and what you can deliver. Used by the AI scorer.

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

Words that describe what you sell, in the tender languages you target (e.g. catering, comedor escolar, restauration, żywienie).

## `excludeKeywords` (type: `array`):

Notices containing any of these lose 40 points (e.g. construction, obras).

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

Full codes (55520000) or prefixes (555, 15). Exact matches score highest, shared prefixes score less.

## `countries` (type: `array`):

e.g. ESP, PRT, FRA, POL. Empty = any.

## `budgetMin` (type: `integer`):

Optional.

## `budgetMax` (type: `integer`):

Optional.

## `sources` (type: `array`):

Where to search. TED covers all EU/EEA above-threshold tenders. BOAMP = France, BZP = Poland national tenders.

## `frenchDepartments` (type: `array`):

e.g. 75, 69, 13.

## `daysBack` (type: `integer`):

Days back.

## `onlyOpen` (type: `boolean`):

Skip awards and notices whose deadline has passed.

## `datasetId` (type: `string`):

ID of a dataset produced by any tender scraper (e.g. our TED/BOAMP/BZP actors). When set, sources are not searched; the notices in that dataset are scored.

## `useAi` (type: `boolean`):

Adds an AI relevance judgement on top of the rules. Off = rules only (no AI fee).

## `typesafeApiKey` (type: `string`):

With your own key you are not charged the AI fee and the per-run AI cap is higher.

## `maxAiCalls` (type: `integer`):

The best rule-scored candidates get AI scoring. Max 500 with our key.

## `minScore` (type: `integer`):

You only pay for notices at or above this score.

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

Max results.

## `maxCandidatesPerSource` (type: `integer`):

How many notices to pull and score per source (not charged).

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

Language for TED titles (falls back to English).

## Actor input object example

```json
{
  "buyerDescription": "We are a Spanish catering company running school and hospital canteens and supplying prepared meals.",
  "keywords": [
    "catering",
    "comedor escolar",
    "school meals"
  ],
  "cpvCodes": [
    "55520000",
    "55523100"
  ],
  "countries": [
    "ESP"
  ],
  "sources": [
    "TED"
  ],
  "daysBack": 14,
  "onlyOpen": true,
  "useAi": true,
  "maxAiCalls": 200,
  "minScore": 40,
  "maxResults": 100,
  "maxCandidatesPerSource": 300,
  "language": "eng"
}
```

# Actor output Schema

## `results` (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 = {
    "buyerDescription": "We are a Spanish catering company running school and hospital canteens and supplying prepared meals.",
    "keywords": [
        "catering",
        "comedor escolar",
        "school meals"
    ],
    "cpvCodes": [
        "55520000",
        "55523100"
    ],
    "countries": [
        "ESP"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nebulosa/tender-relevance-scorer").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 = {
    "buyerDescription": "We are a Spanish catering company running school and hospital canteens and supplying prepared meals.",
    "keywords": [
        "catering",
        "comedor escolar",
        "school meals",
    ],
    "cpvCodes": [
        "55520000",
        "55523100",
    ],
    "countries": ["ESP"],
}

# Run the Actor and wait for it to finish
run = client.actor("nebulosa/tender-relevance-scorer").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 '{
  "buyerDescription": "We are a Spanish catering company running school and hospital canteens and supplying prepared meals.",
  "keywords": [
    "catering",
    "comedor escolar",
    "school meals"
  ],
  "cpvCodes": [
    "55520000",
    "55523100"
  ],
  "countries": [
    "ESP"
  ]
}' |
apify call nebulosa/tender-relevance-scorer --silent --output-dataset

```

## MCP server setup

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

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/fGXJF00oL4TUYWsll/builds/aGnCa6OrJ8rsHyenu/openapi.json
