La API pública de TED: consultarla es fácil, saber qué es nuevo no
TED publica cada día laborable los contratos que los compradores públicos europeos deben anunciar. Su API es abierta, pero encadenar bien la sintaxis y deduplicar avisos lleva más trabajo del que parece.

El Diario Oficial de la UE tiene un suplemento dedicado a licitaciones, TED, y todo comprador público europeo está obligado a publicar ahí los contratos que superan los umbrales comunitarios. El martes 22 de septiembre de 2026 aparecieron 1.648 anuncios de contrato nuevos, en 24 idiomas. El buscador es público y su API de búsqueda no pide clave ni cuenta. Sobre esa base, un desarrollador ha contado cómo montó un vigilante que corre una vez por día laborable y avisa solo de lo aparecido desde la ejecución anterior. Consultar la API fue lo fácil; decidir qué cuenta como nuevo, no.
La llamada es un único POST a https://api.ted.europa.eu/v3/notices/search, con la sintaxis de búsqueda experta del sistema y la lista de campos que se quieren de vuelta. El código, Node 18 o superior, usa el fetch que ya trae el runtime. cn-standard es el anuncio de contrato ordinario, el que lleva plazo de presentación. Los países van en ISO 3166 alpha-3 (ESP, DEU, IRL), así que si el usuario escribe ES hay que convertir el código antes.
Dos filtros que fallan en silencio
La sintaxis tiene trampas que no devuelven error. FT~("software" OR "cloud") se acepta, pero da resultados falsos: en dos semanas devolvió 120 anuncios, mientras que FT~("software") por separado daba 1.586. Encadenando cláusulas, (FT~("software") OR FT~("cloud")) subió a 1.869. Peor es una comilla o una barra invertida dentro de una palabra clave: no rompe la consulta, elimina el filtro. Una palabra con barra invertida devolvió los 28.072 anuncios del mes. La solución es limpiar ambos caracteres antes de construir la consulta.
Para paginar usa el modo ITERATION, porque el modo por número de página se corta en 15.000 resultados (página por límite). El techo de limit es 250 y cada respuesta trae un iterationNextToken que se va devolviendo. Con SORT BY publication-date el cursor perdió avisos: devolvió 568 de 673 coincidencias, probablemente por empates en la fecha. Ordenando por publication-number DESC llegaron las 673, también con lo más reciente primero.
En cuanto a los límites, la política de uso justo habla de 700 peticiones por minuto por IP, pero el autor se comió un 429 de nginx tras unas diez peticiones en dos segundos. Deja 400 ms entre llamadas y reintenta ante 429 y 5xx. Y una respuesta 200 puede traer timedOut: true, es decir, resultados parciales; la trata como error, porque vigilar una lista con agujeros es peor que esperar.
La fecha límite no es un solo campo
Los plazos vienen partidos: fecha y hora en campos distintos, cada uno con su desplazamiento UTC y un valor por lote. Hay que pegarlos en un instante y, si el anuncio tiene varios lotes, quedarse con el plazo más próximo que no haya pasado. La sorpresa mayor son los anuncios sin plazo de presentación: en una muestra de 1.000 anuncios de contrato, el 9% no tenía ninguno, solo plazo para solicitar participar. Por eso recorre tres parejas de campos en orden —tender, request-to-participate, expression-of-interest— y anota cuál usó en cada caso.
El núcleo del problema es otro. Si vigilas "software" en España, hay licitaciones abiertas todos los días, y una alerta que dispare con cualquier resultado dispara a diario con la lista de ayer. El vigilante guarda por cada búsqueda los números de publicación ya vistos, en un almacén clave-valor, y compara. Suena a cinco líneas y acabó necesitando cuatro reglas. La primera: la ejecución inicial es una línea base, no una alerta. En su ejemplo, esa primera pasada encontró 170 anuncios coincidentes.
Ese es el patrón que se repite en casi cualquier API pública que se consulte a diario: la autenticación es lo de menos y el trabajo real está en definir el cambio.
