If your EU TED query returns fewer notices than the TED website shows, you may not have a bug. The maintainers have explained why in a public thread, and there are simple ways to query around it.

Disclosure: I maintain the Actor mentioned at the end. The script is free and stands on its own.

What the maintainers said

In ted-open-data discussion 17, a user's SPARQL query for publication number 00305240-2025 returned nothing, although the notice was visible on the TED website. The reply from the TED team was:

  • The information had not yet been propagated to Cellar, their triple store, and that normally takes about one day.
  • Later in the thread another user reported that notices two weeks old were still missing from SPARQL. In September 2025 a maintainer replied that up to 31 July 2025 they had only transformed competition and result notices for SDK 1.3 to 1.10, and now transform all of them up to SDK 1.13. A backlog of earlier notices was being worked through, updated daily, with coverage from 25 June 2025 onwards at that time.

So two things stack up: a short propagation delay for new notices, and a backlog for older ones. I cannot tell how much of that applies today, so treat the thread as the source and check your own data.

Two ways in, two behaviours

  • SPARQL / open data. This is the linked-data (RDF) view of notices. It is the right tool when you want the relationships between entities. Per the maintainers, a notice appears there only after it has been transformed into the triple store.
  • Search API v3. A JSON POST to https://api.ted.europa.eu/v3/notices/search with an expert-search query. In my test below it needed no key, and it returned the notice from that thread (publication number 305240-2025, published 2025-05-13), the one the SPARQL query could not find. Note the number is used without the leading zeros.

I am not claiming v3 never lags, only that it found that notice. Docs: https://docs.ted.europa.eu/api/latest/search.html

How to query so you do not miss things

  1. Use date windows, not "since last run". Query publication-date>=START AND publication-date<=END over a range.
  2. Overlap and re-poll. Re-run the last several days each time, so anything that appears late is picked up on the next pass.
  3. Dedupe by notice ID. Overlapping windows return the same notices again. Key your storage on publication-number.
  4. Page with the iteration token. Set paginationMode to ITERATION and pass back iterationNextToken until a page comes back empty.
  5. Reconcile. Compare a window's totalNoticeCount with what you stored, and alert on gaps.

Free script

Standard library only:

import datetime, json, urllib.request URL = "https://api.ted.europa.eu/v3/notices/search" FIELDS = ["publication-number", "publication-date", "notice-title"] def search(query, token=None): body = {"query": query, "fields": FIELDS, "limit": 100, "paginationMode": "ITERATION"} if token: body["iterationNextToken"] = token req = urllib.request.Request(URL, data=json.dumps(body).encode(), headers={"Content-Type": "application/json"}, method="POST") with urllib.request.urlopen(req, timeout=60) as r: return json.load(r) def window(start, end, extra=""): q = f"publication-date>={start:%Y%m%d} AND publication-date<={end:%Y%m%d}" seen, token = {}, None while True: page = search(q + (f" AND {extra}" if extra else ""), token) for n in page["notices"]: seen[n["publication-number"]] = n # dedupe by notice ID  token = page.get("iterationNextToken") if not page["notices"] or not token: return seen if __name__ == "__main__": end = datetime.date.today() start = end - datetime.timedelta(days=7) a = window(start, end, "buyer-country=DEU AND classification-cpv=72*") b = window(start, end, "buyer-country=DEU AND classification-cpv=72*") # re-poll  print(len(a), "notices", start, "to", end, "| re-poll adds", len(set(b) - set(a))) by_day = {} for n in a.values(): d = n["publication-date"][:10] by_day[d] = by_day.get(d, 0) + 1 print(dict(sorted(by_day.items()))) for n in list(a.values())[:2]: t = n["notice-title"] print(n["publication-number"], t.get("eng") or next(iter(t.values()))) 

I ran it on 2026-11-08 for German buyers and IT-services CPV codes (72*) over the previous seven days:

158 notices 2026-11-01 to 2026-11-08 | re-poll adds 0 {'2026-11-02': 38, '2026-11-03': 32, '2026-11-04': 23, '2026-11-05': 28, '2026-11-06': 37} 756775-2026 Germany – Software integration consultancy services – Rahmenvertrag Anlageneinbindung MES System - PR1242292-9940 757094-2026 Germany – IT services: consulting, software development, Internet and support – Rahmenvertrag für Dienstleistungen in den Bereichen Usability, User Experience und kontinuierliche Erfolgsmessung 

The second poll added nothing because I ran both back to back. The value of re-polling shows up over days. The days with no notices (1, 7 and 8 November) fall on a weekend, which I expect but did not investigate. The thread's notice also comes back when queried by number: publication-number=305240-2025 returns publication date 2025-05-13.

If you would rather not run it yourself

I publish an Apify Actor over the TED API for scheduled searches without code. It costs $0.40 per 1,000 rows, plus standard Apify platform usage. Examples are in the examples repo. If the script covers your needs, use it.

Not affiliated with or endorsed by the EU Publications Office.