# BOAMP · French Public Tenders by Keyword or Dept (`corent1robert/boamp-announcements-scraper`) Actor

Export official French public tenders and awards from BOAMP — buyer, CPV, amounts, contacts when published. Daily presets or paste a URL. One row per notice. Public gazette. No API key.

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

## Pricing

from $3.49 / 1,000 tender notice retrieveds

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?

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

## BOAMP · French Public Tenders

**Export official French public procurement notices from the [BOAMP](https://www.boamp.fr) gazette — calls for tenders and awards, with buyer, deadline, CPV, amounts, and contacts when published.**

Pick a daily preset, add keywords or a department, get **one row per notice**. JSON, CSV, or Excel from Apify Console.

**No login. No API key. No account on boamp.fr.**

**Built for B2G suppliers, bid desks, and market intel** — see [Who is this for?](#who-is-this-for) below.

***

### Who is this for?

| You are… | Typical goal | Suggested setup |
|----------|--------------|-----------------|
| **B2G supplier / SME** | Morning list of new calls for tenders in your trade | **Daily open tenders** · add Keywords |
| **Bid / tender desk** | Nationwide watch, then filter by department | **Open tenders — last 7 days** · Departments `75` |
| **Agency / outsourcer** | Recurring pipeline for several clients | **Keyword watch** · schedule daily · `maxResults: 0` |
| **Market intel** | Who won what, and for how much | **Recent awards** |
| **Data / ops** | Master table of gazette notices | Daily preset · raise or clear the row cap |

**What you get by default:** notice type, dates, buyer name, subject, procedure, contract kind, department, CPV when present, and a direct BOAMP link.

**Contacts and money:** email, phone, SIRET/SIREN, e-procurement URL, estimated value and award value when the gazette embeds them. Coverage is incomplete — many notices have no mailbox or no amount.

On a mixed sample of **50 notices** (16–23 Aug 2026): **~78% email/phone**, **~52% SIRET**, **~62% website**, **~78% CPV**. Award winner names appear on attribution notices only.

***

### Quick start

#### Console (2 clicks)

1. Leave **What do you need?** on **Daily open tenders**.
2. Click **Start** — export from the **Dataset** tab.

**Store Try** caps at **10** notices. If yesterday is empty (weekend / holiday), the Actor retries the **last 7 days**. Set `maxResults` to `0` for an uncapped daily job.

*Optional:* add **Keywords** (`restauration`, `voirie`) or a department (`75`).

#### Already searched on boamp.fr?

1. Copy the URL from your browser (results page or a single notice).
2. Paste into **BOAMP search URL**.
3. Click **Start** — keywords, notice type, dates, and department are extracted automatically.

***

### Ready-made examples (published tasks)

| Example | Best for |
|---------|----------|
| [Daily French open tenders](https://apify.com/corent1robert/boamp-announcements-scraper/examples/daily-french-open-tenders) | Morning bid desk |
| [Recent BOAMP award notices](https://apify.com/corent1robert/boamp-announcements-scraper/examples/recent-boamp-award-notices) | Winner / B2G intel |
| [Open tenders in Paris (75)](https://apify.com/corent1robert/boamp-announcements-scraper/examples/open-tenders-paris-75) | Île-de-France pipeline |
| [School catering tenders (restauration)](https://apify.com/corent1robert/boamp-announcements-scraper/examples/school-catering-tenders-restauration) | Keyword vertical |

***

### What does this Actor do?

BOAMP is France’s official bulletin of public procurement. This Actor lists **calls for tenders**, **award notices**, and **corrections** published there, then flattens each notice into one export row.

| Preset | What it does |
|--------|----------------|
| **Daily open tenders** | Yesterday→today · calls for tenders only |
| **Recent awards** | Yesterday→today · award / result notices |
| **Open tenders — last 7 days** | Wider bid window · calls for tenders |
| **Keyword watch** | Last 7 days · meant to be used with keywords |

***

### What data does it extract?

| Category | Fields |
|----------|--------|
| Notice | ID, type (EN + FR label), procedure, family (national / EU), status, sub-type |
| Dates | Publication, response deadline, diffusion end, planned contract start/end |
| Object | Full subject, short subject (~300 chars), descriptors, CPV, lot count |
| Money | Estimated value + currency; award value + currency when published |
| Buyer | Name, SIRET, SIREN, email, phone, website, street, city, postal code, contact title |
| E-procurement | Buyer profile URL (marche-publics portals) when published |
| Award | Unique winner names, winner count, winner SIRET when distinct, related notice ID |
| Geo | Buyer department codes; place-of-performance departments when filled |
| Link | Official BOAMP notice URL |

Raw nested gazette XML/JSON is **not** dumped into the dataset (it is huge). Useful values are harvested into the columns above.

***

### How it works

1. Apply the preset (notice type + date window).
2. Optionally narrow by keywords and/or department codes.
3. List matching notices from the public gazette.
4. Flatten each notice into one row and write it immediately (you see the Dataset fill as the run goes).
5. If a **daily** window is empty, retry the **last 7 days**.

***

### How much does it cost to scrape BOAMP?

HTTP-only gazette feed. **Pay per notice row**, plus a one-time actor-start charge.

| Scenario | Rows billed | FREE | Bronze | Silver | Gold |
|----------|-------------|------|--------|--------|------|
| 10 notices (Try) | 10 | ~$0.05 | ~$0.04 | ~$0.04 | ~$0.03 |
| 25 notices | 25 | ~$0.12 | ~$0.11 | ~$0.10 | ~$0.09 |
| 500 notices (busy day) | 500 | ~$2.50 | ~$2.25 | ~$2.00 | ~$1.75 |
| 5,000 notices | 5,000 | ~$25.00 | ~$22.45 | ~$19.95 | ~$17.45 |

*Includes actor-start (~$0.00005). Row prices fall on higher Apify plans. About **$5 / 1,000 notices** on Free.*

Residential proxy is **not** required.

***

### Is it legal to scrape BOAMP?

This Actor only reads notices the DILA publishes as **open data** (Licence Ouverte 2.0). Rows may include buyer contact details. Ensure your use complies with GDPR and French public-procurement outreach rules.

***

### Input

| Field | Console | Description |
|-------|---------|-------------|
| `runPreset` | Yes | Daily open tenders · Recent awards · Last 7 days · Keyword watch |
| `keywords` | Yes | Optional OR terms — one per line |
| `departments` | Yes | Optional department codes (`75`, `69`…) |
| `searchUrl` | Yes | Paste a boamp.fr search or notice URL |
| `maxResults` | Yes | Row cap; **0** = no numerical cap (date window and gazette size still apply) |
| `dateFrom` / `dateTo` | API | Override the preset window (`YYYY-MM-DD`) |
| `natures` | API | `APPEL_OFFRE`, `ATTRIBUTION`, `RECTIFICATIF` |
| `verboseLogs` | API | Technical debug lines |
| `proxyConfiguration` | API | Default `{ useApifyProxy: true }` on cloud |

#### Example — daily open tenders, restauration, 25 rows

```json
{
  "runPreset": "daily_open_tenders",
  "keywords": ["restauration"],
  "maxResults": 25
}
```

#### Example — awards nationwide, no row cap (API)

```json
{
  "runPreset": "recent_awards",
  "maxResults": 0
}
```

***

### Output example

```json
{
  "notice_id": "26-82217",
  "publication_date": "2026-08-23",
  "response_deadline": "2026-09-29",
  "notice_type": "Call for tenders",
  "notice_type_fr": "Avis de marché",
  "procedure": "Procédure Ouverte",
  "contract_kind": "Fournitures",
  "subject": "Fourniture de Dispositifs Médicaux Stériles",
  "cpv_codes": "33140000",
  "estimated_value": "280000",
  "estimated_value_currency": "EUR",
  "buyer_name": "Centre Hospitalier Universitaire de Besançon",
  "buyer_siret": "26250176000264",
  "buyer_siren": "262501760",
  "buyer_email": "camsp-marches@chu-besancon.fr",
  "buyer_city": "Besançon",
  "department_code": "25",
  "url": "https://www.boamp.fr/pages/avis/?q=idweb:26-82217"
}
```

Dataset views: **Overview** (bid desk), **Buyer contacts**, **Awards**.

***

### FAQ

**Why is email empty?** The gazette only stores a mailbox when the buyer published one. That is expected — not a scrape miss.

**Why is estimated\_value empty?** Older notices and some eForms variants omit amounts. Award value is mainly on **Recent awards**.

**Can I get every notice in France?** Yes — leave keywords and departments blank, set `maxResults` to `0`, and use a date window. Very large windows still stop at the gazette listing ceiling (~10,000 rows per query).

**Do I need a BOAMP account?** No.

***

### Local development

```bash
npm install
npm test
apify run --input-file=./input.json
```

`storage/key_value_stores/default/INPUT.json` must satisfy the input schema, or pass `--input-file`. On local runs, repo `input.json` is merged first; **CLI / KV input overwrites** the same keys.

***

### Also available

**[BODACC · French Legal Announcements](https://apify.com/corent1robert/bodacc-announcements-scraper)** — company creations, insolvency, business sales.\
**[JOAFE · French Association Notices](https://apify.com/corent1robert/joafe-associations-scraper)** — association creations and dissolutions.\
**[French Companies · Search & SIREN Enrich](https://apify.com/corent1robert/recherche-entreprises-scraper)** — directors and VAT from a buyer SIREN.

***

### Support

Custom BOAMP watches, CPV packs, or CRM push: **<corentin@outreacher.fr>**

# Actor input Schema

## `runPreset` (type: `string`):

**Daily open tenders** — calls for tenders published yesterday (best for a morning schedule).

**Recent awards** — who won, when published yesterday.

**Open tenders — last 7 days** — a wider bid window.

**Keyword watch** — last 7 days of open tenders, meant to be used with keywords below.

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

Filter by trade or object — **one term per line**, OR logic (e.g. `restauration`, `nettoyage`). Leave blank to export **all** notices matching the preset.

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

Keep notices for these French departments (e.g. `75`, `69`, `13`). Leave blank for nationwide.

## `searchUrl` (type: `string`):

Already filtered on [boamp.fr](https://www.boamp.fr)? Copy the URL from your browser (search or a single notice) — keywords, notice type, dates, and department are picked up automatically.

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

**25** = quick preview. **0** = no numerical cap (the date window and gazette size still apply).

## Actor input object example

```json
{
  "runPreset": "daily_open_tenders",
  "maxResults": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Full export — every field from this run.

## `datasetOverview` (type: `string`):

Bid desk columns — buyer, deadline, department, subject.

## `datasetOutreach` (type: `string`):

Buyer email, phone and SIRET when the gazette published them.

## `datasetAwards` (type: `string`):

Winner, award value and related call-for-tenders ID.

## `runLog` (type: `string`):

Plain-text progress summary from this run.

# 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 = {
    "runPreset": "daily_open_tenders",
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("corent1robert/boamp-announcements-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 = {
    "runPreset": "daily_open_tenders",
    "maxResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("corent1robert/boamp-announcements-scraper").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 '{
  "runPreset": "daily_open_tenders",
  "maxResults": 10
}' |
apify call corent1robert/boamp-announcements-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/KjNpvyqlYBmTpXZ6N/builds/CUGUoQwmxvmea5wut/openapi.json
