Introducción
La API de PriceVault es REST sobre HTTPS, con peticiones y respuestas en JSON. Todos los endpoints
programáticos viven bajo el prefijo /v1. Esta página documenta los endpoints destacados;
para la referencia exhaustiva de todos los endpoints y esquemas dispones de la especificación OpenAPI:
- Swagger UI — interactivo, prueba endpoints desde el navegador.
- ReDoc — referencia completa, limpia y legible.
Autenticación
Cada petición se autentica con tu API key en la cabecera X-API-Key. Genera y gestiona tus
claves desde el dashboard. Trátalas como un secreto: nunca las publiques en el frontend ni en repositorios.
curl "https://pricevault.es/v1/offers" \
-H "X-API-Key: TU_API_KEY"
- API key inválida o inactiva →
401 Unauthorized. - Cuenta desactivada →
403 Forbidden.
Scopes
Cada API key puede limitarse a un conjunto de scopes (permisos). Si llamas a un endpoint sin el
scope requerido, la respuesta es 403 con el scope que falta.
| Scope | Permite |
|---|---|
offers:read | Consultar ofertas, comparar y recomendaciones |
offers:write | Crear, editar y eliminar ofertas |
offers:export | Exportar ofertas a CSV |
commissions:read | Ver comisiones (ofertas, ranking, recomendación, plantillas y calculadora) |
indexed_prices:read | Consultar precios indexados históricos |
companies:read | Consultar comercializadoras |
companies:write | Crear, editar y eliminar comercializadoras |
clients:read | Consultar asesorías (clientes) |
clients:write | Crear, editar y eliminar asesorías (clientes) |
catalog:read | Consultar catálogo de ofertas compartidas |
catalog:write | Crear, editar y eliminar entradas del catálogo |
Claves vinculadas a una asesoría
Una API key puede estar vinculada a una sola asesoría. Cuando lo está, el parámetro client_id
de las consultas se fuerza a esa asesoría; pasar otro distinto devuelve 403. Es el detalle
que más confusión genera al integrar.
Convenciones
Paginación
Los listados aceptan limit (1–200, por defecto 50) y offset. La respuesta
envuelve los datos: { "total", "limit", "offset", "results": [...] }.
Estado de los registros
Por defecto solo se devuelven registros activos; usa include_inactive=true para incluir los
desactivados. El borrado (DELETE) sobre ofertas, asesorías y comercializadoras es lógico
(is_active = false), no físico.
Códigos de error
| Código | Significado |
|---|---|
400 | Petición o formato de archivo inválido. |
401 | Falta la API key o es inválida/inactiva. |
403 | Sin el scope requerido o sin acceso a esa asesoría. |
404 | El recurso no existe. |
409 | Conflicto (nombre duplicado, estado incompatible). |
413 | Archivo mayor de 10 MB. |
422 | Validación de negocio (territorio, tipo de cliente, etc.). |
429 | Límite de peticiones superado. |
Límites de uso
Rate limit de uso razonable (anti-abuso), no una cuota: las llamadas
no se facturan. Escala con el plan en dos niveles — un techo por cuenta y, para las
keys atadas a una asesoría concreta (client_id), un sub-límite por
cliente. Con una key de cuenta (sin client_id) solo aplica el techo. Al
superarlo, la API responde 429.
| Plan | Techo por cuenta (req/min) | Por cliente (req/min) |
|---|---|---|
| Gratis / Trial / Starter | 60 | 30 |
| Pro | 150 | 40 |
| Business | 400 | 60 |
Servidor MCP
Además de REST, PriceVault expone un servidor MCP (Model Context Protocol) para que un
agente de IA use la API como herramienta nativa, sin que escribas la integración. Es la misma API
/v1 de esta página: cada herramienta re-entra por el mismo control de autenticación, scopes
y límites de uso.
- Transporte Streamable HTTP; autenticación con la cabecera
X-API-Key, igual que en REST. -
Solo lectura. Solo se exponen endpoints de consulta y cálculo. La key dedicada de
MCP lleva
offers:readycatalog:read, y ademáscommissions:readsolo si la creas para uso interno: una key para un bot que habla con clientes no ve comisiones. - Incluido en todos los planes: las llamadas no se facturan y comparten el rate limit de la sección anterior.
Guía completa del servidor MCP — herramientas disponibles y cómo configurar Claude, n8n u otro cliente.
Listar tarifas
El corazón de la API: lista las tarifas de tu organización con filtros ricos por suministro, tarifa de acceso, territorio, comunidad, tipo de cliente y comercializadora.
Parámetros de consulta
| Parámetro | Tipo | Descripción |
|---|---|---|
| supply_type | LUZ · GAS | Tipo de suministro (por defecto LUZ). |
| tariff_type | string | Tarifa de acceso. Ej. 2.0TD, 3.0TD, RL.1. |
| company_id | int | Filtrar por comercializadora. |
| company_name | string | Búsqueda parcial por nombre (sin distinción de mayúsculas). |
| territory | enum | peninsula, canarias, baleares, ceuta_melilla. |
| community | enum | Comunidad autónoma (incluye ofertas sin restricción geográfica). |
| placas_solares | bool | Filtrar ofertas con placas solares. |
| client_type | dni · cif · cdad | Particulares (DNI), empresas (CIF) o comunidades de propietarios. |
| client_id | int | Filtrar por asesoría (forzado si la key está vinculada). |
| include_inactive | bool | Incluir ofertas inactivas. |
| limit / offset | int | Paginación. |
curl "https://pricevault.es/v1/offers?supply_type=LUZ&tariff_type=2.0TD&territory=peninsula&limit=20" \
-H "X-API-Key: $PV_KEY"
{
"total": 134,
"limit": 20,
"offset": 0,
"results": [
{
"id": 412,
"offer_name": "Tarifa Hogar 2.0TD",
"supply_type": "LUZ",
"tariff_type": "2.0TD",
"is_indexed": false,
"company": {
"id": 12, "name": "Energía X", "logo_url": "https://...",
"client": { "id": 3, "name": "Asesoría Norte", "image_url": null }
},
"e1": 0.142, "e2": 0.108, "e3": 0.085,
"p1": 0.084, "p2": 0.012,
"commission": 25.0, "commission_type": "fixed",
"acepta_dni": true, "acepta_cif": true, "acepta_cdad_propietarios": true,
"is_active": true,
"created_at": "2026-04-02T10:11:00Z",
"updated_at": "2026-05-30T08:00:00Z"
}
]
}
GAS lleva gas_energy y gas_fixed, y no incluye e1–e6, p1–p6, fee_power, placas_solares, precio_excedentes ni commission_power_tiers. Una oferta LUZ no incluye gas_energy ni gas_fixed. Lo mismo aplica a las filas de indexed_prices y a todos los endpoints que devuelven ofertas, incluido el catálogo. Trata esas claves como opcionales.
commission, commission_type, commission_tiers y commission_power_tiers (este último no existe en ofertas GAS) llegan a null en este endpoint, en GET /v1/offers/{id} y en GET /v1/catalog/offers, y las columnas de comisión del CSV de GET /v1/offers/export salen vacías. Úsala así en bots o asistentes que hablan con tus clientes.
Oferta más eficiente
El motor comparador. Dado un perfil de consumo y potencia, rankea las ofertas combinando coste y comisión con una nota de eficiencia. El peso w desplaza el equilibrio entre ahorro para el cliente y comisión para ti.
Sin el scope commissions:read el ranking es solo por precio: w vale 1, enviar un w menor devuelve 403, y commission, nota_comision y mas_comision_id llegan a null.
El coste (cost) es sin impuestos (IVA, IGIC o IPSI, impuesto eléctrico e impuesto de hidrocarburos) y sin alquiler de contador: sirve para comparar ofertas, no es el importe de la factura. Esos conceptos son iguales para todas las ofertas de un mismo cliente, así que no cambian el ranking.
Cada oferta incluye sus precios por periodo y updated_at. En las indexadas, indexada indica el precio aplicado y de dónde sale: "modo": "media" (media de meses_historico meses, de desde a hasta) o "modo": "mes" (el mes pedido en modo mensual). Las ofertas que no se pueden calcular salen en excluded con su motivo: una indexada sin histórico (o sin el mes pedido), o una oferta sin precio en un periodo donde el cliente consume, que de otro modo saldría artificialmente barata.
Cuerpo (JSON)
| Campo | Tipo | Descripción |
|---|---|---|
| supply_type obligatorio | LUZ · GAS | Tipo de suministro. |
| energia | objeto e1..e6 | Consumo por periodo en kWh durante el periodo de cálculo (por defecto un año) (LUZ). |
| potencia | objeto p1..p6 | Potencia contratada por periodo, kW (LUZ). |
| consumo_gas | float | Consumo de gas en kWh durante el periodo de cálculo. Obligatorio (>0) si supply_type=GAS. |
| dias / meses | int | Periodo de cálculo: dias para la potencia, meses para el término fijo de gas y los servicios. Si envías solo uno, el otro se deriva (dias=30 → meses≈0.99); sin ninguno, un año (365 / 12). La respuesta devuelve los dos valores usados. |
| fee_energia / fee_potencia | float 1–30 | Fee en €/MWh (fee_potencia solo LUZ). |
| w | float 0–1 | Peso ahorro↔comisión (1 = solo ahorro, 0 = solo comisión; por defecto 0.5; 1 sin commissions:read). |
| tariff_type, territory, limit | — | Filtros del universo a rankear. |
| company_name | string | Filtra por nombre de comercializadora (parcial, sin distinguir mayúsculas), en las ofertas propias y en las del catálogo. |
| company_id | int | Id de una comercializadora propia; deja fuera el catálogo. Para filtrar en general, usa company_name. |
| ssaa_included | bool | Filtra por SSAA en precio: true = incluidos, false = aparte. Omítelo para rankear todas. |
| client_type | enum | dni (particulares), cif (empresas) o cdad (comunidades de propietarios). |
curl -X POST "https://pricevault.es/v1/offers/efficient" \
-H "X-API-Key: $PV_KEY" \
-H "Content-Type: application/json" \
-d '{
"supply_type": "LUZ",
"energia": { "e1": 1200, "e2": 800, "e3": 600 },
"potencia": { "p1": 4.6, "p2": 4.6 },
"fee_energia": 5,
"fee_potencia": 3,
"w": 0.5,
"tariff_type": "2.0TD",
"limit": 10
}'
curl -X POST "https://pricevault.es/v1/offers/efficient" \
-H "X-API-Key: $PV_KEY" \
-H "Content-Type: application/json" \
-d '{
"supply_type": "GAS",
"consumo_gas": 12000,
"fee_energia": 3,
"w": 0.5,
"tariff_type": "RL.1",
"limit": 10
}'
{
"w": 0.5,
"dias": 365,
"meses": 12,
"total": 48,
"excluded_count": 1,
"excluded": [
{ "offer_id": 501, "offer_name": "Indexada Plus", "company_name": "Energía Y",
"is_catalog": false, "motivo": "indexada sin histórico de precios" }
],
"mas_barata_id": 412,
"mas_comision_id": 533,
"mas_eficiente_id": 488,
"results": [
{
"offer": {
"id": 488, "offer_name": "Tarifa Óptima",
"company": { "id": 12, "name": "Energía X", "logo_url": "..." },
"e1": 0.142, "e2": 0.118, "e3": 0.096, "p1": 0.0821, "p2": 0.0041,
"services_cost": null, "updated_at": "2026-10-01T09:12:00"
},
"indexada": null,
"cost": { "energia": 312.4, "potencia": 145.0, "fijo": 0.0, "excedentes": 0.0, "servicios": 0.0, "total": 457.4 },
"commission": { "energia": 18.0, "potencia": 6.0, "total": 24.0 },
"nota": 8.7, "nota_precio": 9.1, "nota_comision": 8.3,
"is_optimal": true
}
]
}
Recomendar por consumo y potencia
Ordena las ofertas por la comisión esperada según el consumo anual (consumption_mwh) y la potencia contratada (power_kw, solo LUZ). Cada resultado incluye la comisión por consumo y por potencia con el tramo aplicado.
curl "https://pricevault.es/v1/offers/recommend?consumption_mwh=10&power_kw=4.6&supply_type=LUZ&tariff_type=2.0TD&limit=10" \
-H "X-API-Key: $PV_KEY"
Comparar ofertas
Devuelve el detalle completo de entre 2 y 10 ofertas para mostrarlas lado a lado. Repite el parámetro ids por cada oferta.
curl "https://pricevault.es/v1/offers/compare?ids=412&ids=488&ids=533" \
-H "X-API-Key: $PV_KEY"
Calcular comisión de una oferta
Calcula el importe exacto de comisión por consumo y/o potencia para una oferta concreta, indicando el tramo aplicado.
curl "https://pricevault.es/v1/offers/412/commission-calculator?consumption_mwh=10&power_kw=4.6" \
-H "X-API-Key: $PV_KEY"
{
"offer_id": 412,
"consumption_mwh": 10,
"power_kw": 4.6,
"consumption_commission": {
"value": 25.0, "commission_type": "fixed", "matched_tier": null
},
"power_commission": {
"value": 6.0, "commission_type": "fixed",
"matched_tier": { "power_from": 0, "power_to": 10 }
}
}
Detalle de una oferta
Devuelve la oferta completa, incluyendo tramos de comisión por consumo y potencia (a null sin commissions:read), precios indexados históricos y comunidades autónomas cubiertas.
curl "https://pricevault.es/v1/offers/412" \
-H "X-API-Key: $PV_KEY"
Crear y editar ofertas
Operaciones de escritura sobre una oferta individual. El borrado es lógico: la oferta queda inactiva, no se elimina.
/v1/offersCrear una oferta.offers:write/v1/offers/{id}Editar campos (parcial).offers:write/v1/offers/{id}/toggleActivar / desactivar.offers:write/v1/offers/{id}Desactivar (soft delete).offers:writefee_power y placas_solares solo en LUZ · precio_excedentes requiere placas_solares: true.
Crea una oferta. Puedes incluir tramos de comisión por consumo (commission_tiers) y por potencia (commission_power_tiers) en la misma llamada.
curl -X POST "https://pricevault.es/v1/offers" \
-H "X-API-Key: $PV_KEY" \
-H "Content-Type: application/json" \
-d '{
"company_id": 12,
"offer_name": "Tarifa Hogar 2.0TD",
"supply_type": "LUZ",
"tariff_type": "2.0TD",
"territory_peninsula": true,
"e1": 0.142, "e2": 0.108, "e3": 0.085,
"p1": 0.084, "p2": 0.012,
"commission": 25, "commission_type": "fixed",
"acepta_dni": true, "acepta_cif": true, "acepta_cdad_propietarios": true,
"commission_tiers": [
{ "consumption_from": 0, "consumption_to": 10, "commission": 3, "commission_type": "fixed" },
{ "consumption_from": 10, "consumption_to": 50, "commission": 5, "commission_type": "fixed" }
]
}'
Carga y edición masiva
Dos formas de actualizar muchas ofertas a la vez. Ambas son todo-o-nada: si una fila falla, no se aplica ningún cambio.
/v1/offers/bulk-patchAplicar los mismos cambios a un lote (hasta 500 ofertas), vía JSON.30/minoffers:write/v1/offers/bulk-updateSubir un XLSX (≤10 MB) con hojas de ofertas, indexados y borrados.10/horaoffers:writeEdición masiva por JSON
curl -X POST "https://pricevault.es/v1/offers/bulk-patch" \
-H "X-API-Key: $PV_KEY" \
-H "Content-Type: application/json" \
-d '{
"offer_ids": [412, 488, 533],
"updates": { "is_active": false, "commission": 30 }
}'
Carga por archivo XLSX
curl -X POST "https://pricevault.es/v1/offers/bulk-update" \
-H "X-API-Key: $PV_KEY" \
-F "file=@tarifas.xlsx"
¿Necesitas la plantilla? Descárgala (precargada con tus ofertas) desde GET /v1/offers/bulk-template y /bulk-template-indexed (parámetro client_id). Como llevan las comisiones reales, piden además commissions:read.
Cada fila con offer_id reemplaza la oferta completa: una celda vacía borra ese valor. Parte siempre de la plantilla descargada, que viene rellena con los valores actuales.
Precios indexados
Histórico mensual de precios de las ofertas indexadas (is_indexed: true). El upsert crea o reemplaza el mes indicado en reference_month (formato YYYY-MM-DD).
/v1/offers/{id}/indexed-pricesListar el histórico de una oferta.indexed_prices:read/v1/offers/{id}/indexed-pricesUpsert del precio de un mes.offers:write/v1/offers/{id}/indexed-prices/{month}Borrar el precio de un mes.offers:writecurl -X POST "https://pricevault.es/v1/offers/488/indexed-prices" \
-H "X-API-Key: $PV_KEY" \
-H "Content-Type: application/json" \
-d '{
"reference_month": "2026-06-01",
"e1": 0.154, "e2": 0.143, "e3": 0.121,
"p1": 0.079, "p2": 0.065
}'
Comercializadoras
Las comercializadoras pertenecen a una asesoría (client_id). La lectura requiere el scope companies:read; la escritura, companies:write.
/v1/companiesListar (paginado).companies:read/v1/companies/{id}Detalle.companies:read/v1/companiesCrear (requiere client_id y name).companies:write/v1/companies/{id}Editar.companies:write/v1/companies/{id}/toggleActivar / desactivar.companies:write/v1/companies/{id}Desactivar.companies:writecurl -X POST "https://pricevault.es/v1/companies" \
-H "X-API-Key: $PV_KEY" \
-H "Content-Type: application/json" \
-d '{ "client_id": 3, "name": "Energía X", "logo_url": null }'
Asesorías (clientes)
Las asesorías son el primer nivel de tu organización: agrupan comercializadoras y ofertas. La lectura requiere el scope clients:read; la escritura, clients:write.
/v1/clientsListar (paginado).clients:read/v1/clients/{id}Detalle.clients:read/v1/clientsCrear. 409 si el nombre existe, 403 si superas el límite del plan.clients:write/v1/clients/{id}Editar.clients:write/v1/clients/{id}/toggleActivar / desactivar.clients:write/v1/clients/{id}Desactivar.clients:writeEstadísticas y exportación
/v1/offers/statsTotales por suministro, asesorías, comercializadoras y cargas.offers:read/v1/offers/exportExportar ofertas a CSV (máx. 2000 filas).offers:exportcurl "https://pricevault.es/v1/offers/stats" \
-H "X-API-Key: $PV_KEY"
Catálogo compartido
El catálogo central de ofertas y empresas reutilizables al que se suscriben tus asesorías. Aquí cubrimos la lectura y la carga masiva por XLSX; el resto de la escritura (scope catalog:write, límite 120/min por key) está en la referencia completa.
/v1/catalog/companiesEmpresas del catálogo.catalog:read/v1/catalog/offersOfertas del catálogo, paginadas (filtros company_id, supply_type, client_type).catalog:read/v1/catalog/offers/{id}/indexed-pricesHistórico indexado de una oferta del catálogo.catalog:readcurl "https://pricevault.es/v1/catalog/offers?supply_type=LUZ&client_type=cif" \
-H "X-API-Key: $PV_KEY"
Carga masiva por archivo XLSX
Crea, actualiza y borra ofertas del catálogo en bloque. Es todo-o-nada: si una fila falla, no se aplica ningún cambio. Los cambios llegan al instante a todas las asesorías suscritas.
/v1/catalog/offers/bulk-templatePlantilla de ofertas fijas, precargada con el catálogo (filtros supply_type, include_inactive).catalog:read/v1/catalog/offers/bulk-template-indexedPlantilla de ofertas indexadas, con la hoja precios_indexados.catalog:read/v1/catalog/offers/bulk-updateSubir un XLSX (≤10 MB) con hojas ofertas, precios_indexados, tramos_consumo, tramos_potencia y borrar.10/horacatalog:writecurl -X POST "https://pricevault.es/v1/catalog/offers/bulk-update" \
-H "X-API-Key: $PV_KEY" \
-F "file=@catalogo.xlsx"
El formato es el de la carga de ofertas de asesoría, sin la columna cliente y con catalog_offer_id en lugar de offer_id. Una fila sin id da de alta la oferta (y la comercializadora, si no existe). Los precios indexados y los tramos de una oferta creada en la misma subida se enlazan por comercializadora y nombre de oferta, con la columna del id vacía.
Cada fila con catalog_offer_id reemplaza la oferta completa: una celda vacía borra ese valor. Parte siempre de la plantilla descargada, que viene rellena con los valores actuales.
El límite de 10 subidas por hora se comparte con la subida desde el panel. Si hay filas con errores responde 422 con la lista:
{
"detail": {
"message": "El fichero contiene errores. No se ha aplicado ningún cambio.",
"errors": [
{ "row": 4, "sheet": "ofertas", "catalog_offer_id": 812, "error": "Debe marcarse al menos un territorio" }
]
}
}
Cuenta
Datos de la organización propietaria de la API key. Cualquier API key válida puede consultarlos; para modificarlos hace falta una key de acceso completo (sin scopes ni cliente asociado), o se responde 403.
/v1/accountDatos de la cuenta.sin scope/v1/accountActualizar nombre o email. Cambiar el email exige current_password (401 si no coincide; 409 si el email ya existe).30/horaacceso completo/v1/account/passwordCambiar la contraseña (mínimo 8 caracteres).5/5minacceso completocurl "https://pricevault.es/v1/account" \
-H "X-API-Key: $PV_KEY"
{
"id": 3,
"name": "Asesoría Norte",
"email": "info@asesorianorte.es",
"is_active": true,
"created_at": "2026-01-15T09:00:00Z"
}
Cambiar el email
El email es el identificador de acceso a la cuenta, así que cambiarlo pide también la contraseña actual. Para cambiar solo el nombre no hace falta.
curl -X PATCH "https://pricevault.es/v1/account" \
-H "X-API-Key: $PV_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "nuevo@asesorianorte.es",
"current_password": "••••••••"
}'
Cambiar la contraseña
curl -X POST "https://pricevault.es/v1/account/password" \
-H "X-API-Key: $PV_KEY" \
-H "Content-Type: application/json" \
-d '{
"current_password": "••••••••",
"new_password": "••••••••"
}'
Respuesta 204 No Content si el cambio es correcto; 401 si la contraseña actual no coincide.
Suscripción
El plan actual de la cuenta y sus límites. Devuelve 404 si la cuenta no tiene suscripción activa.
/v1/subscriptionPlan, estado y límite de asesorías.sin scope{
"id": 7,
"plan": "pro",
"status": "active",
"clients_limit": 25,
"trial_ends_at": null,
"current_period_end": "2026-07-15T00:00:00Z",
"created_at": "2026-01-15T09:00:00Z"
}
Uso de la API
Telemetría de tus llamadas: registro detallado y resumen agregado. Útil para depurar integraciones y vigilar el consumo.
/v1/usageRegistro de llamadas. Filtros: endpoint, status_code, from, to (ISO 8601), paginado.sin scope/v1/usage/summaryTotal de llamadas, desglose por endpoint y p95 de latencia.sin scopecurl "https://pricevault.es/v1/usage/summary" \
-H "X-API-Key: $PV_KEY"
{
"total_calls": 18432,
"calls_by_endpoint": [
{ "endpoint": "GET /v1/offers", "calls": 12045 },
{ "endpoint": "POST /v1/offers/efficient", "calls": 3210 }
],
"p95_response_time_ms": 86
}
Cargas
Seguimiento de las cargas de archivos. Tanto las subidas desde el dashboard como las cargas masivas por API (bulk-update) quedan registradas aquí.
/v1/uploadsListar. Filtros: status, client_id, paginado.sin scope/v1/uploads/{id}Detalle de una carga.sin scope/v1/uploadsRegistrar una carga (queda en estado pending).sin scope/v1/uploads/{id}Reintentar una carga fallida (409 si no está failed).sin scope/v1/uploads/{id}Cancelar una carga pendiente (409 si no está pending).sin scopecurl "https://pricevault.es/v1/uploads?status=completed&limit=20" \
-H "X-API-Key: $PV_KEY"