# API de tarifas eléctricas indexadas (V2C)

> API REST pública y gratuita con el precio horario de la luz con tarifa indexada en España (Península) para
> las tarifas de acceso 2.0TD, 3.0TD y 6.1TD: precio de compra y de venta de excedentes, hoy y mañana, más
> histórico desde 2026-01-01. Sin autenticación. Respuestas JSON. Precios en €/kWh sin impuestos.

Esta guía está pensada para que un asistente o agente de IA integre la API en código, automatizaciones
(Home Assistant, Node-RED) o respuestas a usuarios.

- URL base: `https://tarifaelectrica.v2charge.com`
- Web para personas: https://tarifaelectrica.v2charge.com/
- Esta guía: https://tarifaelectrica.v2charge.com/llms.txt (también en `/api.md`)

## Reglas de uso (obligatorias)

1. **Límite: 1 petición cada 10 segundos por IP.** Si se supera: HTTP `429` con cabecera `Retry-After`
   (segundos). Espera ese tiempo y reintenta una sola vez. No hagas peticiones en paralelo.
2. **Cachea.** Los precios de un día no cambian salvo una vez (ver "Provisional"). Pide un día completo y
   reutilízalo; no consultes `/now` cada minuto. Frecuencia razonable: cada 15-60 minutos como máximo.
3. Identifica tu integración (opcional, recomendado) con la cabecera `X-API-Key: <tu-id-libre>`. No es secreta.
4. Se registran IP, identificador, ruta, hora y User-Agent durante 31 días (seguridad y control de uso).
5. Precios orientativos: cada comercializadora aplica sus coeficientes. No los presentes como precio de factura.

## Conceptos

- **tariff**: `2.0TD` (baja tensión ≤15 kW, 3 periodos), `3.0TD` (baja tensión >15 kW, 6 periodos),
  `6.1TD` (alta tensión 1-30 kV, 6 periodos).
- **compra_eur_kwh**: precio de la energía consumida de la red (mercado + pérdidas/margen + peaje + cargo).
- **venta_eur_kwh**: valor de la energía vertida (autoconsumo con excedentes). Puede ser 0 o negativo.
- **period**: periodo tarifario de energía, `P1`…`P6` (P1 = más caro). Depende de hora, día y mes.
- **provisional**: `true` cuando los costes de ajuste de REE aún no están publicados y se han estimado.
  Solo afecta a 2.0TD. Diferencia típica: décimas de céntimo.
- Horas en hora peninsular (`Europe/Madrid`). Un día tiene 23, 24 o 25 horas (cambio de hora).
- Unidades: €/kWh en `/v1`. Para mostrar en céntimos: multiplicar por 100 (c€/kWh).

## Calendario de datos

| Dato | Disponible (hora peninsular) |
|---|---|
| Precios de hoy | Todo el día |
| Precios de mañana | Desde ~13:00 (provisionales en 2.0TD) |
| Precios de mañana definitivos | Desde ~21:00 |

Antes de ~13:00, pedir mañana devuelve `404`. **Mejor momento para consultar:** a partir de las **21:00**,
cuando todas las tarifas son definitivas (una sola petición al día basta). 3.0TD y 6.1TD ya son definitivas
desde las 13:15; 2.0TD es provisional entre las 13:15 y las 21:00. Las subastas intradiarias (IDA1 ~17:30,
IDA2 ~22:30) no modifican el precio indexado, que se basa en el mercado diario.

## Endpoints principales

Todos `GET` salvo `/v1/simulate`. Parámetro `product` opcional (`indexada` por defecto, `generico`).

### GET /v1/prices?tariff=2.0TD&on=AAAA-MM-DD

Día completo (por defecto hoy). Respuesta:

```json
{
  "date": "2026-10-14",
  "tariff": "2.0TD",
  "product": "indexada",
  "summary": {
    "compra_media": 0.287651, "venta_media": 0.182854,
    "compra_min": {"eur_kwh": 0.12647, "ts_local": "2026-10-14T14:00:00+02:00"},
    "compra_max": {"eur_kwh": 0.472063, "ts_local": "2026-10-14T20:00:00+02:00"},
    "horas": 24, "completo": true, "provisional": true
  },
  "prices": [
    {
      "ts_utc": "2026-10-14T18:00:00Z",
      "ts_local": "2026-10-14T20:00:00+02:00",
      "period": "P1",
      "compra_eur_kwh": 0.472063,
      "venta_eur_kwh": 0.289803,
      "provisional": true,
      "pmd_eur_mwh": 293.3925,
      "ajustes_eur_mwh": 42.59,
      "components_eur_kwh": {"energia": 0.37451, "peaje": 0.033261, "cargo": 0.064292}
    }
  ],
  "assumptions": ["..."]
}
```

### GET /v1/prices/tomorrow?tariff=3.0TD

Igual que `/v1/prices` para mañana. `404` si aún no se ha publicado (antes de ~13:00).

### GET /v1/prices/now?tariff=2.0TD

Un solo objeto hora (los campos de `prices[]` más `tariff` y `product`).

### GET /v1/prices/cheapest?tariff=2.0TD&n=4&block=3

Las `n` horas más baratas y la mejor franja contigua de `block` horas. Sin `on`, usa mañana si ya está
publicado; si no, hoy. Respuesta: `date`, `provisional`, `cheapest_hours[]` (objetos hora, en orden
cronológico) y `best_block` (`avg_eur_kwh`, `from`, `to`; `to` es el inicio de la última hora del bloque).
Útil para programar la carga de un coche eléctrico, una batería o electrodomésticos.

### POST /v1/simulate

Factura estimada a partir de consumo horario.

```json
{
  "tariff": "2.0TD",
  "start": "2026-10-12T00:00:00",
  "consumption_kwh": [0.5, 0.4, 0.4],
  "surplus_kwh": [0, 0, 0],
  "power_kw": {"P1": 4.6, "P2": 4.6},
  "meter_rental_eur_month": 0.81,
  "include_taxes": true
}
```

`consumption_kwh`: un valor por hora desde `start` (hora local). `surplus_kwh` opcional, misma longitud.
`power_kw`: 2.0TD usa `P1` y `P2`; 3.0TD/6.1TD `P1`…`P6`. Devuelve energía por periodo, compensación de
excedentes (no puede superar la energía), potencia, impuesto eléctrico, IVA (21 %) y `total_eur`.
`422` si faltan precios para alguna hora.

### Otros

- `GET /v1/status` → `{"AAAA-MM-DD": {"spot": bool, "ajustes": bool, "pvpc": bool}}` para hoy y mañana.
  `spot: true` = hay precios; `ajustes: false` = 2.0TD provisional.
- `GET /v1/periods?tariff=6.1TD&on=AAAA-MM-DD` → periodo de energía y potencia de cada hora.
- `GET /v1/regulated?tariff=3.0TD&on=AAAA-MM-DD` → peajes y cargos (energía €/kWh, potencia €/kW·año).
- `GET /v1/tariffs` → tarifas y productos.
- `GET /health` → `{"ok": true}` (sin límite).

## Rutas compatibles por fecha (formato estilo Node-RED)

`TU_ID` es un identificador libre. Barra final opcional.

| Ruta | Contenido |
|---|---|
| `/TU_ID/indexada/AAAA-MM-DD/` | Horario 2.0TD (`/3/` 3.0TD, `/6/` 6.1TD al final) |
| `/TU_ID/indexada/AAAA-MM/` | Media de 24 h por día del mes |
| `/TU_ID/indexada/AAAA/` | Media de 24 h por mes del año |
| `/TU_ID/omie/AAAA-MM-DD/` | Mercado diario España, `precio` €/kWh (también `AAAA-MM`, `AAAA`) |
| `/TU_ID/pvpc/AAAA-MM-DD/` | PVPC 2.0TD de REE, `precio` €/kWh (también `AAAA-MM`, `AAAA`) |

Día:

```json
{
  "command": "indexada", "success": true, "tarifaAcceso": "3.0TD", "requests": 12,
  "data": [{"costeConsumo": 0.31323, "costePeaje": 0.00979, "fecha": "2026-10-13", "hora": 20,
            "pagoVertido": 0.26061, "perdidas": 16.4, "periodo": 4, "provisional": false}]
}
```

Mes/año: `"table": "medias 24 horas"` y por elemento `dia` o `mes`, `fecha`, `costeConsumo`, `pagoVertido`,
`perdidas`, `periodo1`…`periodoN` (`null` si no hay horas de ese periodo).

## Errores

| Código | Qué hacer |
|---|---|
| 400 | Fecha o tarifa mal formada. Corrige el parámetro. |
| 404 | Día aún no publicado o sin datos. No reintentes hasta la siguiente publicación. |
| 422 | Parámetros no válidos o faltan precios. Revisa `detail`. |
| 429 | Espera `Retry-After` segundos. |

Cuerpo de error: `{"detail": "mensaje"}`.

## Ejemplos

### curl

```bash
curl -s "https://tarifaelectrica.v2charge.com/v1/prices/tomorrow?tariff=2.0TD" -H "X-API-Key: mi-casa"
```

### Python

```python
import time, requests

BASE = "https://tarifaelectrica.v2charge.com"
HEAD = {"X-API-Key": "mi-integracion"}

def get(path, **params):
    r = requests.get(BASE + path, params=params, headers=HEAD, timeout=20)
    if r.status_code == 429:
        time.sleep(int(r.headers.get("Retry-After", 10)))
        r = requests.get(BASE + path, params=params, headers=HEAD, timeout=20)
    r.raise_for_status()
    return r.json()

day = get("/v1/prices", tariff="2.0TD")            # una petición para todo el día
cheapest = min(day["prices"], key=lambda h: h["compra_eur_kwh"])
print(cheapest["ts_local"], round(cheapest["compra_eur_kwh"] * 100, 1), "c€/kWh")
```

### JavaScript

```js
const r = await fetch("https://tarifaelectrica.v2charge.com/v1/prices/cheapest?tariff=2.0TD&n=3&block=3");
if (r.status === 429) { /* esperar r.headers.get("Retry-After") segundos */ }
const { best_block } = await r.json();
console.log(`Mejor franja: ${best_block.from} → ${best_block.to}`);
```

### Home Assistant (sensor REST)

```yaml
sensor:
  - platform: rest
    name: Precio luz indexada
    resource: https://tarifaelectrica.v2charge.com/v1/prices/now?tariff=2.0TD
    headers:
      X-API-Key: home-assistant-mi-casa
    value_template: "{{ value_json.compra_eur_kwh }}"
    unit_of_measurement: "€/kWh"
    json_attributes: [period, venta_eur_kwh, provisional]
    scan_interval: 900
```

### Node-RED

Nodo `http request` (GET) a `https://tarifaelectrica.v2charge.com/mi-id/indexada/{{fecha}}/`, salida
"a parsed JSON object"; los valores horarios están en `msg.payload.data`.

## Cómo se calcula (para explicar a usuarios)

```
compra = k_periodo × mercado + w × ajustes + peaje + cargo
venta  = mercado − descuento
```

- mercado: precio del mercado diario de OMIE (España), media horaria de los cuartos de hora.
- ajustes: servicios de ajuste y otros costes del sistema publicados por Red Eléctrica (ESIOS).
- k, w, descuento: coeficientes del producto (pérdidas de red y margen de comercialización).
- peaje (CNMC) y cargo (Ministerio para la Transición Ecológica) según el periodo.
- Periodos según la Circular 3/2020 de la CNMC, con fines de semana y festivos nacionales.

Fuentes: OMIE, REE (ESIOS), CNMC, MITECO. Los precios no incluyen IVA ni impuesto eléctrico.
