datagrit

datagrit › Public procurement › TED Contract Expiry Radar - Recompete Leads

Public procurement

TED Contract Expiry Radar - Recompete Leads

Find EU public contracts approaching expiry from TED award notices: incumbent, buyer, value, end date and renewal options.

Run it on Apify StoreUse the APIfrom $7.00 per 1,000 results + $10 per run · no code needed
from $7.00 per 1,000 results + $10 per runpay only for expiring contracts you get
JSON · CSV · Excelexport or call via API
Scheduled runsdaily or weekly feeds with Apify schedules
v0.6updated 2026-09-30

TED Contract Expiry Radar turns contract award notices from the official EU tender portal TED into a list of public contracts that are about to end across Europe. For every lot it returns the incumbent supplier, the buying authority and its country, the award value, the contract end date, the days left, contract length, renewal options and CPV codes. You choose the expiry window, for example the next 12 months, and optionally filter by keyword, buyer country, CPV code, supplier name and minimum value. Export the result as JSON, CSV or Excel, call it through the Apify API, or plug it into n8n, Make and AI agents through MCP.

Who is it for?

How to use it

  1. Set the expiry window in months from today: 0 to 12 lists contracts ending within a year, 6 to 18 skips those that are already too close to tender.
  2. Optionally add keywords, buyer countries (two-letter codes such as DE, FR, PL), CPV prefixes (for example 72 for IT services, 90 for waste and cleaning, 45 for construction), incumbent names and a minimum value.
  3. Set maximum results and run the Actor.
  4. Download the dataset or fetch it from the API.

Results are read in monthly expiry slices, so with a maximum results limit you get the soonest-expiring contracts first.

Estimated end dates

Most award notices state how long the contract runs but not the exact end date. When Estimate missing end dates is on, the Actor computes the end date from the start date, or from the contract conclusion date, plus the published duration. The field endDateSource tells you where every date comes from: stated means the buyer published it, start+duration and award+duration mean it was calculated. Turn the option off to keep only stated end dates. Estimated dates ignore extensions and early termination, so treat them as a strong lead rather than a legal fact.

Why it matters: many award notices publish how long a contract runs but not the date it ends. The Actor computes that date from the lot start date or the contract conclusion date plus the duration, and marks every row with endDateSource. In two complete live runs on 30 September 2026, contracts ending within the next month gave 7,053 lots with a stated end date and 9,052 more with a computed one (16,105 rows), and contracts ending in two to three months gave 7,465 stated and 12,064 computed (19,529 rows). See the FAQ for what is not covered.

What data do you get?

Each record is one awarded lot. It includes the TED notice number and link, notice and contract titles, buyer name, country and city, incumbent supplier with country, size class and registry identifier, all winners of the notice when they cannot be matched to a single lot, award value and currency, award, publication and contract start dates, contract end date with its source, days and months to expiry, contract length, renewal options and terms, framework agreement flag and type, main and additional CPV codes, contract nature and the number of lots in the notice.

Is it legal to use this data?

The Actor reads only information that the European Union publishes openly through the TED search API. It does not log in or bypass any access control. The data covers organisations and public contracts. You are responsible for using it in line with applicable law, including data protection rules. This description is not legal advice.

Output fields

Every result is one flat record, so it drops straight into a spreadsheet, a database or a CRM.

FieldTypeDescriptionExample
querystringThe keyword from your input found in this lot (lot title, lot description, contract title or notice title); null only when you gave no keywords.software
idstringStable identifier: TED notice number plus the lot. One award notice can hold several lots, each returned as its own row. Empty only on the single status row emitted when nothing matched.406127-2023/LOT-0001
noticeNumberstringPublication number of the contract award notice on TED.406127-2023
lotIdstringLot identifier inside the notice.LOT-0001
titlestringTitle of the award notice as published on TED.Estonia – Software package and information systems – Public transport ticketing
contractTitlestringTitle of the contract given by the buyer, when it differs from the notice title. Filled only when the notice has one contract title; empty when several contracts have different titles.Ticketing system maintenance
lotTitlestringTitle of this lot in the language of the notice. Matched to the lot by its identifier; empty when TED does not list one title per lot.Los 2 Dienstleistung
lotDescriptionstringDescription of this lot in the language of the notice, cut to 1000 characters. Matched to the lot by its identifier; empty when TED does not list one description per lot.Support services for the licence administration.
sourceUrlstringLink to the award notice on TED.https://ted.europa.eu/en/notice/-/detail/406127-2023
buyerNamestringContracting authority that awarded the contract, i.e. who will buy again.Tallinna Transpordiamet
buyerCountrystringThree-letter ISO country code of the buyer.EST
buyerCitystringCity of the buyer.Tallinn
incumbentNamestringSupplier that won this lot. Null when the notice does not allow a safe match of winner to lot; see allIncumbents.AS Connecto Eesti
incumbentCountrystringThree-letter ISO country code of the incumbent supplier.EST
incumbentSizestringCompany size class of the incumbent as declared in the notice (micro, small, medium, large).sme
incumbentIdstringCompany registration identifier of the incumbent as published in the notice.10201316
allIncumbentsstringNames of all winning suppliers in the notice, separated by semicolons. Filled when winners cannot be matched to a single lot.CIR FOOD S.C.; VIVENDA SPA
awardValuenumberAwarded value of the lot in the currency given in the currency field.500000
currencystringCurrency code of awardValue.EUR
awardDatestringDate the contract was concluded (YYYY-MM-DD). Filled only when the notice has one contract or all its contracts were concluded the same day; TED does not say which contract belongs to which lot, so otherwise empty.2023-10-16
publishedAtstringDate the award notice was published (YYYY-MM-DD).2023-11-13
contractStartstringContract start date when stated in the notice (YYYY-MM-DD).2023-09-06
contractEndstringContract end date (YYYY-MM-DD), stated by the buyer or computed; see endDateSource.2026-09-05
endDateSourcestringHow contractEnd was obtained: stated (published by the buyer), start+duration or award+duration (estimated from the published duration).start+duration
daysToExpiryintegerDays from the run date to contractEnd.158
monthsToExpirynumberMonths from the run date to contractEnd, one decimal.5.2
contractLengthMonthsnumberPublished contract duration in months.36
renewalOptionsMaxintegerMaximum number of renewals the buyer reserved, when stated.1
renewalTermsstringDescription of the renewal option, cut at 400 characters.The contract may be extended once for up to 12 months.
frameworkAgreementbooleanTrue when the lot is a framework agreement rather than a stand-alone contract.false
frameworkTypestringFramework agreement type as published, for example with or without reopening of competition.fa-w-rc
cpvCodestringMain CPV code of the lot (from TED main-classification-lot); the first code of the notice when TED gives no lot code.72000000
cpvScopestringlot when cpvCode is the lot's own code, notice when it falls back to the notice-level classification.lot
cpvCodesstringAll CPV codes of the notice, separated by commas.72000000, 48000000
contractNaturestringSupplies, services or works.services
lotCountintegerNumber of lots in the award notice (TED lot list, identifier-lot); when TED gives no lot list, the number of awarded results.3
foundbooleanFalse only for the single status row emitted when a run has no results.true
scrapedAtstringISO 8601 timestamp of extraction.2026-09-30T08:00:00.000Z

Sample record

{
  "query": "software",
  "id": "406127-2023/LOT-0001",
  "noticeNumber": "406127-2023",
  "lotId": "LOT-0001",
  "title": "Estonia – Software package and information systems – Public transport ticketing software",
  "contractTitle": "Ticketing system maintenance",
  "lotTitle": "Los 2 Dienstleistung",
  "lotDescription": "Support services for the licence administration.",
  "sourceUrl": "https://ted.europa.eu/en/notice/-/detail/406127-2023",
  "buyerName": "Tallinna Transpordiamet",
  "buyerCountry": "EST",
  "buyerCity": "Tallinn",
  "incumbentName": "AS Connecto Eesti",
  "incumbentCountry": "EST",
  "incumbentSize": "sme",
  "incumbentId": "10201316",
  "allIncumbents": "CIR FOOD S.C.; VIVENDA SPA",
  "awardValue": 500000,
  "currency": "EUR",
  "awardDate": "2023-10-16",
  "publishedAt": "2023-11-13",
  "contractStart": "2023-09-06",
  "contractEnd": "2026-09-05",
  "endDateSource": "start+duration",
  "daysToExpiry": 158,
  "monthsToExpiry": 5.2,
  "contractLengthMonths": 36,
  "renewalOptionsMax": 1,
  "renewalTerms": "The contract may be extended once for up to 12 months.",
  "frameworkAgreement": false,
  "frameworkType": "fa-w-rc",
  "cpvCode": "72000000",
  "cpvScope": "lot",
  "cpvCodes": "72000000, 48000000",
  "contractNature": "services",
  "lotCount": 3,
  "found": true,
  "scrapedAt": "2026-09-30T08:00:00.000Z"
}

Input

FieldNameTypeWhat it does
queriesKeywordsarrayOptional keywords. TED full-text search finds candidate notices, and a lot is returned only when a keyword appears in its own title or description, or in the contract or notice title (case-insensitive, in the language of the notice). Leave empty to list all expiring contracts.
countriesBuyer countriesarrayOptional. Two-letter country codes of the contracting authority, for example DE, FR, PL, NL, ES, IT. Leave empty for all countries covered by TED.
cpvPrefixesCPV code prefixesarrayOptional. Keep only contracts whose CPV classification starts with one of these digits, for example 72 (IT services), 90 (waste and cleaning) or 45 (construction).
incumbentsIncumbent supplier namesarrayOptional. Keep only contracts won by suppliers whose name contains one of these words, for example to see when a competitor's contracts end.
expiresAfterMonthsExpires no earlier than (months from today)integerStart of the expiry window. 0 means contracts ending today or later.
expiresBeforeMonthsExpires no later than (months from today)integerEnd of the expiry window. 12 returns contracts ending within the next twelve months. Results are read in monthly slices, soonest expiry first.
minValueMinimum contract valuenumberOptional. Minimum awarded value in the contract's own currency (mostly EUR). Contracts without a published value are excluded when this is above 0.
includeFrameworkAgreementsInclude framework agreementsbooleanTurn off to drop framework agreements and keep only stand-alone contracts.
estimateMissingEndDatesEstimate missing end datesbooleanMost notices state a contract duration but no end date. When on, the end date is computed from start date (or award date) plus duration and marked in endDateSource. Turn off to return only end dates stated by the buyer.
maxItemsMaximum resultsintegerStop after this many contracts.
proxyConfigurationProxy configurationobjectOptional proxy. The TED API is public and needs no proxy; leave disabled.

Call it from your code

Run the Actor and get the results in one request. Replace YOUR_APIFY_TOKEN with the token from your Apify account settings.

curl -X POST "https://api.apify.com/v2/acts/datagrit~ted-contract-expiry-radar/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queries":["software"],"expiresAfterMonths":0,"expiresBeforeMonths":12,"maxItems":20}'
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('datagrit/ted-contract-expiry-radar').call({
  "queries": [
    "software"
  ],
  "expiresAfterMonths": 0,
  "expiresBeforeMonths": 12,
  "maxItems": 20
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);

Install with npm i apify-client.

from apify_client import ApifyClient
import os

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("datagrit/ted-contract-expiry-radar").call(run_input={
  "queries": [
    "software"
  ],
  "expiresAfterMonths": 0,
  "expiresBeforeMonths": 12,
  "maxItems": 20
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(len(items), items[0] if items else None)

Install with pip install apify-client.

Frequently asked questions

Does it list contracts that are not yet awarded?

No. It covers awarded contracts and shows when each one ends. Use the expiry window to find recompete opportunities early.

Why are some contracts missing?

Four reasons. First, a notice with neither an end date nor a duration cannot be placed in an expiry window, and lots without a selected winner are skipped. Second, contracts with a stated end date are always found, but for estimated end dates the Actor asks TED only about common durations: 3, 6, 9, 12, 15, 18, 24, 30, 36, 42, 48, 60, 72, 84, 96 and 120 months, and 1 to 8 and 10 years. Contracts published with another length (for example 4, 11 or 20 months) or with a duration in days or weeks are found only when they also state an end date. In a sample of 2023 notices this leaves out roughly 7 percent of those that publish a duration. Third, TED lists conclusion dates per signed contract, not per lot, and does not say which contract belongs to which lot. When a notice has several contracts with different conclusion dates, the Actor does not guess: awardDate and contractTitle stay empty, and the end date is computed only from the lot's own start date. This is the most common reason a notice found by the duration search gives no row; the run status says how many of those notices produced one. Fourth, TED publishes the per-lot fields (end dates, durations, titles, CPV codes, renewal terms) as lists that follow the notice's lot list, and it sometimes leaves entries out: one notice with 46 lots had 45 end dates. The Actor matches each awarded lot to its entry by the lot identifier and uses a list only when it has exactly one entry per lot; otherwise those fields stay empty and a lot that cannot be placed in the window is not returned, because guessing would attach another lot's date. TED lets a single query read at most 15,000 notices, and 11 of the 24 queries of the default 12-month window match more. The Actor splits such a query into shorter date windows on its own, so nothing is dropped. Only a single day with more than 15,000 notices would be cut, and then the run status says INCOMPLETE and names the day.

How long does a run take and are there limits?

TED rate-limits its public search API, so the Actor sends about one request per second. Results are read in monthly expiry slices, two queries per month (stated dates, then estimated ones). A window of 12 months means up to 24 queries. Each query can span many pages of 100 notices, one request per page. In a measurement on 30 September 2026 the default 12-month window without filters matched about 251,000 notices (64,000 for stated dates, 187,000 for estimated ones), which is roughly 2,600 requests and about 45 minutes; a window of two to three months took about 8 minutes. Narrow the window, add a country or CPV prefix, or set a maximum number of results to finish faster. Most runs finish much sooner, because the Actor stops as soon as it reaches your maximum results on the first pages. TED matches notices, not single lots: a notice is returned when any of its lots fits the window, and the Actor then keeps only the lots that really fit, so many lots read are discarded. The run status reports how many lots had a stated, estimated or missing end date. The Actor stops as soon as it reaches your maximum results, and a run that is migrated to another server continues without charging twice for the results it already returned.

The run failed with "none has a contract end date". What does it mean?

TED returned awarded lots but none of them carried an end date or duration, which points to a change in the source data rather than to an empty search. The run fails on purpose so that you are not told there are no expiring contracts. Try again later or open an issue.

Which countries are covered?

All buyers that publish award notices on TED, that is the EU member states and several other European countries.

Why is the incumbent empty for some lots?

When a notice has several lots and the winners cannot be matched to lots reliably, the Actor leaves the incumbent empty and lists all winners in `allIncumbents` instead of guessing.

How current is the data?

Every run reads the live TED API. Notices appear on TED shortly after publication.

Can I schedule it?

Yes, use Apify schedules or call the Actor from your own workflow.

Something looks wrong.

Open an issue with the input you used; layout or API changes at the source are fixed quickly.

Try TED Contract Expiry Radar - Recompete Leads on Apify

Related Actors

Company data

French Company Finder - Sirene Financials

French company lead lists from Sirene screened by net result and revenue, with net margin, size, matching establishment and optional directors.

from $5.60 / 1,000 results
Jobs & salaries

Career Site Jobs Aggregator - ATS Salary Data

Open jobs from Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters, Recruitee and Personio in one schema with normalized salaries.

from $2.80 / 1,000 results