# Public Tenders France & EU: BOAMP + TED Alerts (`datacraft-studio/public-tenders-france-eu`) Actor

Daily alerts for new public tenders from BOAMP (France) and TED (EU) in one feed. Filter by keywords, CPV codes, departments, NUTS regions and countries. EU notices published on both sources are delivered once, and earlier runs are skipped. Pay per new tender.

- **URL**: https://apify.com/datacraft-studio/public-tenders-france-eu.md
- **Developed by:** [Yahya Nouali](https://apify.com/datacraft-studio) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 new 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/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

## Public Tenders France & EU: BOAMP + TED Alerts

Get every new public tender that matters to you from **BOAMP** (Bulletin officiel des annonces des marchés publics, France) and **TED** (Tenders Electronic Daily, the EU Official Journal) in one clean dataset. Filter by keywords, CPV codes, French departments, NUTS regions, buyer countries and notice type. Schedule it daily and receive only the notices you have not seen yet.

- **Two official sources, one feed.** EU-threshold French notices are published on both BOAMP and TED. The Actor reads the eForms notice UUID and delivers each notice **once**.
- **Only new notices.** With `onlyNew` on, notices already delivered by an earlier run are skipped and not charged.
- **Ready for sales teams.** Buyer, city, department, CPV, deadline, days left, estimated value, winners on award notices and the official link.
- **Official open data.** Licence Ouverte 2.0 (BOAMP) and the EU reuse policy (TED). No login, no scraping of private portals.

### Use cases

- Daily tender alerts for a construction, cleaning, IT, consulting or catering company.
- Business development: feed new calls for tenders into a CRM, Google Sheets, Slack or Teams.
- Market intelligence: who buys what, at which value, and who wins (`noticeKinds: ["result"]`).
- Bid consultants watching several clients, with one `stateStoreName` per client profile.

### Input

| Field | What it does | Default |
| --- | --- | --- |
| `keywords` | Any of these words or phrases must appear in the title or description. Accent and case insensitive, whole words. `nettoy*` matches word endings. | all notices |
| `excludeKeywords` | Drop notices containing any of these. | none |
| `cpvCodes` | CPV codes or prefixes. `72000000` = every IT service. | all |
| `sources` | `BOAMP`, `TED` or both. | both |
| `tedCountries` | Buyer countries for TED, ISO codes. `ALL` for all of Europe. | `FR` |
| `departments` | French departments (`75`, `69`, `2A`, `974`). | all |
| `nutsCodes` | NUTS prefixes of the place of performance (`FR1`, `BE1`). | all |
| `noticeKinds` | `competition`, `result`, `planning`, `change` or `all`. | `competition` |
| `contractTypes` | `works`, `supplies`, `services`. | all |
| `lookbackDays` | Days of publications, ending today (Europe/Paris). | 2 |
| `dateFrom`, `dateTo` | Fixed publication window, max 31 days. | |
| `onlyNew` | Skip notices delivered in earlier runs. | `true` |
| `stateStoreName` | Key-value store that remembers delivered notices. | `public-tenders-state` |
| `maxItems` | Hard cap on delivered (and charged) tenders. | 500 |
| `preferredLanguage` | `EN` or `FR` for TED titles and links. | `EN` |

Example: IT services in France, Belgium and Luxembourg.

```json
{
  "cpvCodes": ["72000000", "48000000"],
  "tedCountries": ["FR", "BE", "LU"],
  "noticeKinds": ["competition"],
  "lookbackDays": 2,
  "stateStoreName": "tenders-it-services",
  "maxItems": 200
}
```

Example: cleaning contracts in Paris and the inner suburbs.

```json
{
  "keywords": ["nettoyage", "propreté", "entretien des locaux"],
  "departments": ["75", "92", "93", "94"]
}
```

### Output

One row per new tender, then one `run_summary` row. Views **Tenders** and **Run summary** are available in the dataset.

```json
{
  "recordType": "tender",
  "source": "BOAMP",
  "noticeId": "26-103006",
  "noticeUuid": null,
  "title": "Prestations de support de maintenance corrective et évolutive sur les produits SAP BO",
  "buyerName": "Conseil régional Auvergne-Rhône-Alpes",
  "buyerCity": "Lyon Cedex 2",
  "buyerCountry": "FRA",
  "departments": ["69"],
  "noticeKind": "competition",
  "contractTypes": ["services"],
  "cpvCodes": ["72261000"],
  "mainCpv": "72261000",
  "publicationDate": "2026-10-23",
  "deadline": "2026-11-30T10:00:00+00:00",
  "daysToDeadline": 36,
  "estimatedValue": 600000,
  "currency": "EUR",
  "winners": [],
  "matchedKeywords": [],
  "url": "https://www.boamp.fr/pages/avis/?q=idweb:26-103006",
  "licence": "Licence Ouverte 2.0 (DILA, BOAMP)",
  "charged": true
}
```

Every field can be `null` or empty when the source does not publish it. National BOAMP notices below EU thresholds often have no CPV code; a `cpvCodes` filter excludes them. The full field list is in the dataset schema.

### Pricing

Pay per event: **$0.004 per new tender delivered** ($4 per 1,000).

Free: notices already delivered by an earlier run, the second copy of an EU notice found on both sources, notices removed by your filters, failed items and the summary row. `maxItems` caps the cost of each run.

| Run | Charge |
| --- | --- |
| Daily IT alert, about 30 new notices | $0.12 |
| Same schedule the next day, nothing new | $0 |
| All French calls for tenders over 2 days, about 900 notices | $3.60 |

### Schedule it

Create a schedule in Apify with cron `0 7 * * 1-6` and time zone `Europe/Paris`. Keep `lookbackDays` at 2 and `onlyNew` on: late or delayed publications are caught the next morning without paying twice. Use a different `stateStoreName` for each alert profile.

### Automate with Make, Zapier or n8n

Put the **Apify token** in the integration credentials. Never paste it into the Actor input. Synchronous calls that return dataset items time out at 300 seconds; a filtered daily alert finishes well inside that. For very wide windows, schedule the Actor in Apify and read the dataset afterwards.

#### Make

App **Apify**, module **Run an Actor and Get Dataset** (or **Run an Actor**, then **Get Dataset Items**).

1. Start from a scheduled scenario at 07:00.
2. **Run an Actor and Get Dataset**, Actor `public-tenders-france-eu`, input from [examples/input.it-services.json](examples/input.it-services.json).
3. Filter: `recordType` = `tender`.
4. Add a row in Google Sheets, post to Slack or Teams, or create a deal in your CRM. Map `title`, `buyerName`, `deadline`, `estimatedValue` and `url`.

#### Zapier

App **Apify**.

1. Trigger: Schedule by Zapier, every weekday.
2. Action: **Run Actor** with the JSON input, wait for finish.
3. Action: **Get Dataset Items**. Filter by Zapier: `recordType` is `tender`.
4. Action: Gmail, Slack or a CRM. Alternatively use the trigger **Finished Actor Run** on an Apify schedule.

#### n8n

Official node: [`@apify/n8n-nodes-apify`](https://docs.apify.com/platform/integrations/n8n). Resource **Actors**, operation **Run an Actor and Get Dataset**. Select your Apify credential, set the Actor to `public-tenders-france-eu`, paste the JSON input, then add an IF node on `recordType` = `tender` before Slack, Google Sheets or email.

### Sources and licences

| Source | Publisher | Coverage | Licence |
| --- | --- | --- | --- |
| BOAMP via the DILA open data API | Direction de l'information légale et administrative | French notices: national (MAPA over 90 k€, FNS) and EU-threshold notices | Licence Ouverte 2.0 / Etalab |
| TED API v3 | Publications Office of the European Union | EU, EEA and partner countries, EU-threshold notices | EU reuse policy, Decision 2011/833/EU |

The Actor uses the public anonymous APIs at a polite rate. Always read the official notice and documents before bidding. Notices are copied as published; the buyer's notice is authoritative.

### En français

Cet Actor livre chaque jour les **nouveaux appels d'offres publics** du **BOAMP** et du **TED** (JOUE) dans un seul fichier, sans doublon. Filtrez par mots-clés (sans accents ni majuscules), codes CPV, départements, régions NUTS et type d'avis (avis de marché, attribution, pré-information, rectificatif). Avec `onlyNew`, seuls les avis jamais livrés sont facturés, **0,004 $ par avis**. Les avis européens publiés à la fois au BOAMP et au TED sont dédoublonnés grâce à l'identifiant eForms. Données publiques sous Licence Ouverte 2.0 et politique de réutilisation de l'UE.

### Local development

```bash
npm install
npm test
npm run typecheck
npm run smoke   # live calls to BOAMP and TED, no charges
```

# Changelog

This Actor's version history is a separate document: https://apify.com/datacraft-studio/public-tenders-france-eu/changelog.md

# Actor input Schema

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

Any of these words or phrases must appear in the title or description. Accent and case insensitive, whole words; end a word with \* to match endings (nettoy\*). Leave empty for all notices.

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

Drop notices whose title or description contains any of these.

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

CPV codes or prefixes. 72000000 matches every IT service, 45210000 every building work. Trailing zeros are treated as a prefix.

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

BOAMP (French official bulletin: national and EU-threshold notices) and/or TED (EU Official Journal, all EU/EEA countries).

## `tedCountries` (type: `array`):

Buyer countries for TED, ISO codes (FR, BE, DE, LU, CH...). Use ALL for every country. Does not affect BOAMP, which is France only.

## `departments` (type: `array`):

Department codes such as 75, 69, 2A, 974. BOAMP uses the notice departments; TED uses the buyer postal code. Non-French TED notices are dropped when this is set.

## `nutsCodes` (type: `array`):

NUTS prefixes of the place of performance, e.g. FR1 (Ile-de-France), BE1, DE2. Only notices that state a NUTS code can match.

## `noticeKinds` (type: `array`):

competition (calls for tenders), result (awards, with winners), planning (prior information), change (corrigenda and modifications) or all.

## `contractTypes` (type: `array`):

works, supplies and/or services. Empty means all.

## `lookbackDays` (type: `integer`):

Civil days to include, ending today in Europe/Paris. 2 is yesterday and today. Ignored when dateFrom and dateTo are set. Maximum 31.

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

Inclusive start publication date, YYYY-MM-DD. Set both dates to replace lookbackDays. Maximum span 31 days.

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

Inclusive end publication date, YYYY-MM-DD.

## `onlyNew` (type: `boolean`):

Skip notices delivered by an earlier run that used the same state store. Keep this on for scheduled alerts.

## `stateStoreName` (type: `string`):

Named key-value store that remembers delivered notices. Use a different name per alert profile.

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

Stop after this many delivered tenders. This caps the cost of a run.

## `preferredLanguage` (type: `string`):

Language used for TED titles, buyer names and notice links when several are available.

## Actor input object example

```json
{
  "keywords": [
    "nettoyage"
  ],
  "sources": [
    "BOAMP",
    "TED"
  ],
  "tedCountries": [
    "FR"
  ],
  "noticeKinds": [
    "competition"
  ],
  "lookbackDays": 2,
  "onlyNew": true,
  "stateStoreName": "public-tenders-state",
  "maxItems": 500,
  "preferredLanguage": "EN"
}
```

# Actor output Schema

## `overview` (type: `string`):

One row per delivered tender, then a run summary.

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

Counts per source, the date window and the price that applied.

# 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 = {
    "keywords": [
        "nettoyage"
    ],
    "sources": [
        "BOAMP",
        "TED"
    ],
    "tedCountries": [
        "FR"
    ],
    "noticeKinds": [
        "competition"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datacraft-studio/public-tenders-france-eu").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 = {
    "keywords": ["nettoyage"],
    "sources": [
        "BOAMP",
        "TED",
    ],
    "tedCountries": ["FR"],
    "noticeKinds": ["competition"],
}

# Run the Actor and wait for it to finish
run = client.actor("datacraft-studio/public-tenders-france-eu").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 '{
  "keywords": [
    "nettoyage"
  ],
  "sources": [
    "BOAMP",
    "TED"
  ],
  "tedCountries": [
    "FR"
  ],
  "noticeKinds": [
    "competition"
  ]
}' |
apify call datacraft-studio/public-tenders-france-eu --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datacraft-studio/public-tenders-france-eu"
        }
    }
}
```

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/VwvCi1kGYhjb0L6JY/builds/1URXgUbZ9epYAY1xn/openapi.json
