# Spain Fuel Station Prices Scraper (Gasolineras, official data) (`lafabbricallc/spain-fuel-station-prices`) Actor

Current prices for every fuel type at Spanish service stations (gasolineras) from the Ministry of Industry's open-data API, as clean numeric rows. Filter by province, municipality, region, brand, fuel or radius; sort by cheapest. No personal data.

- **URL**: https://apify.com/lafabbricallc/spain-fuel-station-prices.md
- **Developed by:** [La Fabbrica LLC](https://apify.com/lafabbricallc) (community)
- **Categories:** Business, Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 station rows

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

### What does Spain Fuel Station Prices Scraper do?

Spain Fuel Station Prices Scraper (Gasolineras) gives you the **current price of every fuel at every service station in Spain** as clean, numeric, spreadsheet-ready rows. It reads the Ministry of Industry's official open-data API (Geoportal Gasolineras, the same source behind the government's price map), converts the comma-decimal Spanish strings (`"1,459"`) into numbers, translates the field names to English, and lets you filter by **province, municipality, autonomous community, brand, fuel type, opening hours or a radius around a point**, then sort by the cheapest price for the fuel you care about.

Every row is one station with 23 price columns (Gasolina 95 E5/E10/E25/E85, 95 Premium, Gasolina 98 E5/E10, Gasóleo A, Premium and B, renewable diesel HVO, renewable gasoline, biodiesel, bioethanol, LPG/Autogas, CNG, LNG, bio-CNG, bio-LNG, hydrogen, methanol, ammonia and AdBlue), plus address, postcode, municipality, province, region, WGS84 coordinates, a map link, opening hours, an `open24h` flag, public/restricted sale, road side, and the timestamp of the ministry's price list. Prices are refreshed by the stations roughly every 30 minutes.

It reads only the public, unauthenticated REST endpoints. The actor runs with Apify's **limited permissions** level: it can only read and write its own run storages and never touches other data in your account. It does **not** log in and does **not** collect personal data: the source has no contact fields at all, and the few independent stations whose sign is an individual's personal name get `brand: null`.

### Why use it?

- **Fleet managers and logistics**: pull the cheapest Gasóleo A within 10 km of each depot every morning and route drivers accordingly.
- **Price-comparison apps and chatbots**: a JSON or CSV feed per province or municipality, already numeric and deduplicated, with coordinates for a map.
- **Retail-margin and market analysts**: track every Repsol, Moeve (Cepsa), BP, Galp, Ballenoil or Plenergy station's price by province over time; schedule daily runs and diff the datasets.
- **AI agents and RAG pipelines**: "What is the cheapest Gasolina 95 near Valencia airport?" becomes one actor call with `latitude`, `longitude`, `radiusKm` and `sortBy: "G95E5"`.

On Apify you also get scheduling, webhooks, run monitoring and integrations (Google Sheets, Make, Zapier, LangChain, MCP).

### How to use it

On the actor page, open the **Input** tab, pick one or more provinces (or municipalities, or regions), optionally set filters and a sort order, and click **Start**. Leave every field at its default to get the first 100 stations in the province of Madrid. The fields:

| Field | Type | Default | Meaning |
|---|---|---|---|
| `provinces` | array of strings | `["Madrid"]` | Province names or two-digit ids (`"Barcelona"`, `"08"`, `"Illes Balears"`, `"La Coruña"`), accent-insensitive |
| `municipalities` | array of strings | none | Municipality names (INE spelling) or ministry ids; add `provinces` to narrow the lookup, or write `"Name (Province)"` |
| `regions` | array of strings | none | Autonomous communities by name or id; used only when `provinces` and `municipalities` are empty |
| `products` | array of codes | none | Keep stations selling at least one of these fuels: `G95E5`, `G95E10`, `G95E5+`, `G98E5`, `GOA`, `GOA+`, `GOB`, `DREN`, `GLP`, `GNC`, `GNL`, `H2`, `ADB`, ... |
| `brands` | array of strings | none | Keep stations whose sign contains one of these words (`"Repsol"`, `"Cepsa"`, `"BP"`) |
| `only24h` | boolean | `false` | Keep only stations open 24 hours, 7 days (`L-D: 24H`) |
| `latitude`, `longitude`, `radiusKm` | numbers | none | Radius search around a WGS84 point; rows get a `distanceKm` column and are sorted by distance |
| `sortBy` | `none` / `distance` / fuel code | `none` | Sort ascending by a fuel's price (stations without that fuel are dropped) |
| `maxItems` | integer | `100` | Stop after this many stations (max 15,000; the whole country is about 12,000) |

Leave `provinces`, `municipalities` and `regions` all empty to download the national list (about 12 MB, 15-20 seconds) and filter it, for example with a radius or a brand.

#### Example input

Cheapest regular diesel in the province of Barcelona, 50 stations:

```json
{
    "provinces": ["Barcelona"],
    "products": ["GOA"],
    "sortBy": "GOA",
    "maxItems": 50
}
```

Stations open 24 h within 8 km of Valencia city centre:

```json
{
    "provinces": ["Valencia"],
    "latitude": 39.4699,
    "longitude": -0.3763,
    "radiusKm": 8,
    "only24h": true,
    "maxItems": 40
}
```

Major brands in two municipalities of Seville, sorted by Gasolina 95:

```json
{
    "provinces": ["Sevilla"],
    "municipalities": ["Sevilla", "Dos Hermanas"],
    "brands": ["Repsol", "Cepsa", "Moeve", "BP"],
    "sortBy": "G95E5",
    "maxItems": 60
}
```

### What data does it return?

One row per station. Prices are in EUR per litre (per kg for hydrogen and the gas fuels) and `null` when the station does not sell that fuel.

| Field | Meaning |
|---|---|
| `stationId` | Ministry station id (stable across days) |
| `brand` | Station sign / brand (`REPSOL`, `BALLENOIL`, ...), `null` if none |
| `address`, `postcode`, `locality`, `municipality`, `municipalityId`, `province`, `provinceId`, `region`, `regionId` | Location |
| `latitude`, `longitude`, `mapUrl` | WGS84 coordinates and a Google Maps link |
| `openingHours`, `open24h` | Hours in the ministry's Spanish notation (`L-D: 24H`, `L-V: 07:00-22:00; S: 08:00-14:00`) and a 24/7 flag |
| `saleType` | `public` or `restricted` (cooperative / members only) |
| `roadSide` | `right`, `left` or `none`, relative to increasing kilometre points |
| `reportingCode` | Ministry reporting-channel code as published (`OM` or `dm`) |
| `bioethanolPct`, `fameEsterPct` | Bio-component shares declared by the station |
| `priceGasoline95E5`, `priceGasoline95E10`, `priceGasoline95E25`, `priceGasoline95E85`, `priceGasoline95E5Premium`, `priceGasoline98E5`, `priceGasoline98E10` | Gasoline prices |
| `priceDieselA`, `priceDieselPremium`, `priceDieselB`, `priceRenewableDiesel`, `priceRenewableGasoline`, `priceBiodiesel`, `priceBioethanol` | Diesel and bio prices |
| `priceLpg`, `priceCng`, `priceLng`, `priceBioCng`, `priceBioLng`, `priceHydrogen`, `priceMethanol`, `priceAmmonia`, `priceAdblue` | Gas and other prices |
| `distanceKm` | Only for radius searches: great-circle distance from the centre |
| `pricesAsOf` | Timestamp of the ministry's price list, Europe/Madrid local time |
| `retrievedAt` | UTC time the actor downloaded the list |

#### Example output

```json
{
    "stationId": "15493",
    "brand": "PLENERGY",
    "address": "CAMINO TORREJON, 4",
    "postcode": "28864",
    "locality": "AJALVIR",
    "municipality": "Ajalvir",
    "municipalityId": "4277",
    "province": "MADRID",
    "provinceId": "28",
    "region": "Madrid",
    "regionId": "13",
    "latitude": 40.528778,
    "longitude": -3.481639,
    "mapUrl": "https://www.google.com/maps?q=40.528778,-3.481639",
    "openingHours": "L-D: 24H",
    "open24h": true,
    "saleType": "public",
    "roadSide": "right",
    "reportingCode": "dm",
    "bioethanolPct": 0,
    "fameEsterPct": 0,
    "priceGasoline95E5": 1.459,
    "priceGasoline95E10": null,
    "priceGasoline98E5": null,
    "priceDieselA": 1.409,
    "priceDieselPremium": null,
    "priceLpg": null,
    "priceAdblue": null,
    "pricesAsOf": "2026-09-25 05:11:40",
    "retrievedAt": "2026-09-25T03:20:41.512Z"
}
```

(Shortened: the real row carries all 23 price columns.)

### How much does it cost?

The actor is priced **pay-per-event**: a flat **$0.01 per run** plus **$0.002 per station row** saved to the dataset. You pay only for rows you receive; filtered-out stations and failed runs cost nothing beyond the start fee.

| Run | Rows | Price |
|---|---|---|
| One municipality | 25 | $0.06 |
| Default (province of Madrid, first 100) | 100 | $0.21 |
| Whole province of Barcelona | ~750 | $1.51 |
| Whole country | ~11,500 | $23.01 |

Platform usage (compute) for a province run is well under one cent and is included in your Apify plan. Set **Maximum cost per run** in the run options to cap what a run may charge; the actor stops cleanly at the cap.

### Limits and things to know

- The ministry's API has no paging: a province is one request (0.3-1 MB), the whole country one 12 MB request. Filters and sorting are applied after download, so `maxItems` limits what you pay for, not what is downloaded.
- `sortBy` with a fuel code drops stations that do not sell that fuel. Use `products` alone if you want to keep the ministry's order.
- A radius search without `provinces`/`regions` downloads the whole country first; combine it with the province the point lies in for a faster run.
- Municipality names must follow the INE spelling used by the ministry (`"Hospitalet de Llobregat (L')"`, `"Acebeda (La)"`); the actor also accepts the natural order (`"La Acebeda"`) and tells you the candidates when a name is ambiguous or unknown.
- `restricted` sale stations (cooperatives) are included; filter on `saleType` afterwards if you only want public stations.
- Prices are those declared by the station operators under Spanish law (Orden ITC/2308/2007); the ministry does not verify them in real time.
- Memory: the default 512 MB is enough for any province; a whole-country run with sorting fits in 1 GB.

### Data licence and attribution

The data comes from the Ministerio de Industria, Comercio y Turismo open-data service (`sedeaplicaciones.minetur.gob.es/ServiciosRESTCarburantes`), published for reuse under Spain's public-sector information law (Ley 37/2007) and listed on datos.gob.es. When you republish the data, credit the source as "Ministerio de Industria, Comercio y Turismo - Precios de carburantes en estaciones de servicio".

### Personal data

None is collected. The source publishes station-level business data only (sign, address, hours, prices, coordinates); there are no operator, owner, e-mail or phone fields. Independent stations whose sign is a person's own name are returned with `brand: null`.

### FAQ and support

**Why does a station show every price as null?** It reported no prices in the current half-hour cycle; run again later or check `pricesAsOf`.

**Can I get historical prices?** Not with this actor; schedule it daily and keep the datasets, or ask for the ministry's `EstacionesTerrestresHist` endpoint in an issue.

**A municipality name is rejected.** Add the province under `provinces` and use the spelling from the error message's suggestions, or pass the numeric `municipalityId` from a previous run.

Open an issue on the actor page for bugs or field requests.

# Actor input Schema

## `provinces` (type: `array`):

Spanish provinces by name or two-digit id, e.g. "Madrid", "Barcelona", "08", "Illes Balears". Accent-insensitive. Leave empty to use regions or the whole country.

## `municipalities` (type: `array`):

Municipality names (as the INE writes them, e.g. "Alcalá de Henares", "L'Hospitalet de Llobregat") or ministry ids. When set, only these municipalities are downloaded. Add the province under "Provinces" to disambiguate names that exist in several provinces, or write "Name (Province)".

## `regions` (type: `array`):

Autonomous communities by name or id, e.g. "Cataluña", "Andalucia", "13". Used only when "Provinces" and "Municipalities" are empty.

## `products` (type: `array`):

Keep only stations that sell at least one of these fuels. Codes: G95E5 (Gasolina 95), G95E10, G95E25, G95E85, G95E5+ (95 Premium), G98E5, G98E10, GOA (Gasóleo A / regular diesel), GOA+ (Diesel Premium), GOB (agricultural diesel), DREN (renewable diesel / HVO), GREN, BIO, BIE, GLP (LPG / Autogas), GNC, GNL, BGNC, BGNL, H2, MET, AMO, ADB (AdBlue). Every row still contains all price columns.

## `brands` (type: `array`):

Keep only stations whose sign contains one of these words (case- and accent-insensitive), e.g. "Repsol", "Cepsa", "Moeve", "BP", "Ballenoil", "Plenergy".

## `only24h` (type: `boolean`):

Keep only stations whose opening hours are "L-D: 24H".

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

Centre of a radius search, WGS84 decimal degrees, e.g. 40.4168. Requires "Longitude" and "Radius". Rows then include "distanceKm" and are sorted by distance unless "Sort by" says otherwise.

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

WGS84 decimal degrees, negative west of Greenwich, e.g. -3.7038 for Madrid.

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

Keep stations within this distance of the centre, in kilometres (great-circle distance).

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

"none" keeps the ministry's order; "distance" needs a radius search; a fuel code sorts ascending by that fuel's price and drops stations that do not sell it ("cheapest Gasolina 95 in Madrid").

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

Stop after this many stations have been saved. You are charged only for stations actually saved. The whole country is about 12,000 stations.

## Actor input object example

```json
{
  "provinces": [
    "Madrid",
    "Toledo"
  ],
  "municipalities": [
    "Alcalá de Henares",
    "Getafe"
  ],
  "regions": [
    "Cataluña"
  ],
  "products": [
    "G95E5",
    "GOA"
  ],
  "brands": [
    "Repsol",
    "Ballenoil"
  ],
  "only24h": false,
  "latitude": 40.4168,
  "longitude": -3.7038,
  "radiusKm": 10,
  "sortBy": "none",
  "maxItems": 100
}
```

# Actor output Schema

## `items` (type: `string`):

No description

## `itemsCsv` (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 = {
    "provinces": [
        "Madrid"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lafabbricallc/spain-fuel-station-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 = { "provinces": ["Madrid"] }

# Run the Actor and wait for it to finish
run = client.actor("lafabbricallc/spain-fuel-station-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 '{
  "provinces": [
    "Madrid"
  ]
}' |
apify call lafabbricallc/spain-fuel-station-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lafabbricallc/spain-fuel-station-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/m83MEDtazcyONWtOP/builds/M2opGeWDOlugDVWrw/openapi.json
