# France Fuel Prices (Open Data) (`shadow-tools/france-fuel-prices`) Actor

Cheapest petrol and diesel in France: search stations by area, nearby GPS search, and min/median/max price statistics for gazole, SP95, SP98, E10, E85, GPLc, with each price's update date. Official open data, unofficial tool, no personal data.

- **URL**: https://apify.com/shadow-tools/france-fuel-prices.md
- **Developed by:** [Shadow Tools](https://apify.com/shadow-tools) (community)
- **Categories:** Travel, Automation, Other
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 fuel stations

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

## France Fuel Prices (Open Data)

Call this Actor to find **where fuel is cheapest in France** and **what the average price is**: gazole (diesel), SP95, SP98, E10, E85 and GPLc, per station, from the official French open data (prix-carburants.gouv.fr). Use `search` for an area (department, region, postcode, city) or all of France, `nearby` when you have GPS coordinates and a radius, and `stats` for min / median / mean / max price per fuel (optionally per department or region). Every price carries its **last update timestamp and age in days**, and `maxPriceAgeDays` drops stale prices.

#### Minimal input per mode

| Mode | Minimal input | What comes back |
|---|---|---|
| `search` | `{"fuel": "gazole", "department": ["69"], "maxPriceAgeDays": 3, "maxResults": 5}` | the 5 cheapest diesel stations of the Rhône with a fresh price |
| `nearby` | `{"mode": "nearby", "fuel": "e10", "latitude": 45.764, "longitude": 4.835, "radiusKm": 5, "maxResults": 5}` | cheapest E10 within 5 km, with `distanceKm` |
| `stats` | `{"mode": "stats", "fuel": "gazole", "groupBy": "department", "maxPriceAgeDays": 3}` | one record per department: min / median / mean / max, station counts, freshness |

An **empty input** `{}` works too: it returns 20 stations with all their prices.

Short real output (nearby, 5 Nov 2026, shortened; the full record also has `services`, `openingHours`, `fuels[]` and `attribution`):
`{"type": "station", "stationId": "69008008", "address": "86 AVENUE JEAN MERMOZ", "postcode": "69008", "city": "Lyon", "departmentCode": "69", "latitude": 45.733, "longitude": 4.881, "locationType": "road", "selectedFuel": "e10", "selectedPrice": 1.974, "selectedUpdatedAt": "2026-11-05T00:01:00+00:00", "selectedAgeDays": 0.6, "distanceKm": 4.96}`

#### For AI agents (MCP)

- **One call = one answer.** Default `maxResults` is 20 (max 10,000; France has about 9,800 stations). Typical run: 1-10 s. A full 9,000-station run takes about 75 s.
- **Pick the mode by what you have**: an area name or code -> `search`; coordinates -> `nearby` (geocode a place name first, the Actor does not geocode); "average / cheapest price in X" -> `stats`.
- **Always set `fuel`** for price questions (without it, stations come in id/distance order, not by price) and **`maxPriceAgeDays`** (3 is a good value) so that abandoned old prices do not win.
- **Errors fail fast** as `Field X invalid: expected Y, got Z, example W` (for example `Field fuel invalid: expected one of gazole, sp95, sp98, e10, e85, gplc, got "kerosene", example gazole.`). Fix that field and call again; nothing is charged.
- **0 results is not an error.** The run succeeds with an empty dataset and the key-value record `SUMMARY` has a `note` saying why and how to widen (bigger `radiusKm`, drop `maxPrice` / `maxPriceAgeDays` / `services`). The same `note` explains capped and partial results.
- Department codes: `"69"`, `"2A"`, `"971"`; regions by name (`"Bretagne"`, `"Île-de-France"`) or 2-digit code.

> **Unofficial tool. Not affiliated with, endorsed by or sponsored by the French Ministry of Economy or prix-carburants.gouv.fr.** It reads their official open data and reformats it. Prices are reported by the stations: always check the pump.

### Who uses it

- **Drivers, fleet managers and delivery companies**: cheapest diesel or E10 around a depot, along a route (call `nearby` for several points) or in a department.
- **Price comparison sites, apps and dashboards**: a clean JSON/CSV feed per station with price age.
- **Journalists, economists and consumer associations**: regional price levels and dispersion today (`stats` with `groupBy`).
- **AI assistants**: answer "cheapest petrol near me" or "average diesel price in Bretagne" with a dated source.
- **Retail and logistics analysts**: share of stations offering E85, GPLc, 24/24 pumps, car wash, electric charging points.

### Modes

- **search**: stations matching your filters, cheapest first when `fuel` is set. Filters (all optional, combined with AND; several values inside one filter are OR, except `services` which are all required): `fuel`, `department`, `region`, `postcode`, `city`, `services`, `open24h`, `roadType` (motorway / road), `fuelAvailability` (available / temporary-shortage), `maxPrice`, `maxPriceAgeDays`.
- **nearby**: same filters inside `radiusKm` (0.1-100) around `latitude` / `longitude`; cheapest first when `fuel` is set, otherwise closest first (`sortBy: "distance"` forces distance order).
- **stats**: min, median, mean, max, number of stations with a price, number of stations in scope, newest and oldest price update, for one fuel or all six; `groupBy` `department` or `region` gives one record per group. Computed on the live data by the open data API.

### Prices, freshness and shortages (read this)

- Each station reports its own prices; some stop reporting. On 5 Nov 2026, of 9,092 stations with a diesel price, 86.9 % had updated it within 3 days, 92.9 % within 7 days and 250 (2.7 %) had a price older than 30 days; the oldest diesel price in Paris dated from 31 March. Use `maxPriceAgeDays`, and read `selectedAgeDays` / `fuels[].ageDays`.
- The data is refreshed continuously by the Ministry (the dataset timestamp moved several times within one hour of testing; the metadata gives no fixed frequency). `SUMMARY.datasetUpdatedAt` and each record's `attribution` tell the dataset update time.
- Shortages: when the source flags a fuel as temporarily unavailable (`rupture` of type temporaire) the fuel appears in `fuels[]` with `status: "temporary-shortage"`; `fuelAvailability: "available"` excludes those stations. Permanent withdrawals are simply not listed.
- Prices below 0.30 or above 5.00 EUR per litre are ignored as data-entry errors (none found on the day of testing).
- `open24h` is the station's own "24/24 automate" flag (card pump), not staffed opening hours.

### What you get

`type` ("station" or "stats"). **Station record**: `stationId`, `address`, `postcode`, `city`, `department`, `departmentCode`, `region`, `regionCode`, `latitude`, `longitude`, `locationType` (motorway / road), `open24hAutomate`, `services[]` (French names), `openingHours[]`, `fuels[]` (`fuel`, `label`, `price` EUR/L, `updatedAt`, `ageDays`, `status`), and when `fuel` is set `selectedFuel`, `selectedPrice`, `selectedUpdatedAt`, `selectedAgeDays`; in nearby mode `distanceKm`; always `attribution`, `extractedAt`.
**Stats record**: `fuel`, optional `departmentCode`/`department` or `regionCode`/`region`, `stationsWithPrice`, `stationsInScope`, `minPrice`, `medianPrice`, `meanPrice`, `maxPrice`, `newestUpdateAt`, `oldestUpdateAt`, `oldestAgeDays`, `attribution`, `extractedAt`.

Example (stats, real, 5 Nov 2026, Côtes-d'Armor, diesel prices updated in the last 3 days, shortened):

```json
{"type": "stats", "fuel": "gazole", "departmentCode": "22", "department": "Côtes-d'Armor", "stationsWithPrice": 79, "stationsInScope": 114, "minPrice": 2.077, "medianPrice": 2.169, "meanPrice": 2.181, "maxPrice": 2.399, "newestUpdateAt": "2026-11-05T12:39:37+00:00", "oldestUpdateAt": "2026-11-02T18:48:57+00:00", "oldestAgeDays": 2.8}
```

### Quick start

Cheapest SP98 on motorways in Île-de-France, fresh prices only:

```json
{ "fuel": "sp98", "region": ["Île-de-France"], "roadType": "motorway", "maxPriceAgeDays": 2, "maxResults": 10 }
```

Stations near Lyon with E85 and a car wash, no shortage:

```json
{ "mode": "nearby", "fuel": "e85", "latitude": 45.764, "longitude": 4.835, "radiusKm": 30, "services": ["Lavage automatique"], "fuelAvailability": "available", "maxResults": 10 }
```

Diesel price level per department:

```json
{ "mode": "stats", "fuel": "gazole", "groupBy": "department", "maxPriceAgeDays": 3, "maxResults": 120 }
```

Call it from the API: `POST https://api.apify.com/v2/acts/<ACTOR>/run-sync-get-dataset-items?token=<TOKEN>` with the JSON input as body.

### Pricing

Pay per event, no subscription:

| Event | Price | Per 1,000 |
|---|---|---|
| `fuel-station`: one record delivered (a station, or one statistics record) | **0.002 USD** | **2 USD per 1,000 records** |
| Actor start (once per run) | 0.00005 USD | n/a |

A run returning nothing costs only the start event. Example: the 20 cheapest stations of a department cost 0.04 USD; the whole of France (about 9,100 diesel stations) about 18 USD. Use the platform's **Maximum cost per run** to cap spending: the Actor reads it, stops cleanly at the cap and returns what it already delivered (the cap must allow at least one record).

### Privacy / GDPR by design

This Actor is built so that it returns **no data about natural persons**, and the filtering below is applied **on purpose to respect the GDPR**:

- **Places, not people.** A record describes a fuel station: its identifier, business address, coordinates, services, fuels, prices and update timestamps. Nothing else is returned.
- **Allow-list, fail-closed.** The Actor asks the open data API for a fixed list of fields and maps only those fields; any other field the source might add in the future (operator or manager names, phone numbers, e-mail addresses, company identifiers...) is dropped, not passed through. A unit test pins that list.
- **No operator names.** The dataset has no brand field, and station operator or owner names are never requested or returned (an independent station can be named after its owner). The station `address` is a business address; e-mail addresses and phone numbers are stripped from address text anyway.
- **Your location stays in memory.** In `nearby` mode the coordinates you give are used only to build the query. They are not written to the dataset, the `SUMMARY` record or the run log (the query log shows `POINT(redacted)`). Note that the Apify platform itself stores the run input (as for any Actor): use a rounded point, or the centre of a town, if that matters.
- **No tracking, no state**: the Actor keeps nothing between runs.

Residual risk, stated honestly: a very small independent station could in theory operate from a private person's premises, so its business address is also a place linked to that person. We return only what the Ministry already publishes openly about the point of sale, and no name.

### Licences and attribution

Data: "Prix des carburants en France - Flux instantané v2", Ministère de l'Économie, published on data.economie.gouv.fr under the **Licence Ouverte v2.0 (Etalab)**; the Actor adds the source and the dataset update date to each record's `attribution`. The data are extracted from the information system "Prix des carburants" and concern the points of sale listed on prix-carburants.gouv.fr (Arrêté ministériel du 12 décembre 2006).

### Limits and good to know

- The brand/banner of a station is **not** in this dataset (so no brand filter); services, location and prices are.
- `city` matches by words (accents and case ignored): "Paris" also matches "Le Touquet-Paris-Plage": combine with `postcode` or `department`.
- `nearby` takes coordinates, it does not geocode place names.
- Prices are the ones displayed by the stations; differences with the pump are possible. The history is not covered here (the Ministry publishes a separate daily/yearly history dataset, "Prix des carburants en France - Flux quotidien").
- Unofficial; data may be incomplete or late: check the price at the pump.

### En français (court)

Outil **non officiel** qui reprend les données ouvertes du Ministère de l'Économie sur les prix des carburants (Licence Ouverte v2.0) : stations les moins chères par zone ou autour d'un point GPS, et statistiques min / médiane / max par département ou région, avec la date de mise à jour de chaque prix. Aucune donnée personnelle : seulement des stations (lieux), jamais de noms d'exploitants.

# Actor input Schema

## `mode` (type: `string`):

Pick one. "search" = use to list stations in an area (department, region, postcode, city) or the whole of France, cheapest first when a fuel is given. "nearby" = use when you have GPS coordinates (latitude and longitude in decimal degrees) and want the cheapest or closest stations within radiusKm. "stats" = use to get min/median/mean/max price and the number of stations per fuel for an area (one record per fuel, or per department/region with groupBy), without station details. Allowed values: search, nearby, stats. Default: search.

## `fuel` (type: `string`):

Fuel to price and sort by, e.g. "gazole". Allowed values: gazole (diesel B7), sp95 (unleaded 95), sp98 (unleaded 98), e10 (SP95-E10), e85 (superethanol), gplc (LPG). When set, only stations selling that fuel are returned, cheapest first. Empty = all fuels (search/nearby: stations in id/distance order with all their prices; stats: one record per fuel).

## `department` (type: `array`):

French department codes or names as a list, e.g. \["75", "2A"] or \["Rhône"]. Metropolitan codes have two characters ("01", "69", "2A"), overseas three ("971"). Any of them matches (OR). Empty = all of France.

## `region` (type: `array`):

Region names or 2-digit region codes as a list, e.g. \["Bretagne"] or \["53"]. Exact name with accents as in the source, for example "Île-de-France", "Auvergne-Rhône-Alpes". Any of them matches (OR). Empty = all regions.

## `postcode` (type: `array`):

5-digit postal codes as a list, e.g. \["69003", "75015"]. Any of them matches (OR). Empty = no postcode filter.

## `city` (type: `array`):

City name words as a list, e.g. \["Lyon", "Saint-Étienne"]. Matches the city name by words, accents and case ignored ("Paris" also matches "Le Touquet-Paris-Plage": add a postcode or department to be precise). Empty = no city filter.

## `latitude` (type: `number`):

Latitude of the search centre in decimal degrees, e.g. 45.764 for Lyon. Required in nearby mode, ignored otherwise. Used in memory only: it is not written to the dataset or the run SUMMARY.

## `longitude` (type: `number`):

Longitude of the search centre in decimal degrees, e.g. 4.835 for Lyon. Required in nearby mode, ignored otherwise. Used in memory only: it is not written to the dataset or the run SUMMARY.

## `radiusKm` (type: `number`):

Search radius around the point in kilometres, between 0.1 and 100, e.g. 10. Used only in nearby mode. Default: 10.

## `services` (type: `array`):

Services the station must offer, as a list, e.g. \["Lavage automatique", "Toilettes publiques"]. ALL listed services are required (AND). Exact source names (French). Empty = no service filter.

## `open24h` (type: `boolean`):

true to keep only stations flagged 24/24 automate (pumps usable day and night with a card), e.g. true. This is the station's own flag, not staffed opening hours. Default: false (no filter).

## `roadType` (type: `string`):

Where the station is, e.g. "motorway". Allowed values: any (default), motorway (on a motorway, usually pricier), road (on an ordinary road).

## `fuelAvailability` (type: `string`):

Stock status of the chosen fuel, e.g. "available". Allowed values: any (default), available (exclude stations reporting a temporary shortage of that fuel; needs fuel), temporary-shortage (only stations reporting a shortage of that fuel, or of any fuel if fuel is empty).

## `maxPrice` (type: `number`):

Highest accepted price of the chosen fuel in euros per litre, between 0.3 and 5, e.g. 1.9. Needs fuel. Empty = no limit (prices outside 0.30-5.00 EUR/L are always ignored as data errors).

## `maxPriceAgeDays` (type: `integer`):

Ignore prices not updated in the last N days, an integer between 1 and 365, e.g. 3. Some stations stop reporting and their old prices stay in the data, so set this for reliable results. Empty = no age filter.

## `sortBy` (type: `string`):

Order of the stations, e.g. "price". Allowed values: price (cheapest first, needs fuel; default when fuel is set), distance (closest first, nearby mode only; default in nearby without fuel), id (stable order; default otherwise). Ignored in stats mode.

## `groupBy` (type: `string`):

Only for stats mode: one statistics record per group, e.g. "department" to compare the 100 departments of France. Allowed values: none (default, one record per fuel for the whole area), department, region.

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

Maximum number of records returned (stations, or statistics records), an integer between 1 and 10000, e.g. 20. There are about 9,800 stations in France. Default: 20.

## Actor input object example

```json
{
  "mode": "search",
  "department": [
    "69"
  ],
  "region": [
    "Bretagne"
  ],
  "postcode": [
    "69003"
  ],
  "city": [
    "Lyon"
  ],
  "latitude": 45.764,
  "longitude": 4.835,
  "radiusKm": 10,
  "services": [
    "Toilettes publiques"
  ],
  "open24h": true,
  "maxPrice": 1.9,
  "maxPriceAgeDays": 3,
  "groupBy": "none",
  "maxResults": 20
}
```

# Actor output Schema

## `stations` (type: `string`):

Matching stations with address, services, prices per fuel and the last update of each price.

## `stats` (type: `string`):

Min, median, mean and max price per fuel (and per department or region), with freshness.

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

Every field of every record, downloadable as JSON, CSV or Excel.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("shadow-tools/france-fuel-prices").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("shadow-tools/france-fuel-prices").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 '{}' |
apify call shadow-tools/france-fuel-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,shadow-tools/france-fuel-prices"
        }
    }
}
```

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/WCmLGm8gitWQ0QOjH/builds/bmBxwLXezEJkmwxmZ/openapi.json
