API REST
Para tus aplicaciones, scripts e integraciones (ERP, CRM, hojas de cálculo, automatizaciones). Se autentica con una API key.
Planes Pro y Enterprise de pago
Lleva las licitaciones públicas de España a tus aplicaciones con una API REST, o conecta tu asistente de IA con el servidor MCP. Los datos son los de la Plataforma de Contratación del Sector Público y las plataformas autonómicas que ya usas en la app, actualizados a diario.
https://api.licitaciones365.es/v1https://api.licitaciones365.es/mcpBeta. Estamos abriendo el acceso por fases. Si al usar tu clave recibes el error beta_cerrada, escríbenos a contact-us@licitaciones365.es y te damos acceso.
Licitaciones365 ofrece dos puertas a los mismos datos y a la misma cuenta, con los permisos y los límites de tu plan:
Para tus aplicaciones, scripts e integraciones (ERP, CRM, hojas de cálculo, automatizaciones). Se autentica con una API key.
Planes Pro y Enterprise de pago
Para asistentes de IA compatibles con el Model Context Protocol, como ChatGPT y Claude. Se conecta con OAuth: el usuario inicia sesión y autoriza.
Planes Plus, Pro y Enterprise, y la prueba gratuita
La API está en https://api.licitaciones365.es/v1. Con ella puedes buscar licitaciones con los mismos filtros que el buscador de la app, ver su ficha por secciones y sus documentos; gestionar las favoritas y las descartadas del equipo, la nota, las tareas y los recordatorios de cada licitación y la agenda de plazos; crear, probar y ajustar alertas; consultar los contratos que vencen, los órganos de contratación, los adjudicatarios y estadísticas de mercado; y usar la IA de Licitaciones365 sobre cada licitación. La referencia recoge todas las operaciones.
Si buscas usar Licitaciones365 desde ChatGPT o Claude sin programar, tienes los pasos en ChatGPT y Claude.
Cada petición lleva una API key en la cabecera Authorization:
Authorization: Bearer l365_tu_claveSi tu herramienta solo te deja indicar el nombre de una cabecera (Copilot Studio, Power Automate…), envíala en X-API-Key: l365_tu_clave.
l365_.plan_insuficiente) hasta que vuelva a subir.X-Equipo: <id> (los ids salen en GET /cuenta). Sin ella, trabaja con el equipo con el que la creaste.solo_lectura. Empieza con solo lectura si no necesitas más.Guarda tu clave en la variable de entorno L365_API_KEY (la usan todos los ejemplos) y comprueba que funciona con GET /cuenta, que no consume cuota diaria:
curl https://api.licitaciones365.es/v1/cuenta \
-H "Authorization: Bearer $L365_API_KEY"{
"data": {
"usuario": { "id": 1234, "email": "ana@empresa.es", "nombre": "Ana García" },
"equipo": { "id": 567, "nombre": "Licitaciones", "plan": "Pro", "rol": "owner" },
"credencial": { "tipo": "api_key", "nombre": "ERP de ofertas", "escritura": false },
"cuotas_api_hoy": {
"busquedas_dia": { "limite": 1000, "usado": 37 },
"fichas_dia": { "limite": 2000, "usado": 112 },
…
},
"datos_actualizados": "2026-10-13T21:47:12+02:00",
…
}
}La respuesta dice con qué usuario, equipo y plan trabaja la clave, si puede escribir y cuánto has usado hoy de cada cuota.
El servidor MCP permite a un asistente de IA buscar licitaciones y trabajar con los datos del equipo en nombre del usuario. Usa el transporte HTTP en streaming (Streamable HTTP) en:
https://api.licitaciones365.es/mcpEs lo que usa el botón «Conectar» de los asistentes. El usuario no comparte su contraseña: inicia sesión en Licitaciones365, elige el equipo y si el asistente puede hacer cambios o solo consultar, y autoriza. Disponible en los planes Plus, Pro y Enterprise y en la prueba gratuita.
S256), con registro dinámico de clientes (RFC 7591) en /oauth/register./.well-known/oauth-protected-resource y /.well-known/oauth-authorization-server. Una petición sin token recibe un 401 con la cabecera WWW-Authenticate que apunta a esos metadatos.claude.ai, claude.com), ChatGPT (chatgpt.com, chat.openai.com) y las locales (localhost, 127.0.0.1), que usan clientes de escritorio y de terminal como Claude Code.En clientes que permiten cabeceras propias, puedes conectar el MCP con una API key (planes Pro y Enterprise de pago). Por ejemplo, en Claude Code:
claude mcp add --transport http licitaciones365 https://api.licitaciones365.es/mcp \
--header "Authorization: Bearer l365_..."La clave trabaja con su equipo y su permiso, igual que en la API. Los pasos para conectar Claude y ChatGPT con OAuth están en ChatGPT y Claude.
Los nombres y las descripciones de las herramientas están en español, para que el asistente sepa cuándo usar cada una. Cada conexión ve solo las que su plan y su permiso permiten: con permiso de solo lectura no aparecen las que escriben. Lo marcado como «Pro y Enterprise» también está en la prueba gratuita.
Cada herramienta declara si solo consulta, si añade algo o si modifica o borra (las marcas readOnlyHint y destructiveHint del protocolo), y los asistentes se basan en ellas: lo que solo consulta lo ejecutan sin preguntar, para añadir piden permiso salvo que el usuario lo haya dejado permitido, y antes de modificar o borrar piden confirmación. El detalle depende de cómo tenga configurados los permisos cada asistente.
contexto_cuentaConsultaUsuario, equipo, plan, límites, presupuesto de IA y cuotas de hoy.
buscar_codigosConsultaTraduce sectores y territorios a códigos: CPV, provincias, comunidades, estados, tipos de contrato…
buscar_licitacionesConsultaBúsqueda con los filtros del buscador de la app, 25 resultados por página.
ver_licitacionConsultaFicha por secciones (o el resumen de hasta 30 a la vez). La sección de vencimiento, en Pro y Enterprise.
listar_documentosConsultaEnlaces a los pliegos y anuncios en el portal oficial.
listar_favoritasConsultaFavoritas del equipo («Mis concursos») y sus grupos.
anadir_favoritaEscribeAñadir una licitación a favoritas, si quieres en un grupo.
mover_favoritaEscribeDestructivaCambiar una favorita de grupo o dejarla sin grupo.
quitar_favoritaEscribeDestructivaQuitar de favoritas (también desactiva sus recordatorios pendientes).
descartar_licitacionEscribeMarcar una licitación como «No me interesa».
deshacer_descarteEscribeDestructivaQuitar la marca «No me interesa».
listar_alertasConsultaAlertas del equipo, con los ajustes del resumen y sus destinatarios.
probar_alertaConsultaCuántas y cuáles licitaciones habría traído una alerta en los últimos 30 días.
crear_alertaEscribeCrear una alerta. Propietario del equipo.
modificar_alertaEscribeDestructivaCambiar, activar o pausar una alerta. Propietario del equipo.
borrar_alertaEscribeDestructivaBorrar una alerta. Propietario del equipo.
historial_resumenConsultaLo que se envió cada día en el resumen y los cambios en favoritas.
ajustes_resumenEscribeDestructivaCambiar los ajustes del correo de resumen y sus destinatarios. Pro y Enterprise; propietario del equipo.
espacio_licitacionConsultaNota, tareas, recordatorios y conversaciones de IA de una licitación.
anadir_a_notaEscribeAñadir texto al final de la nota del equipo.
reemplazar_notaEscribeDestructivaSustituir la nota del equipo por otra.
crear_tareaEscribeCrear una tarea en el checklist.
modificar_tareaEscribeDestructivaCambiar o completar una tarea. Asignarla a alguien, en Pro y Enterprise.
borrar_tareaEscribeDestructivaBorrar una tarea del checklist.
crear_recordatorioEscribeCrear un recordatorio de un plazo.
modificar_recordatorioEscribeDestructivaPausar o reactivar un recordatorio.
borrar_recordatorioEscribeDestructivaBorrar un recordatorio.
agendaConsultaCalendario de plazos y vencimientos. Tus tareas, en Pro y Enterprise.
Las que generan algo gastan presupuesto de IA del equipo y necesitan permiso de escritura; las de consulta, no.
preguntar_licitacionEscribePregunta a la IA, que responde leyendo los pliegos; la conversación queda en la app.
consultar_respuesta_iaConsultaRecoge una respuesta que aún estaba en curso.
generar_checklist_iaEscribePropuesta de tareas para presentarse, a partir de los pliegos. Pro y Enterprise.
ver_checklist_iaConsultaLa última propuesta de tareas de la IA. Pro y Enterprise.
importar_checklist_iaEscribePasa las tareas propuestas al checklist del equipo. Pro y Enterprise.
generar_informe_pdfEscribeInforme PDF de la licitación, con un enlace de descarga que caduca a los 15 minutos.
ver_informe_pdfConsultaEstado del informe y enlace de descarga.
analizar_competenciaEscribeCalcula quién suele ganar en el segmento de un lote y con qué bajas. Pro y Enterprise.
ver_competenciaConsultaEl último análisis de competencia del equipo, sin coste. Pro y Enterprise.
recomendar_cpvEscribeLos 10 CPV que mejor encajan con la descripción de una empresa.
buscar_vencimientosConsultaContratos que vencen pronto, con filtros y estadísticas. Pro y Enterprise.
buscar_organos_adjudicatariosConsultaDirectorios de órganos de contratación y de empresas adjudicatarias.
estadisticas_mercadoConsultaEstadísticas por CPV, provincia y comunidad autónoma.
El servidor ofrece también estos atajos (prompts del MCP), que el asistente muestra para lanzar una tarea completa con un clic:
buscar_oportunidadesBuscar oportunidades para mi empresa. Licitaciones abiertas que encajan con tu actividad, tu zona y el importe que te interesa, con su plazo y por qué encajan.analizar_licitacionAnalizar una licitación. Alcance, importes, plazos, criterios, solvencia, riesgos y si encaja con tu empresa.preparar_ofertaPreparar la oferta de una licitación. Plan de trabajo para presentarse: documentación, tareas con fechas, responsables y recordatorios.novedadesNovedades y próximos plazos. Lo que ha llegado por tus alertas, los cambios en tus favoritas y los plazos y tareas que tienes por delante.En el MCP, los errores (plan, permisos, límites, parámetros) llegan como resultado de la herramienta, con un mensaje en español para que el asistente se lo explique al usuario. Las llamadas del MCP cuentan en las mismas cuotas diarias del equipo que la API.
Los límites dependen del plan del equipo. Los de por minuto cuentan por credencial (cada API key o cada asistente conectado); los diarios, por equipo, sumando todas sus credenciales, y se renuevan a medianoche (hora de Madrid).
| Límite | Plus | Pro y prueba gratuita | Enterprise |
|---|---|---|---|
| Consultas por minuto por credencial | 60 | 120 | 240 |
| Cambios por minuto por credencial | 15 | 30 | 60 |
| Búsquedas al día por equipo | 300 | 1.000 | 3.000 |
| Fichas de detalle al día por equipo | 500 | 2.000 | 6.000 |
cuotas_api_hoy de GET /cuenta (en el MCP, la herramienta contexto_cuenta).429 limite_superado con la cabecera Retry-After en segundos.busqueda_demasiado_amplia: acótala con más filtros.Todas las respuestas son JSON con los datos en data y, cuando hay información adicional (paginación, filtros aplicados…), en meta:
{
"data": [ … ],
"meta": {
"paginacion": { "pagina": 1, "por_pagina": 25, "total": 312, "total_es_minimo": false, "hay_mas": true },
"filtros": { … }
}
}importe_sin_iva.{"codigo": "PUB", "nombre": "En plazo"}.2026-10-02T09:15:00+02:00); fechas sin hora como AAAA-MM-DD._con_iva.null; en algunas secciones de la ficha, la clave sin dato no aparece.estado=PUB,EV.pagina y por_pagina; sigue pidiendo mientras meta.paginacion.hay_mas sea true.url_app abre la licitación en la app de Licitaciones365; url_origen, en el portal oficial.202 con estado: "en_curso" y se recogen después con su GET. Con el parámetro esperar (hasta 25 segundos), la misma llamada espera a que terminen./v1 podemos añadir recursos y campos nuevos, así que ignora los que no conozcas. Un cambio incompatible iría en una versión nueva de la API.Los errores llevan el estado HTTP que corresponde y este cuerpo. Usa code en tu código: es estable. message está en español para mostrarlo a una persona y puede cambiar.
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json
{
"error": {
"code": "limite_superado",
"message": "Demasiadas peticiones: con el plan Pro cada credencial puede hacer 120 consultas por minuto. Vuelve a intentarlo en 42 s."
}
}{
"error": {
"code": "parametros_invalidos",
"message": "El campo por_pagina debe ser como máximo 100.",
"details": {
"por_pagina": ["El campo por_pagina debe ser como máximo 100."]
}
}
}| code | HTTP | Cuándo | Qué hacer |
|---|---|---|---|
no_autenticado | 401 | Falta la cabecera Authorization. | Envía |
credencial_invalida | 401 | La clave no existe, ha caducado o se ha revocado, o su equipo ya no existe. También si usas en | Crea una clave nueva en la app (Perfil → Integraciones). |
cuenta_no_disponible | 403 | La cuenta de la clave está suspendida o se ha borrado. | Escríbenos si crees que es un error. |
plan_insuficiente | 403 | El plan del equipo no incluye la API (o el MCP), o la operación necesita un plan superior. | Revisa el plan del equipo en la app. |
beta_cerrada | 403 | Las integraciones están en beta cerrada y tu cuenta todavía no tiene acceso. | Pídenos acceso por correo. |
sin_equipo | 403 | Ya no perteneces al equipo de la clave. | Usa una clave de un equipo del que seas propietario. |
solo_propietario | 403 | La clave no es del propietario del equipo: solo él puede crear y usar las API keys de su equipo. | Pide al propietario del equipo que cree la clave. |
equipo_bloqueado | 403 | El equipo está bloqueado por los límites del plan. | Gestiónalo en «Gestionar equipos» de la app. |
solo_lectura | 403 | Una petición que cambia datos con una clave de solo lectura. | Usa una clave con permiso de escritura. |
sin_permiso | 403 | La acción está reservada a otro rol, por ejemplo al propietario del equipo. También si | Pide al propietario que la haga o que te dé acceso. |
presupuesto_ia_agotado | 403 | El equipo ha agotado el presupuesto mensual de IA de su plan. | Espera a que se renueve; buscar y consultar licitaciones sigue funcionando. |
limite_del_plan | 403 | El equipo ha llegado a un límite de su plan, por ejemplo el máximo de favoritas. | Libera espacio (por ejemplo, quitando favoritas) o amplía el plan. |
no_encontrado | 404 | El recurso o la ruta no existen (o no tienes acceso a ellos). | Revisa el identificador y la ruta. |
metodo_no_permitido | 405 | La ruta existe, pero no con ese método HTTP. | Consulta la referencia. |
ya_es_favorita | 409 | La licitación ya está en las favoritas del equipo. | Para cambiarla de grupo, usa |
es_favorita | 409 | Una favorita no se puede descartar. | Quítala antes de favoritas ( |
ia_en_curso | 409 | Ya hay una ejecución de IA en curso con esta clave: solo puede haber una a la vez. | Espera a que termine y vuelve a intentarlo. |
sin_checklist_ia | 409 | Has pedido importar un checklist IA que todavía no se ha generado. | Genéralo antes con |
parametros_invalidos | 422 | Algún parámetro no es válido. Si es un error de formato, | Corrige los parámetros; el mensaje dice cuál falla. |
busqueda_demasiado_amplia | 422 | La búsqueda ha superado el tiempo máximo. | Acótala: fechas de publicación, provincia, CPV, estado o un texto más concreto. |
limite_superado | 429 | Has superado el límite por minuto de la clave o una cuota diaria del equipo. | Espera los segundos que indica la cabecera |
error_interno | 500 | Un error inesperado de nuestro lado. | Reintenta más tarde; si se repite, escríbenos. |
Si recibes un código que no está en la lista, trátalo según su estado HTTP.
Los pliegos, anexos y anuncios de cada licitación se devuelven como enlaces al portal oficial donde se publicaron (la Plataforma de Contratación del Sector Público o el portal autonómico), igual que en la app. Ni la API ni el MCP descargan, guardan ni sirven su contenido.
pliego_adm:…, pliego_tec:…, anexo:…, general:…, anuncio:…, modweb:…), su tipo, nombre, fecha y el dominio del portal (fuente).retirado: true.persona_fisica_protegida: true. Solo se muestran el nombre y el NIF de empresas identificadas con certeza; en caso de duda, se protegen. Lo mismo vale en el MCP: estos datos acaban en sistemas de terceros.Además de los términos y condiciones de Licitaciones365, al usar la API o el MCP aceptas estas condiciones:
fuente.Los casos más comunes, en curl, Python (con requests) y JavaScript (Node.js 18 o superior, con fetch). Al elegir un lenguaje, se elige en todos los ejemplos de la página.
Busca las licitaciones en plazo de un CPV en una comunidad autónoma y recorre todas las páginas de 100 en 100. Cada página consume una búsqueda de la cuota diaria.
curl -G "https://api.licitaciones365.es/v1/licitaciones" \
--data-urlencode "cpv=77310000" \
--data-urlencode "comunidad=ES52" \
--data-urlencode "estado=PUB" \
--data-urlencode "por_pagina=100" \
--data-urlencode "pagina=1" \
-H "Authorization: Bearer $L365_API_KEY"import os
import requests
API = "https://api.licitaciones365.es/v1"
CABECERAS = {"Authorization": f"Bearer {os.environ['L365_API_KEY']}"}
def licitaciones_en_plazo(cpv, comunidad):
"""Devuelve una a una las licitaciones en plazo, página a página."""
pagina = 1
while True:
respuesta = requests.get(
f"{API}/licitaciones",
params={
"cpv": cpv,
"comunidad": comunidad,
"estado": "PUB",
"por_pagina": 100,
"pagina": pagina,
},
headers=CABECERAS,
timeout=30,
)
cuerpo = respuesta.json()
if not respuesta.ok:
raise RuntimeError(f"{cuerpo['error']['code']}: {cuerpo['error']['message']}")
yield from cuerpo["data"]
if not cuerpo["meta"]["paginacion"]["hay_mas"]:
return
pagina += 1
for licitacion in licitaciones_en_plazo("77310000", "ES52"):
plazo = licitacion["plazo_presentacion"] or {}
print(licitacion["id"], licitacion["titulo"], licitacion["importe_sin_iva"], plazo.get("fecha"))const API = "https://api.licitaciones365.es/v1";
const cabeceras = { Authorization: `Bearer ${process.env.L365_API_KEY}` };
async function* licitacionesEnPlazo(cpv, comunidad) {
for (let pagina = 1; ; pagina++) {
const params = new URLSearchParams({
cpv,
comunidad,
estado: "PUB",
por_pagina: "100",
pagina: String(pagina),
});
const respuesta = await fetch(`${API}/licitaciones?${params}`, { headers: cabeceras });
const cuerpo = await respuesta.json();
if (!respuesta.ok) throw new Error(`${cuerpo.error.code}: ${cuerpo.error.message}`);
yield* cuerpo.data;
if (!cuerpo.meta.paginacion.hay_mas) return;
}
}
for await (const licitacion of licitacionesEnPlazo("77310000", "ES52")) {
console.log(licitacion.id, licitacion.titulo, licitacion.importe_sin_iva, licitacion.plazo_presentacion?.fecha);
}Antes de filtrar por sector, busca sus códigos CPV por texto. El mismo endpoint traduce provincias, comunidades, estados y tipos de contrato.
curl -G "https://api.licitaciones365.es/v1/catalogos/cpv" \
--data-urlencode "texto=limpieza de edificios" \
-H "Authorization: Bearer $L365_API_KEY"import os
import requests
respuesta = requests.get(
"https://api.licitaciones365.es/v1/catalogos/cpv",
params={"texto": "limpieza de edificios"},
headers={"Authorization": f"Bearer {os.environ['L365_API_KEY']}"},
timeout=30,
)
respuesta.raise_for_status()
for cpv in respuesta.json()["data"]:
print(cpv["codigo"], cpv["descripcion"])const params = new URLSearchParams({ texto: "limpieza de edificios" });
const respuesta = await fetch(`https://api.licitaciones365.es/v1/catalogos/cpv?${params}`, {
headers: { Authorization: `Bearer ${process.env.L365_API_KEY}` },
});
const { data } = await respuesta.json();
for (const cpv of data) console.log(cpv.codigo, cpv.descripcion);Pide solo las secciones que necesitas. Los documentos son enlaces al portal oficial: tu código decide si los abre o los guarda.
curl -G "https://api.licitaciones365.es/v1/licitaciones/4815162" \
--data-urlencode "secciones=general,condiciones,documentos" \
-H "Authorization: Bearer $L365_API_KEY"import os
import requests
respuesta = requests.get(
"https://api.licitaciones365.es/v1/licitaciones/4815162",
params={"secciones": "general,condiciones,documentos"},
headers={"Authorization": f"Bearer {os.environ['L365_API_KEY']}"},
timeout=30,
)
respuesta.raise_for_status()
ficha = respuesta.json()["data"]
print(ficha["titulo"], "·", ficha["estado"]["nombre"])
for criterio in (ficha["condiciones"] or {}).get("criterios_adjudicacion", []):
print("Criterio:", criterio.get("descripcion"), criterio.get("peso"))
for documento in ficha["documentos"]:
if not documento["retirado"]:
print(documento["tipo"], documento["url"])const params = new URLSearchParams({ secciones: "general,condiciones,documentos" });
const respuesta = await fetch(`https://api.licitaciones365.es/v1/licitaciones/4815162?${params}`, {
headers: { Authorization: `Bearer ${process.env.L365_API_KEY}` },
});
const { data: ficha } = await respuesta.json();
console.log(ficha.titulo, "·", ficha.estado.nombre);
for (const criterio of ficha.condiciones?.criterios_adjudicacion ?? []) {
console.log("Criterio:", criterio.descripcion, criterio.peso);
}
for (const documento of ficha.documentos.filter((d) => !d.retirado)) {
console.log(documento.tipo, documento.url);
}La IA de Licitaciones365 responde leyendo los pliegos de la licitación. Necesita una clave con permiso de escritura y gasta presupuesto de IA del equipo. La primera pregunta sobre una licitación puede tardar unos minutos: si no llega dentro de «esperar», recoge la respuesta con su run_id.
# 1. La pregunta (espera hasta 20 s a la respuesta)
curl -X POST "https://api.licitaciones365.es/v1/licitaciones/4815162/ia/preguntas" \
-H "Authorization: Bearer $L365_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pregunta": "¿Qué solvencia técnica piden y cómo se acredita?", "esperar": 20}'
# 2. Si responde 202 (estado "en_curso"), recoge la respuesta con su run_id
curl "https://api.licitaciones365.es/v1/ia/consultas/98231?esperar=20" \
-H "Authorization: Bearer $L365_API_KEY"import os
import requests
API = "https://api.licitaciones365.es/v1"
CABECERAS = {"Authorization": f"Bearer {os.environ['L365_API_KEY']}"}
def datos(respuesta):
cuerpo = respuesta.json()
if not respuesta.ok:
raise RuntimeError(f"{cuerpo['error']['code']}: {cuerpo['error']['message']}")
return cuerpo["data"]
consulta = datos(requests.post(
f"{API}/licitaciones/4815162/ia/preguntas",
json={"pregunta": "¿Qué solvencia técnica piden y cómo se acredita?", "esperar": 20},
headers=CABECERAS,
timeout=60,
))
while consulta["estado"] == "en_curso":
consulta = datos(requests.get(
f"{API}/ia/consultas/{consulta['run_id']}",
params={"esperar": 20},
headers=CABECERAS,
timeout=60,
))
print(consulta.get("respuesta") or consulta.get("mensaje"))const API = "https://api.licitaciones365.es/v1";
const cabeceras = {
Authorization: `Bearer ${process.env.L365_API_KEY}`,
"Content-Type": "application/json",
};
async function datos(peticion) {
const respuesta = await peticion;
const cuerpo = await respuesta.json();
if (!respuesta.ok) throw new Error(`${cuerpo.error.code}: ${cuerpo.error.message}`);
return cuerpo.data;
}
let consulta = await datos(
fetch(`${API}/licitaciones/4815162/ia/preguntas`, {
method: "POST",
headers: cabeceras,
body: JSON.stringify({ pregunta: "¿Qué solvencia técnica piden y cómo se acredita?", esperar: 20 }),
}),
);
while (consulta.estado === "en_curso") {
consulta = await datos(fetch(`${API}/ia/consultas/${consulta.run_id}?esperar=20`, { headers: cabeceras }));
}
console.log(consulta.respuesta ?? consulta.mensaje);Ante un 429, espera los segundos de la cabecera Retry-After. Si son muchos, se ha agotado una cuota diaria: no reintentes hasta el día siguiente.
# -i muestra las cabeceras: ante un 429, mira Retry-After
curl -i "https://api.licitaciones365.es/v1/cuenta" \
-H "Authorization: Bearer $L365_API_KEY"import os
import time
import requests
CABECERAS = {"Authorization": f"Bearer {os.environ['L365_API_KEY']}"}
def get_con_reintentos(url, params=None, intentos=3):
for _ in range(intentos):
respuesta = requests.get(url, params=params, headers=CABECERAS, timeout=30)
if respuesta.status_code != 429:
return respuesta
espera = int(respuesta.headers.get("Retry-After", "5"))
if espera > 120:
# Cuota diaria agotada: se renueva a medianoche (hora de Madrid).
raise RuntimeError(respuesta.json()["error"]["message"])
time.sleep(espera)
return respuestaconst cabeceras = { Authorization: `Bearer ${process.env.L365_API_KEY}` };
async function getConReintentos(url, intentos = 3) {
for (let intento = 0; intento < intentos; intento++) {
const respuesta = await fetch(url, { headers: cabeceras });
if (respuesta.status !== 429) return respuesta;
const espera = Number(respuesta.headers.get("Retry-After") ?? 5);
if (espera > 120) {
// Cuota diaria agotada: se renueva a medianoche (hora de Madrid).
throw new Error((await respuesta.json()).error.message);
}
await new Promise((resolver) => setTimeout(resolver, espera * 1000));
}
throw new Error("Demasiados reintentos");
}La referencia interactiva tiene todas las operaciones, con sus parámetros, respuestas y errores, generadas a partir de la especificación OpenAPI 1.0.0. Pulsa «Authorize», pega tu API key y prueba cualquier operación con «Try it out». También puedes importar la especificación en Postman, Insomnia o tu generador de clientes. Todas las rutas cuelgan de https://api.licitaciones365.es/v1.
Crea tu API key en la app o conecta tu asistente. Si algo no está claro o echas en falta un recurso, escríbenos a contact-us@licitaciones365.es.