Ir al contenido

Integrar en tu web o app

Esta guía reúne lo necesario para usar la API en producción: llamarla desde el navegador (CORS), cachear hasta la próxima actualización con meta.next_update, mostrar la atribución y reintentar bien cuando la API pide esperar (429 o 503 con Retry-After).

La API admite CORS desde cualquier origen (Access-Control-Allow-Origin: *, sin credenciales ni cookies), así que puedes llamarla directamente con fetch desde una página web. Los límites son por IP: si la llaman tus usuarios desde su navegador, cada uno tiene su cuota.

Si tu servidor hace las peticiones para muchos usuarios (una app con backend, un bot), todas salen de la misma IP y comparten los límites: en ese caso cachea en tu servidor y sirve desde ahí.

Las cabeceras que puedes leer desde JavaScript en otro origen son RateLimit-Policy, Retry-After y X-Request-Id (además de las simples, como Content-Type y Cache-Control).

Los precios solo cambian dos veces al día (hacia las 8:00 y las 20:00, hora peninsular). Cada respuesta dice cuándo llegarán datos nuevos en meta.next_update, y las respuestas con precios llevan Cache-Control: public, max-age=… hasta esa hora (como mucho una hora, con stale-while-revalidate=300). Pedir lo mismo antes no te dará datos distintos.

  1. Guarda cada respuesta con la clave de su URL.
  2. Sírvela desde tu caché mientras la hora actual sea anterior a meta.next_update (más un margen de unos minutos, porque la actualización tarda en completarse).
  3. Después, vuelve a pedirla. Si meta.last_updated no ha cambiado, la actualización aún no ha terminado: reintenta en unos minutos.

Con caché hasta next_update, reintentos con Retry-After y errores RFC 9457:

const API = "https://api.preciosgasolina.es/v1";
const cache = new Map(); // url → { body, until }
const MARGEN_MS = 10 * 60 * 1000; // la actualización tarda unos minutos
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
export class ApiError extends Error {
constructor(problem) {
super(`${problem.code}: ${problem.detail}`);
Object.assign(this, problem); // type, title, status, detail, code, request_id, instance
}
}
export async function api(path, params = {}, { intentos = 3 } = {}) {
const url = API + path + (Object.keys(params).length ? "?" + new URLSearchParams(params) : "");
const hit = cache.get(url);
if (hit && Date.now() < hit.until) return hit.body;
for (let i = 1; ; i++) {
const res = await fetch(url, { headers: { Accept: "application/json" }, signal: AbortSignal.timeout(10_000) });
if (res.ok) {
const body = await res.json();
const next = Date.parse(body.meta?.next_update);
// Hasta la próxima actualización (+ margen); si no hay fecha, 10 minutos.
const until = Number.isFinite(next) ? next + MARGEN_MS : Date.now() + MARGEN_MS;
cache.set(url, { body, until });
return body;
}
const problem = await res.json().catch(() => ({ status: res.status, code: "HTTP_" + res.status, detail: res.statusText }));
// Solo se reintenta lo que es temporal: 429 y 503 (con Retry-After) y errores 5xx.
const temporal = res.status === 429 || res.status >= 500;
if (!temporal || i >= intentos) {
if (hit) return hit.body; // mejor un dato de la actualización anterior que nada
throw new ApiError(problem);
}
const retryAfter = Number(res.headers.get("Retry-After"));
await sleep((Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter : 2 ** i) * 1000);
}
}
// Uso
const { data, meta } = await api("/prices", { level: "province", code: "28" });

Debajo de los datos, muestra meta.attribution, la fecha de meta.last_updated en hora de Madrid y un enlace a meta.web_url. Es la condición de la licencia CC BY 4.0: Atribución y licencia.

meta.stale: datos de la actualización anterior

Sección titulada «meta.stale: datos de la actualización anterior»

Si la base de datos no responde, la API sirve la última copia buena de esa respuesta con meta.stale: true y Cache-Control: public, max-age=60. Los datos son correctos pero pueden ser de la actualización anterior: puedes mostrarlos con la fecha de meta.last_updated y volver a pedirlos en un minuto. Ver Frescura de los datos.

  • Cacheas cada respuesta hasta meta.next_update.
  • Muestras meta.attribution, la fecha de los datos y un enlace a meta.web_url.
  • Reintentas solo 429, 503 y otros 5xx, respetando Retry-After.
  • Guardas request_id en tus registros de error (y nos lo envías si nos escribes por un fallo).
  • Usas limit (máximo 50) y no recorres toda España petición a petición: para eso están los datos abiertos.
  • No pides parámetros que el endpoint no admite: la API responde 400 a los parámetros desconocidos.

Fuente de los datos: Ministerio para la Transición Ecológica y el Reto Demográfico (Geoportal de Gasolineras). Datos de la API y del servidor MCP con licencia CC BY 4.0: cita Gasolina hoy y la fuente.