A company that has just won a public contract is about to spend money. It has a delivery deadline, and it often needs to hire, subcontract and buy equipment, software and services. For a B2B sales team, a recruiter or a subcontractor looking for a prime, that is a strong buying signal.
The data is public. The US, the UK and the EU all publish who won which contract, for how much and from which buyer. The problem is that each one publishes it in its own format, on its own portal, with its own codes, and none of them sends you a tidy list of yesterday's winners in your market.
This tutorial shows how to get that list every morning from three Actors on the Apify Store, filter it by industry code, and send it to a CRM or a Google Sheet. I built these Actors, so I will also point out where the source data has gaps.
The three sources
| Market | Actor | Source | Industry codes |
|---|---|---|---|
| United States | agents001/us-federal-contract-awards |
USAspending.gov API (US Treasury) | NAICS and PSC |
| United Kingdom | agents001/uk-public-contracts-feed |
Find a Tender and Contracts Finder (Cabinet Office) | CPV |
| European Union | agents001/eu-ted-tenders-feed |
TED Search API (EU Publications Office) | CPV |
| Australia | AusTender feed, coming soon | AusTender | UNSPSC |
All three return company-level records only. Awards to private individuals, such as sole traders listed under their own name, are dropped, and no contact names, emails or phone numbers are collected. Each Actor is independent and is not affiliated with or endorsed by the governments or agencies that publish the data.
The record shape differs a little per market, because the sources differ, but each record tells you the same four things: who won, who bought, how much, and what it was for.
Step 1: Run each one once in the Apify Console
Open each Actor's Store page (links at the end) and start it with the example input. Then narrow it to your market.
US federal awards. The fields that matter:
-
Award types:
contractsby default.idvs(contract vehicles such as IDIQs and GSA schedules) andgrantsare optional. -
Awarded in the last N days (
daysBack): default 7. -
NAICS codes (
naicsCodes): codes or prefixes, so5415covers all of computer systems design. -
Product or service codes (
pscCodes): for exampleDorR425. - US states with Match states against set to the recipient address or the place of performance.
- Minimum award amount (USD) and Businesses only, which leaves out universities, nonprofits and government bodies.
UK contracts. Set What to return to awards, then use CPV codes (cpvCodes). Trailing zeros mean "everything below", so 72000000 matches all IT services. You can add Regions, Minimum value (GBP) and Winning supplier size (sme or large). Each award record carries the supplier's Companies House number when the notice includes one, which makes CRM matching much easier.
EU contracts. Set What to return to awards, pick Buyer countries (DE, DEU or Germany all work) and CPV codes. Winner countries filters by where the winning company is based, which is useful if you only sell in one territory.
Step 2: Schedule a daily onlyNew feed
Scheduled, the same inputs give you a list every morning with only the winners you have not seen yet.
- Switch on
onlyNewon each Actor. In the US Actor it is labelled Only awards not seen in previous runs, and the UK and EU Actors have the same option for records. The Actor keeps a list of the records it has already returned for your filters in a named key-value store in your Apify account. The next run skips them, and you are only charged for new records. - Keep a short rolling window with some overlap:
daysBack3 for the US, 2 for the UK, 3 for the EU (TED counts publishing days, Monday to Friday). The date window is not part of the memory, so the overlap catches late notices without sending anything twice. - Set the maximum records field (
maxItems) to0(no limit) so a busy day is not cut short. - Save each input as a task. In Schedules, create one schedule, for example cron
0 7 * * 1-5with timezoneEurope/London, and add all three tasks to it.
Changing a filter starts a fresh history, so the first run after a change returns everything that matches again.
Step 3: Pull all three into one table with Python
If you would rather work from code, the official apify-client package can run the three tasks and merge the results into one CSV with the same columns. Install it with pip install apify-client and copy your API token from the Integrations section of the Console.
import csv
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
FEEDS = {
"agents001/us-federal-contract-awards": {
"awardTypes": ["contracts"],
"daysBack": 3,
"naicsCodes": ["5415", "518210"],
"minAwardAmount": 100000,
"businessesOnly": True,
"onlyNew": True,
"maxItems": 0,
},
"agents001/uk-public-contracts-feed": {
"mode": "awards",
"daysBack": 2,
"cpvCodes": ["72000000", "48000000"],
"minValue": 50000,
"onlyNew": True,
"maxItems": 0,
},
"agents001/eu-ted-tenders-feed": {
"mode": "awards",
"daysBack": 3,
"countries": ["DE", "FR", "NL", "IE"],
"cpvCodes": ["72000000", "48000000"],
"minValueEur": 100000,
"onlyNew": True,
"maxItems": 0,
},
}
def to_row(actor_id, r):
"""Map each market's fields onto one set of columns."""
if actor_id.endswith("us-federal-contract-awards"):
return {
"market": "US", "company": r["recipientName"], "companyId": r["recipientUei"],
"country": r.get("recipientCountry"), "buyer": r.get("awardingSubAgency"),
"value": r["awardAmount"], "currency": "USD", "code": r["naicsCode"],
"date": r["actionDate"], "url": r["awardUrl"],
}
if actor_id.endswith("uk-public-contracts-feed"):
return {
"market": "UK", "company": r["supplierName"],
"companyId": r.get("supplierCompaniesHouseNumber"),
"country": r.get("supplierCountry"), "buyer": r["buyerName"],
"value": r.get("value"), "currency": r.get("currency"), "code": r.get("mainCpvCode"),
"date": r.get("awardDate"), "url": r["noticeUrl"],
}
return {
"market": "EU", "company": r["winnerName"], "companyId": r.get("winnerIdentifier"),
"country": r.get("winnerCountry"), "buyer": r["buyerName"],
"value": r.get("valueEur"), "currency": "EUR", "code": r.get("mainCpv"),
"date": r.get("awardDecisionDate") or r.get("publicationDate"), "url": r["noticeUrl"],
}
rows = []
for actor_id, run_input in FEEDS.items():
# Starts the run and waits for it to finish. Caps spend at $2 for this run.
run = client.actor(actor_id).call(run_input=run_input, max_total_charge_usd=2)
if run is None or run.status != "SUCCEEDED":
print(f"{actor_id}: run did not succeed, skipping")
continue
for item in client.dataset(run.default_dataset_id).iterate_items():
rows.append(to_row(actor_id, item))
with open("new_contract_winners.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=list(rows[0]) if rows else ["market"])
writer.writeheader()
writer.writerows(rows)
print(f"{len(rows)} new winners saved")
call() returns None if the run could not be started or followed, so the check matters. iterate_items() pages through the dataset for you.
What the output looks like
These are real records from sample runs in October 2026, trimmed to the most useful fields.
A US award (NAICS 541519, other computer related services):
{
"awardId": "1232SA27F0002",
"awardType": "DELIVERY ORDER",
"recipientName": "DSFEDERAL, INC",
"recipientUei": "WHLSDNWNCGJ5",
"recipientCity": "ROCKVILLE",
"recipientState": "MD",
"awardAmount": 2715265.2,
"actionDate": "2026-10-21",
"awardingAgency": "Department of Agriculture",
"awardingSubAgency": "Agricultural Research Service",
"naicsCode": "541519",
"pscCode": "R499",
"description": "GRANTS MANAGEMENT REPORTING SYSTEM (GMRS) APPLICATIONS DEVELOPMENT AND ENHANCEMENT SUPPORT",
"awardUrl": "https://www.usaspending.gov/award/CONT_AWD_1232SA27F0002_12H2_47QTCA25D00DU_4732"
}
A UK award published under the Procurement Act 2023:
{
"recordType": "award",
"source": "findATender",
"noticeId": "100750-2026",
"noticeType": "UK7",
"noticeTypeName": "Contract details notice",
"legalFramework": "Procurement Act 2023",
"title": "Lifts Servicing and Maintenance",
"supplierName": "CALANDINE LIFTS LIMITED",
"supplierCompaniesHouseNumber": "05541807",
"supplierRegion": "East Midlands",
"supplierSize": "sme",
"buyerName": "NOTTINGHAM COMMUNITY HOUSING ASSOCIATION LIMITED",
"mainCpvCode": "42416000",
"mainCpvDescription": "Lifts, skip hoists, hoists, escalators and moving walkways",
"value": 900000.0,
"currency": "GBP",
"awardDate": "2026-10-01",
"noticeUrl": "https://www.find-tender.service.gov.uk/Notice/100750-2026"
}
An EU award, with the value converted to euros at the ECB reference rate:
{
"recordType": "award",
"noticeId": "736814-2026",
"publicationDate": "2026-10-23",
"cpvDescription": "Software programming and consultancy services",
"mainCpv": "72200000",
"buyerName": "Ministerstvo práce a sociálních věcí",
"buyerCountry": "CZE",
"value": 4620000,
"currency": "CZK",
"valueEur": 189912.44,
"winnerName": "Aricoma Systems a.s.",
"winnerCountry": "CZE",
"winnerSize": "large",
"winnersInNotice": 1,
"awardDecisionDate": "2026-09-08",
"noticeUrl": "https://ted.europa.eu/en/notice/-/detail/736814-2026"
}
A note on the UK Procurement Act 2023
The Procurement Act 2023 has been in force since 24 February 2025. Procurements started since then are published on Find a Tender with new notice types (UK6 for contract award notices, UK7 for contract details notices and so on) and a different legal basis from notices under the old regulations. Older procurements that are still running are published under the old rules, so for some years you will see both.
If you build your own client on the Find a Tender API, check the stages parameter. When it was tested on 23 October 2026, stages=award, stages=tender and stages=planning only returned notices under the old regulations and left out every Procurement Act 2023 notice. In a sample run of 500 recent UK award records, 317 came from Procurement Act notices, so that filter would have dropped most of them.
The Actor does not use that filter. It reads all stages and classifies each notice itself, and every record has a legalFramework field so you can see which regime it falls under. It also returns a win reported first in a contract award notice (UK6) and later in a contract details notice (UK7) only once.
What it costs
All three use pay-per-event pricing with platform usage included. You pay per record saved to your dataset. Records your filters drop are not charged. Prices below are per 1,000 records on the Free plan, and lower on paid Apify plans:
- US awards: $3.00 per 1,000 award records, down to $1.50 on higher plans.
- UK awards: $2.50 per 1,000 contract award records, down to $1.50. Open tenders, if you ask for them, are $2.00 per 1,000.
- EU awards: $2.50 per 1,000 award records, down to $1.75. Open tenders are $1.50 per 1,000.
A filtered daily feed of 30 new US winners, 20 UK winners and 20 EU winners costs about $0.09 + $0.05 + $0.05, so roughly $0.19 a day or $4 over 21 working days on the Free plan prices. Set a maximum charge per run and the Actor stops cleanly when it is reached. Check each Store page for current pricing.
Send it to a CRM or a Google Sheet
You can connect each task in its Integrations tab.
-
Google Sheets: add the Google Sheets Import & Export Actor (
lukaskrivka/google-sheets) as an Actor integration on the task, in append mode, so each morning's winners land under yesterday's. -
CRM: use the Apify app in Zapier, Make or n8n. Trigger on a finished task run, fetch the dataset items, then create or update a company in HubSpot, Salesforce or Pipedrive. Use a stable key so you never create duplicates:
recipientUeifor US awards,supplierCompaniesHouseNumberfor UK awards where it is filled, andnoticeIdpluswinnerNamefor EU awards. - Email: the Gmail integration can send each run's dataset as an attachment.
- Webhook: point a webhook at your own endpoint on the "run succeeded" event and fetch the dataset from there.
Limitations
- Delays at source. Most US civilian contracts appear within a few days. Department of Defense contracts are published with a delay, so recent weeks show few DoD awards. Grants appear weeks after signing.
-
Values mean different things. US contract amounts are the amount obligated so far and can grow. UK and EU framework awards often state a ceiling shared by every supplier on the framework (
valueSharedin the UK,winnersInNoticein the EU). - Missing identifiers. Companies House numbers are present when the notice includes them. TED only ties a company number, country and size to a winner when the match is unambiguous, and roughly 1 EU award notice in 10 names no winner.
- Coverage. The UK feed covers Find a Tender and Contracts Finder. Public Contracts Scotland, Sell2Wales and eTendersNI carry some notices that are not on those two services.
- Privacy filters err on the side of removal, so a few small businesses with person-like names are left out.
Always check the official notice before acting on a record.
Try it
- US federal contract awards: https://apify.com/agents001/us-federal-contract-awards
- UK public contracts: https://apify.com/agents001/uk-public-contracts-feed
- EU TED tenders and awards: https://apify.com/agents001/eu-ted-tenders-feed
- Australian Government contracts (AusTender): coming soon
Top comments (0)