# BOAMP Public Procurement Scraper — French Government Tenders (`apikiy/boamp-scraper`) Actor

Scrape French public procurement notices from BOAMP open data by keyword, department, procedure type and estimated value.

- **URL**: https://apify.com/apikiy/boamp-scraper.md
- **Developed by:** [Julien ApiKiy](https://apify.com/apikiy) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.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/platform/actors/running/actors-in-store#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

## French & EU Public Procurement Scraper — BOAMP + TED, Veille Marchés Publics 🇫🇷🇪🇺

Search **and monitor** public procurement notices (*marchés publics* / *appels d'offres*) from **two official sources in one Actor**:

- **BOAMP** — the French national tender bulletin ([boamp.fr](https://www.boamp.fr), DILA open data)
- **TED** — Tenders Electronic Daily, the **EU-wide** procurement journal (all 27 member states + EEA)

Filter by **CPV code**, keyword, department, buyer, country, publication date and deadline. Turn on **monitor mode (veille)** and the Actor returns only the *new* tenders on each run — and pushes them straight to **Slack or a webhook**. Perfect for B2G sales, bid teams, and market intelligence.

### Why this Actor

- 🇫🇷🇪🇺 **National + European in one run** — combine BOAMP (France) and TED (all EU) with a single `sources` setting.
- 🎯 **CPV filtering** — target exactly your sector (e.g. `72` = IT, `45` = construction, `79` = business services). Full 8-digit codes or category prefixes.
- 🔔 **Monitor mode (veille)** — scheduled daily watch that emits **only new tenders**, so you never pay for or re-read the same notice twice.
- 📩 **Alerts** — new tenders delivered to **Slack** and/or a **generic webhook** (email services, Make, n8n, your CRM…).
- 🗓️ **Date & deadline filters** — exclude tenders that already closed; focus on a publication window.
- 🏛️ **Rich, structured, deduplicated data** — buyer, location, country, estimated value, procedure type, CPV codes, source URL.
- ⚖️ **100% legal** — BOAMP and TED are official public open data, no authentication required.

### Input

| Field | Description |
|---|---|
| `sources` | Which sources to search: `boamp` (France), `ted` (EU), or both. Default `["boamp"]`. |
| `keywords` | Search keywords in the tender object (e.g. `informatique`, `bâtiment`). Empty = all. |
| `cpvCodes` | CPV code(s) — full 8-digit (`45432113`) or prefixes for whole categories (`45`, `72`, `79`). A tender matches if any of its CPV codes starts with any value you give. |
| `countries` | **TED only** — restrict to buyer countries by ISO3 code (`FRA`, `DEU`, `ESP`, `ITA`…). Empty = all EU. Ignored by BOAMP. |
| `department` | **BOAMP only** — French department code (e.g. `75` = Paris, `54`). Empty = all France. |
| `buyer` | Buying organization name (e.g. `Angers`, `CHU`, `Ministère`). Partial, accent-insensitive. |
| `procedureType` | `all`, `ouvert`, `restreint`, `negocie`, `dialogue_competitif`, `partenariat_innovation`. |
| `publishedFrom` / `publishedTo` | Publication date window, `YYYY-MM-DD`. |
| `deadlineFrom` / `deadlineTo` | Response-deadline window, `YYYY-MM-DD`. Use `deadlineFrom = today` to skip closed tenders. |
| `minEstimatedValue` | Minimum estimated value in EUR. |
| `maxResults` | Max matching tenders to return (1–10,000). |
| `monitorMode` | When ON, remembers tenders from previous runs and returns **only the new ones**. |
| `monitorName` | Label for the watch (e.g. `IT-Paris`). Each name keeps its own separate memory, so you can run several independent watches. |
| `slackWebhookUrl` | Optional. In monitor mode, posts a summary of new tenders to this Slack Incoming Webhook. |
| `webhookUrl` | Optional. In monitor mode, POSTs `{monitorName, count, tenders[]}` to this URL. |

### Typical setups

**One-off sourcing — all IT tenders in Paris, still open:**

```json
{ "cpvCodes": ["72"], "department": "75", "deadlineFrom": "2026-07-08", "maxResults": 200 }
```

**Pan-EU sourcing — IT tenders across France + Germany + Spain (TED):**

```json
{ "sources": ["ted"], "cpvCodes": ["72"], "countries": ["FRA", "DEU", "ESP"], "maxResults": 200 }
```

**Daily veille with Slack alerts, national + EU** (schedule this on Apify to run every morning):

```json
{
  "sources": ["boamp", "ted"],
  "cpvCodes": ["45", "71"],
  "monitorMode": true,
  "monitorName": "BTP-national-EU",
  "slackWebhookUrl": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
  "maxResults": 500
}
```

The first run seeds the memory; every run after that returns and alerts **only newly published tenders**.

### Output

```json
{
  "id": "25-88236",
  "title": "Mission d'audits internes et assistance technique...",
  "description": "Les prestations du marché concernent...",
  "organization": "SID - Expertise et production nationale",
  "organizationCity": "Versailles",
  "organizationDepartment": "78",
  "publicationDate": "2026-06-01T00:00:00.000Z",
  "deadline": "2026-07-01T12:00:00.000Z",
  "estimatedValue": 237500,
  "currency": "EUR",
  "procedureType": "Procédure Ouverte",
  "cpvCodes": ["71314200"],
  "cpvLabels": [],
  "url": "https://www.boamp.fr/pages/avis/?q=idweb:25-88236",
  "scrapedAt": "2026-07-08T12:00:00.000Z"
}
```

Every item carries a `source` (`"boamp"` or `"ted"`) and a `country` (ISO3).

Notes:

- `estimatedValue`/`cpvCodes` come from the notice itself for tenders published under the EU eForms format (2024+). Older BOAMP notices fall back to BOAMP's own descriptor codes for `cpvCodes`/`cpvLabels`, and `estimatedValue` is `null` when the source doesn't disclose it.
- CPV **prefix** filtering is applied to the extracted codes, so it works across BOAMP and TED.
- **TED specifics:** `deadline`, `description`, `organizationCity` and `procedureType` are not exposed by the TED search API and come back `null`; use the `url` to open the full notice. TED CPV-prefix filtering scans the newest notices within your `maxResults` window, so raise `maxResults` for very narrow sectors.

### Pricing

Pay-per-use, no monthly fees. In monitor mode you're only charged for **new** tenders, keeping a daily watch cheap.

Data source: BOAMP / DILA public open data — free, official, no authentication.

# Actor input Schema

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

Which procurement sources to search. 'boamp' = French national tenders (BOAMP). 'ted' = EU-wide tenders (TED / Tenders Electronic Daily). Select both to cover national + European in one run.

## `keywords` (type: `string`):

Search keywords in the tender object (e.g. 'informatique', 'bâtiment', 'transport'). Leave empty for all.

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

For the TED (EU) source only: restrict to buyer countries by 3-letter ISO code (e.g. 'FRA', 'DEU', 'ESP', 'ITA', 'BEL'). Leave empty for all EU countries. Ignored by BOAMP (always France).

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

Filter by CPV code(s). Accepts full 8-digit codes (e.g. '45432113') or prefixes for whole categories (e.g. '45' = construction, '72' = IT services, '79' = business services). A tender matches if any of its CPV codes starts with any value you provide.

## `department` (type: `string`):

Filter by French department code (e.g. '75' for Paris, '54' for Meurthe-et-Moselle). Leave empty for all of France.

## `buyer` (type: `string`):

Filter by the buying organization name (e.g. 'Angers', 'Ministère', 'CHU'). Partial, accent-insensitive match.

## `procedureType` (type: `string`):

Filter by procedure type

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

Only tenders published on or after this date (e.g. '2026-01-01').

## `publishedTo` (type: `string`):

Only tenders published on or before this date.

## `deadlineFrom` (type: `string`):

Only tenders whose response deadline is on or after this date. Useful to exclude tenders that already closed.

## `deadlineTo` (type: `string`):

Only tenders whose response deadline is on or before this date.

## `minEstimatedValue` (type: `number`):

Minimum estimated value in EUR. Leave empty for no filter. Notices that don't disclose a structured estimated value are excluded when this is set.

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

Maximum number of matching tenders to return.

## `monitorMode` (type: `boolean`):

When ON, remembers tenders seen on previous runs and returns ONLY the new ones each time — ideal for a scheduled daily watch. New tenders can trigger Slack / webhook alerts below.

## `monitorName` (type: `string`):

A label for this watch (e.g. 'IT-Paris'). Each named monitor keeps its own separate 'already seen' memory, so you can run several independent watches from the same Actor.

## `slackWebhookUrl` (type: `string`):

Optional. In monitor mode, posts a summary of the new tenders to this Slack Incoming Webhook.

## `webhookUrl` (type: `string`):

Optional. In monitor mode, POSTs a JSON payload {monitorName, count, tenders\[]} of the new tenders to this URL (for email services, Make, n8n, your own backend, etc.).

## Actor input object example

```json
{
  "sources": [
    "boamp"
  ],
  "keywords": "",
  "countries": [],
  "cpvCodes": [
    "72"
  ],
  "department": "",
  "buyer": "",
  "procedureType": "all",
  "publishedFrom": "",
  "publishedTo": "",
  "deadlineFrom": "",
  "deadlineTo": "",
  "maxResults": 100,
  "monitorMode": false,
  "monitorName": ""
}
```

# 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 = {
    "sources": [
        "boamp"
    ],
    "cpvCodes": [
        "72"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("apikiy/boamp-scraper").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 = {
    "sources": ["boamp"],
    "cpvCodes": ["72"],
}

# Run the Actor and wait for it to finish
run = client.actor("apikiy/boamp-scraper").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 '{
  "sources": [
    "boamp"
  ],
  "cpvCodes": [
    "72"
  ]
}' |
apify call apikiy/boamp-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=apikiy/boamp-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/eIVguV41QChNW14bG/builds/n1pD95JOPYAcamSTw/openapi.json
