South Africa Government Supplier Tracker
Pricing
from $5.00 / 1,000 results
South Africa Government Supplier Tracker
Which suppliers win South African government tenders: eTenders awards grouped by supplier, with buyers, signed contract values and official links.
Pricing
from $5.00 / 1,000 results
Rating
0.0
(0)
Developer
Mastazee Data Lab
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
11 days ago
Last modified
Categories
Security
Limited permissionsShare
See which companies win South African government tenders, how often, from which buyers, and for how much. This Actor reads National Treasury's official eTenders open-data API and groups the awards by winning supplier. You get one row per supplier with their award count, the total value of their signed contracts, their main buyers and categories, and a link to every official award record.
Use it to:
- Watch competitors. Follow named rivals (by name or eTenders supplier id) and get a row whenever they win something new.
- Find partners and subcontractors. See who keeps winning security, cleaning, IT or construction work in your province.
- Size a market. Count awards and known contract values per supplier for a buyer or category.
Tested on 26 October 2026 against the live eTenders API:
- Default input: 500 tenders scanned in about a minute, 38 suppliers from 39 awards (217 tenders skipped by the eTenders server error described below).
- Monitor mode: a second run of the same monitor straight after the first returned 0 suppliers, repeating nothing.
- On the Apify platform: the same default run took about 1.5 minutes, and a repeat run of a monitor again returned 0 suppliers.
This is a data tool, not a lead guarantee. Coverage is what buyers publish on eTenders (see Data limitations).
Example input
{"supplierNames": ["security"],"provinces": ["KwaZulu-Natal"],"toOcid": 0,"recordsToScan": 15000,"onlyNewAwards": true,"stateKey": "kzn-security"}
Leave every filter empty to see all suppliers in the scanned range. Schedule this weekly with onlyNewAwards on, and each run returns only suppliers with awards you haven't received yet. Monitors with different filters keep separate memories, even under the same stateKey.
Example output (one supplier, trimmed)
{"supplier_key": "id:1442842","supplier_name": "QEDANATION","supplier_id": "1442842","name_variants": ["QEDANATION"],"other_ids_with_same_name": [],"award_count": 2,"awards_with_known_value": 0,"awards_with_unknown_value": 2,"total_contract_value_zar": null,"first_tender_published_date": "2026-03-06","buyers": [{"name": "Ingquza Hill Local Municipality", "awards": 2}],"categories": [{"name": "goods", "awards": 2}],"awards": [{"ocid": "ocds-9t57fa-150000", "award_id": "66010", "buyer_name": "Ingquza Hill Local Municipality","tender_title": "PROCUREMENT OF STATIONERY", "contract_value_zar": null,"source_url": "https://ocds-api.etenders.gov.za/api/OCDSReleases/release/ocds-9t57fa-150000"}]}
Fields
| Field | Meaning |
|---|---|
supplier_key | id:<eTenders supplier id>, or name:<normalised name> when eTenders gives no id |
supplier_name, name_variants | The most common spelling and every spelling seen |
other_ids_with_same_name | Other eTenders supplier ids using the same name. These are not merged (see below) |
award_count | Awards in the scanned range. Separate awards in one tender count separately |
awards_with_known_value, awards_with_unknown_value | How many awards have a signed contract value |
total_contract_value_zar, largest_contract_value_zar | Sum and maximum of known contract values only. null when none is known |
first_/last_contract_signed_date | Earliest and latest contract signing date (eTenders has no award date) |
first_/last_tender_published_date | When the related tenders opened |
buyers, categories, buyer_provinces | Counts of awards per buyer, category and province |
enterprise_sizes | EME / QSE / GEN as recorded by eTenders |
awards | Each award with its official source_url (turn off with includeAwardList) |
How suppliers are matched (conservatively)
- Awards are grouped by eTenders' own supplier id, which stays the same across a company's awards.
- Different ids are never merged, even if the names match. You'll see them listed in
other_ids_with_same_nameand can decide for yourself. - Awards without a supplier id are grouped by name, separately from any id-based group.
- Only case, spaces and punctuation are ignored when comparing names. "ACME" and "ACME (PTY) LTD" stay separate suppliers.
Data source and attribution
eTenders OCDS API, published by National Treasury (South Africa) under the Open Data Commons PDDL. Every award links to its official record, as the eTenders publication policy asks. No API key is needed.
Data limitations (please read)
- About 25–45% of eTenders records can't be read. The official API returns a server error for them (217 of 500 in the default range, 50 of the newest 200 on 26 October 2026). They're skipped and counted in the run's
RUN_SUMMARY; re-running the range picks them up if Treasury fixes them. - Values come only from signed contracts. eTenders' own award amounts are published with the decimal point dropped (10× or 100× too high), so they're never used. About a third of awards have a contract value. The rest are counted as unknown.
- There's no award date. Contract signing date and tender publication date are given instead.
- Province comes from the buyer's name (provincial departments and the big metros). National departments and most municipalities have none.
- Coverage: eTenders covers national and provincial government plus the municipalities and state entities that choose to publish there. It isn't every public contract in South Africa.
- Personal contact details are never output. eTenders attaches officials' names, phones and emails to awards.
Running time
The Actor checks tender numbers one by one (about 110 new tenders a day), roughly 8–10 a second at the default 4 parallel requests. Measured on 26 October 2026:
| Scan | Time | Suppliers |
|---|---|---|
| 500 tenders (default, May 2026) | ~1 min | 38 |
| Newest ~800 tenders (about a week) | ~1.5 min | ~125 |
| 15,000 tenders (~4–5 months) | ~25–30 min (estimated) | roughly 800–2,000 (estimated) |
Pricing
Pay per event: $5.00 per 1,000 supplier rows ($0.005 per supplier), plus Apify's standard tiny start fee. Each row is one supplier with all its awards in the scanned range, and you are charged only for rows written to your dataset.
| How you use it | Supplier rows | You pay (approx.) |
|---|---|---|
| Default run (500 tenders) | ~38 | ~$0.19 |
| Weekly run over the newest ~800 tenders, no filter, for a month | ~550 | ~$2.75 a month |
| Weekly watch list of a few named competitors | 0–a few a week | cents a month |
- No double billing in monitor mode: with
onlyNewAwards, an award is delivered once. A supplier comes back only when it has an award you haven't received. - Spending limits: if you set a maximum cost per run, the Actor stops cleanly when it's reached. Suppliers it didn't deliver come back on the next monitor run.
- Overlapping runs: if a scheduled run starts while the previous run of the same monitor is still going, it stops at once without output or charges.
Troubleshooting
- Few rows? Many awards are added weeks or months after a tender opens. To catch them, scan more tenders (15,000 is about 4–5 months) or start from an older number.
- Many "skipped" tenders in the summary: that's the eTenders server error described above, not a fault in the Actor.
- Run failed with "could not be reached": the eTenders API is down. Try again later.
- "Only new awards" returned nothing: everything in the range was already delivered to this monitor. Use a new
stateKeyto start over. - Summary says
status: skipped: another run of the same monitor was still in progress, so this one stopped without output or charges.
Known restrictions
- Up to 50,000 tenders per run, and up to 8 parallel requests, to stay polite to a government server.
onlyNewAwardsremembers up to 200,000 awards per monitor.- Up to 36,000 seconds of scanning per run (
maxRuntimeSeconds, default 3,300). - A run that hits
maxRuntimeSecondsoutputs what it found. Its summary saysstatus: partialand gives the lowest tender number reached.