# BOAMP Tenders Scraper (French public procurement, normalized) (`islandata/boamp-tenders-fr`) Actor

French public procurement notices from BOAMP (DILA open data) as clean JSON: one row per notice with CPV decoded (FR+EN), estimated and awarded values, deadline with days left, lots, winners, departments. Filter by department, family, kind, keywords; 'only new' mode. No personal data.

- **URL**: https://apify.com/islandata/boamp-tenders-fr.md
- **Developed by:** [Radu Coman](https://apify.com/islandata) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 tender notices

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## BOAMP Tenders Scraper: French public procurement, normalized for people and AI agents

**Every public procurement notice published in France's official bulletin (BOAMP, Bulletin officiel des annonces des marchés publics) as one clean JSON row: buyer, departments, CPV codes decoded in French and English, estimated and awarded values, deadline with days left, lots, winners.** Export French public tenders (appels d'offres, avis de marché, MAPA) and awards to JSON, CSV or Excel, set up a daily veille by département and CPV, and feed Google Sheets, n8n, Make or an AI agent.

BOAMP publishes about 600 notices a day: EU-level tenders (JOUE), national tenders between 90 k€ and the EU thresholds, and small adapted-procedure contracts (MAPA) that never reach the EU's TED. DILA exposes them as open data, but the useful details sit inside a nested XML-turned-JSON blob in three different formats (eForms UBL, FNSimple, MAPA). This Actor reads all three and returns one flat, predictable schema.

### Who uses it

- **Bid teams and consultants** in France who need every new avis de marché in their départements and sectors, every morning, in Excel or Google Sheets.
- **Companies selling to French public bodies** who want to know which contracts were awarded, to whom and for how much (résultats de marché).
- **Analysts** comparing procurement volumes by département, procedure and CPV.
- **AI agents and automations** (n8n, Make, Zapier, MCP) that need flat JSON instead of three nested XML formats.

### What you get per row

| Block | Fields |
|---|---|
| identity | `idweb`, BOAMP page URL, publication date, end of publication, family (JOUE, FNS, MAPA, DSP) and label, notice kind (Avis de marché, Résultat de marché, Rectificatif…), status, procedure, market types |
| text | `title`, `description` |
| `buyer` | name, city, postal code, NUTS region, SIRET, buyer profile URL |
| location | `departments` (codes), `placeNuts` (places of performance) |
| classification | `descriptors` (BOAMP's own thesaurus), `cpv` with `labelFr` and `labelEn`, `cpvDivisions` |
| `lots` | title, estimated value, currency, deadline; `lotCount` |
| `value` | estimated, awarded, framework maximum, currency |
| `deadline` | date, date-time, **daysLeft** |
| `award` | winners (`titulaire`), number of tenders received |
| extras | related notices, document links, data format used, source attribution, scrape timestamp |

No contact persons, phone numbers or emails are extracted, in line with the dataset's personal-data conditions.

### Input

- **Departments**: codes such as `75`, `13`, `69`, `2A`, `971`. Empty = all of France.
- **Families**: EU-level, national, MAPA, public service delegations. Empty = all.
- **Notice kind**: open tenders (default), awards, corrections…
- **Market type**: services, works, supplies.
- **Keywords**: full-text search.
- **Published in the last N days** (default 3) or explicit dates.
- **Max items** (default 500) and **Only new since last run** (monitoring mode).

```json
{ "departments": ["75", "92", "93", "94"], "natures": ["Avis de marché"], "marketTypes": ["SERVICES"], "publishedSinceDays": 7 }
```

```json
{ "natures": ["Résultat de marché"], "publishedFrom": "2026-10-01", "publishedTo": "2026-10-31", "maxItems": 5000 }
```

### Pricing

Pay per event: **$0.002 per notice row**, charged only after the row is saved. No charge for platform usage. A daily monitor of all open tenders in France (about 350 rows per day) costs about $0.70 per day; one department costs cents.

### Quality and limits

- Every row is validated against a strict schema before it is saved; invalid rows are logged and never charged.
- Tested on 240 real notices across the three data formats: CPV present on 100 % of EU-level and 85 %+ of national notices (MAPA notices carry no CPV in the source), deadline on more than 90 %, buyer city on more than 95 %.
- Awarded amounts come from the eForms result block when the buyer published them; many award notices publish the winner without an amount.
- The source API serves at most 10,000 rows per query; for larger extractions, split by dates.
- Buyer profile and document links are kept only when they are real URLs.

### Attribution and reuse

Data: BOAMP, DILA (Direction de l'information légale et administrative), published as open data on data.gouv.fr under the Licence Ouverte / Open Licence. Keep the attribution in `meta.source` when you redistribute.

### How to use it

**In the Apify Console**: choose départements and notice kinds, press Start, download the dataset as JSON, CSV or Excel.

**From your code (Apify API)**:

```bash
curl -X POST "https://api.apify.com/v2/acts/islandata~boamp-tenders-fr/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"departments": ["75"], "natures": ["Avis de marché"], "publishedSinceDays": 1}'
```

**In n8n, Make or Zapier**: use the Apify node or app, pick `islandata/boamp-tenders-fr`, schedule it daily with `onlyNew: true` and map the rows to Google Sheets, Airtable, Notion, Slack or email: a complete veille marchés publics in three nodes.

**With an AI agent (MCP)**: connect the agent to `https://mcp.apify.com?tools=islandata/boamp-tenders-fr`. Flat JSON, small rows: the agent calls it with a département list and `onlyNew: true`, then ranks by `value.estimated` and `deadline.daysLeft`.

### FAQ

**Is this the official BOAMP API?** It uses the official open-data API published by DILA on data.gouv.fr and returns its data normalized. No account or key is needed.

**Does it include MAPA (small adapted-procedure contracts)?** Yes. MAPA notices never reach the EU's TED and are the main reason to read BOAMP directly. Note that the source publishes no CPV for most MAPA notices.

**Can I get awards (résultats de marché)?** Yes: `natures: ["Résultat de marché"]` returns award notices with the winner (titulaire), the number of tenders received and, when published, the amount.

**Does it include contact persons?** No. Buyer organisations are public bodies and are included; contact names, phones and emails are not extracted, in line with the dataset's conditions.

### Français : les marchés publics du BOAMP en JSON, CSV ou Excel

**Chaque avis publié au BOAMP (appels d'offres, avis de marché, MAPA, résultats de marché, délégations de service public) devient une ligne JSON propre : acheteur, départements, codes CPV décodés en français et en anglais, montants estimés et attribués, date limite avec jours restants, lots, attributaires.** Exportez les marchés publics vers Excel ou Google Sheets, mettez en place une veille quotidienne par département et par CPV, et connectez-la à n8n, Make ou à un agent IA.

**Pour qui** : équipes de réponse aux appels d'offres, consultants en marchés publics, entreprises qui vendent aux collectivités et à l'État, analystes, automatisations et agents IA.

**Entrée** : départements (`75`, `13`, `69`, `2A`, `971`…), familles (JOUE, national, MAPA, DSP), nature de l'avis (avis de marché, résultat, rectificatif), type de marché (services, travaux, fournitures), mots-clés, période de publication, mode « nouveaux uniquement ».

**Prix** : 0,002 $ par ligne enregistrée, sans frais de plateforme. La veille quotidienne de tous les appels d'offres ouverts en France (environ 350 lignes par jour) coûte environ 0,70 $ par jour ; un seul département, quelques centimes.

**Source et réutilisation** : données BOAMP de la DILA, publiées en open data sur data.gouv.fr sous Licence Ouverte. Aucune donnée personnelle (ni noms de contact, ni téléphones, ni e-mails). Conservez la mention de source `meta.source` en cas de rediffusion.

# Actor input Schema

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

French department codes, e.g. 75 (Paris), 13 (Bouches-du-Rhône), 69 (Rhône), 2A/2B (Corse), 971-976 (overseas). Empty = all of France.

## `families` (type: `array`):

Empty = all families.

## `natures` (type: `array`):

Empty = all kinds. Default: open tenders.

## `marketTypes` (type: `array`):

Empty = all.

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

Free-text search over the notice (title, buyer, descriptors), e.g. "photovoltaïque" or "logiciel".

## `publishedSinceDays` (type: `integer`):

Default 3. France publishes about 600 notices per day.

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

Optional explicit start date; overrides the N days setting.

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

Optional end date.

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

Stop after this many notices. Each row is one paid 'result' event. The source API serves at most 10,000 rows per query; narrow the dates for more.

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

Remembers the notice ids already returned by this Actor (per account) and skips them.

## Actor input object example

```json
{
  "departments": [
    "75"
  ],
  "families": [],
  "natures": [
    "Avis de marché"
  ],
  "marketTypes": [],
  "keywords": "",
  "publishedSinceDays": 3,
  "maxItems": 500,
  "onlyNew": false
}
```

# Actor output Schema

## `rows` (type: `string`):

One row per notice: buyer, departments, CPV with labels, values, deadline, lots, winners.

## `rowsCsv` (type: `string`):

No description

## `runSummary` (type: `string`):

Filter used, counts, data formats and run economics.

# 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 = {
    "departments": [
        "75"
    ],
    "natures": [
        "Avis de marché"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("islandata/boamp-tenders-fr").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 = {
    "departments": ["75"],
    "natures": ["Avis de marché"],
}

# Run the Actor and wait for it to finish
run = client.actor("islandata/boamp-tenders-fr").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 '{
  "departments": [
    "75"
  ],
  "natures": [
    "Avis de marché"
  ]
}' |
apify call islandata/boamp-tenders-fr --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,islandata/boamp-tenders-fr"
        }
    }
}
```

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/IFNJYtyi2rLjMlvQ4/builds/MbVEZjFe8BBiFNWXH/openapi.json
