World Bank Tenders Scraper — Procurement & Bid Notices
Pricing
from $2.50 / 1,000 results
World Bank Tenders Scraper — Procurement & Bid Notices
World Bank procurement notices from the official API: open tenders, RFPs, expressions of interest, prequalifications and contract awards across 100+ countries. Filter by country, notice type, keyword, date. Contact emails included. B2G leads, tender alerts, procurement intel.
Pricing
from $2.50 / 1,000 results
Rating
0.0
(0)
Developer
Berkan Kaplan
Maintained by CommunityActor stats
0
Bookmarked
12
Total users
4
Monthly active users
12 days ago
Last modified
Categories
Share
World Bank Tenders & Procurement Notices 🌍
foXLabs procurement series: EU TED · IADB · Ukraine Prozorro · UK Contracts Finder · US federal awards · India
🎉 Turn the World Bank's procurement notices into clean, structured tender data — no login, no API key, one row per notice, with the project and country and, on invitations and calls, the buyer's contact person, e-mail and phone and (except on general notices) the deadline. Built for suppliers, consultants and bid teams pursuing World Bank-financed contracts.
🔍 What is World Bank Tenders — and when should you use it?
Give this Actor a notice kind (invitations and calls, contract awards or both), countries, a keyword and a date range, and it returns the matching notices from the World Bank's official procurement-notices API — 422,084 notices on 2026-10-08 — as one list, newest first, in clean rows you can filter, export or feed to an AI agent. Every run reads the API live. An invitation or call whose deadline day has passed is left out unless you ask for it.
Use it when you need: World Bank-financed invitations for bids and consultancy calls with the buyer's contact details; a regular feed of new notices in your countries; or contract-award notices as market intelligence.
Use something else when: you need the winning firm and the contract price as columns — on award notices they are inside the notice text, which the Actor can attach (includeFullText) but does not split into fields.
🤖 Use with AI agents
Already on the Apify MCP server? Ask for this Actor by name: foxlabs/worldbank-tenders. Checked on 2026-10-08: the server lists it as the tool foxlabs--worldbank-tenders.
Your agent can pay for its own runs. For an agent with a wallet and no human to sign in, Apify sells a prepaid, spend-capped API token over x402 at agi.apify.com; such a token runs any Actor that uses limited permissions, as this one does (the terms of that path are Apify's own — see apify.com/agents.md). The Actor also meets Apify's conditions for per-request agentic payments — pay-per-event only, limited permissions, no Standby — and Apify Store lists it as eligible for them (read from the Store on 2026-10-08). The Actor charges the same events either way: one per notice delivered, plus the run start.
Otherwise paste this into Claude, ChatGPT, Cursor or any MCP-enabled assistant:
I want World Bank procurement notices from the Apify Actor `foxlabs/worldbank-tenders`.Input: `status` is "open" (invitations for bids, calls for expressions of interest, general notices and prequalifications), "awarded" (contract awards) or "all". Notices past their submission deadline are left out unless `includeExpired` is true. `countries` is a list of country names, e.g. India, Kenya, Viet Nam (empty = all). `searchTerm` is a keyword for the World Bank's own search: it also finds notices written in other languages and is not a literal match; double quotes around several words narrow the result. `datePreset` is last_24_hours, last_7_days, last_30_days, last_90_days, ytd, all_time or custom (with `dateFrom` / `dateTo` as YYYY-MM-DD), on the notice date. `maxResults` caps the rows (0 = no cap); rows come newest first. `noticeTypes` picks exact types, e.g. ["Invitation for Bids"] for tenders only. `includeFullText` adds the notice body.Start with: {"status":"open","countries":["India","Kenya"],"datePreset":"last_30_days","maxResults":100}Ask me what to look for, run the Actor, read the run's status message (delivered, left out, unknown country names), then show the rows as a table with description, country, type, deadline and contact e-mail.
The Actor's machine-readable page is apify.com/foxlabs/worldbank-tenders.md: it prints API examples and the MCP config and links to the OpenAPI definition (read on 2026-10-08).
📋 Overview
The World Bank's procurement notices as structured data — one Actor, no login, cookies or API key.
Why teams pick this Actor:
- ✅ The official API, read live — every run queries the World Bank procurement-notices API.
- 📇 Buyer contact on every invitation and call — contact name, organisation, e-mail and phone were filled on 1,062 of 1,062 such notices in the measured run.
- ⏳ Past-deadline notices left out — 443 of those 1,062 were past their deadline on the run day; they are left out unless you set
includeExpired. - 🔎 Invitations or awards — one switch, or exact notice types when you only want invitations for bids.
- 🧾 Measured, not promised — how often each field was filled and what a run does not give you.
- 💰 Pay only for notices delivered — a run that delivers nothing costs only the run start.
- 🤖 Agent-ready — MCP tool + agentic payments.
✨ Features
- 🚦 Notice kind —
open,awardedorall, or exact types withnoticeTypes;includeExpiredkeeps the notices past their deadline. - 🌍 Countries — one or more project countries; names are checked against the World Bank's own list.
- 🔎 Keyword — the World Bank's own search, across languages and not a literal match; double quotation marks around several words narrow the result.
- 📅 Dates — presets from the last 24 hours to all time, or a custom
YYYY-MM-DDrange, on the notice date. - 📄 Full notice text on demand —
includeFullTextadds the notice body; on award notices that is where the awarded bidder and the contract price are. - 🧹 Clean schema — 21 camelCase fields, the same on every row (
noticeTextis a 22nd, on request). - 💬 Status message — a run that finishes says what was delivered, what was left out and why.
🎬 Quick Start
curl -X POST "https://api.apify.com/v2/acts/foxlabs~worldbank-tenders/runs?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"status":"open","countries":["India","Kenya"],"datePreset":"last_30_days","maxResults":100}'
The call starts a run and returns it; the rows are in the run's default dataset. On 2026-10-08 this input returned 26 notices and the status message Delivered 26 notices, newest first. 13 notices read on the way were left out because the deadline has passed — set includeExpired to true to get them.
🚀 Getting Started (3 steps)
- Choose what to fetch — invitations and calls, awards or exact notice types; add countries, a keyword and a date range.
- Set the cap —
maxResultslimits the rows (default 500,0= no cap). A capped run holds the newest notices of everything you chose. - Run and export — read the run's status message, then get the dataset as JSON, CSV or Excel.
📥 Input
{"status":"open","countries":["India","Kenya"],"datePreset":"last_30_days","maxResults":100}
Every field is optional. An empty input ({}) asks for the open notice types of the last 30 days that are not past their deadline, capped at 500; on 2026-10-08 it returned 500 rows.
| Field | Type | Description |
|---|---|---|
status | string | open (default) — four notice types: Request for Expression of Interest, Invitation for Bids, General Procurement Notice, Invitation for Prequalification. awarded — Contract Award, Goods and Works Award, Small Contracts Award. all — no type filter. |
includeExpired | boolean | false (default): a notice whose submissionDeadlineDate is before the run day (UTC) is left out and not charged. true: every notice of the chosen types. |
countries | array | Project countries: India, Kenya, Viet Nam, Turkiye, Egypt, Arab Republic of, Congo, Democratic Republic of … Empty = all countries. Names are checked against the World Bank's own list (see below). |
searchTerm | string | A keyword for the World Bank's own search (solar, road construction). The match is the World Bank's, not a literal one, and reaches notices in other languages; double quotation marks around several words narrow the result — see "How is a keyword matched?". |
datePreset | string | Window on the notice date: last_24_hours, last_7_days, last_30_days (default), last_90_days, ytd, all_time or custom. A preset starts at 00:00 UTC of its first day, so last_24_hours covers yesterday and today. |
dateFrom / dateTo | string | YYYY-MM-DD, both days included; used when datePreset is custom. Empty dateTo = no end, empty dateFrom = no start. The run stops when a date does not exist (2026-02-30), when both are empty or when dateFrom is after dateTo. |
maxResults | integer | Most rows in one run. Default 500; 0 = no cap. |
includeFullText | boolean | Add the full notice body as noticeText (HTML). Off by default. |
noticeTypes | array | Exact notice types instead of status — the seven names listed under status. |
Only all_time reaches the notices that carry no date — 305 of the open types on 2026-10-08. The World Bank's API refuses an offset above 100,000; past that point the Actor continues by date. A local run of this source against the live API (includeExpired on, no cap) returned all 110,776 open-type notices, each once, the undated ones last. On the platform, where all_time ran with a cap of 50 only, that many rows cost about $277.
Country names. Case, spacing and accents are ignored (vietnam → Viet Nam + Vietnam), Egypt is read as Egypt, Arab Republic of, and a country the World Bank files under more than one name is read under all of them, whichever you type (Turkey and TÜRKİYE → Turkiye + Turkey + Türkiye; Viet Nam → Viet Nam + Vietnam). A name the World Bank does not use (not in its list, no notice under it when asked directly) is named in the status message: Delivered 35 notices, newest first. Not a country name the World Bank uses: "Atlantis". If none of the names exists, the run reads nothing. By the code, when the World Bank's list itself cannot be read the names are used as typed, unchecked, and the status message says so.
Notices past their deadline are left out
A notice whose submissionDeadlineDate is before the run day (UTC) is left out and not charged — with every status and with noticeTypes. This is new with the build at the top of the Changelog; the build before delivered such notices.
Measured on 2026-10-08 over the last 30 days: 1,062 open-type notices, 443 of them past their deadline. {"maxResults":0} delivered the other 619 — 572 with the deadline ahead, 28 with it on the run day, 19 general notices without one. {"includeExpired":true,"maxResults":0} delivered all 1,062.
- Days, not hours.
submissionDeadlineTimenames no time zone, so a notice due on the run day is delivered whatever the hour: in the India + Kenya example 4 of the 26 rows were due at 15:00 that day and the run started at 20:27 UTC. - No deadline, no check. General procurement notices and awards always stay.
- The status message counts what the run read before it stopped: the capped default run reported 184 of the 443 left out.
One list, newest first
All chosen notice types and countries are read as one list, sorted by notice date, newest first, then by notice ID. maxResults cuts that list and counts delivered rows only. Measured on 2026-10-08:
- Default run (cap 500): 260 expressions of interest, 227 invitations for bids, 12 general notices, 1 prequalification, dated 18-Sep to 07-Oct. The build before returned 500 expressions of interest and no invitation for bids.
{"status":"all","maxResults":500}: 352 contract awards and 148 invitations and calls, dated 05-Oct to 07-Oct — chooseopenornoticeTypeswhen you do not want awards.- Several countries: the India + Kenya example returned 13 rows for each, mixed by date. A list of 45 names (40 to a request, merged by date), with
includeExpired, returned 702 rows — for every country the count the World Bank's API gave.
📤 Output
One row per notice, saved to the dataset.
{"id": "OP00473485","noticeType": "Invitation for Bids","noticeDate": "07-Oct-2026","submissionDeadlineDate": "2026-11-20","submissionDeadlineTime": "10:15","country": "Angola","projectId": "P505158","projectName": "Angola Youth Employment Opportunities Project","bidReference": "012./C-012/COMP.3/BENS/INEFOP-2026","bidDescription": "Aquisição de Viaturas para a Equipa de Implementação do Projecto AYEOP / JOVEM+","procurementCategory": "GO","procurementMethod": "Request for Bids","contactName": "Aurea Cassoma","contactOrganization": "National Institute of Employment and Professional Training","contactEmail": "aurea.cassoma@inefop.gov.ao","contactPhone": "+244934505576","contactAddress": "Av. do 1º Congresso do MPLA, \nLuanda, Angola","contactCountry": "Angola","detailUrl": "https://search.worldbank.org/api/v2/procnotices?format=json&id=OP00473485","source": "World Bank Procurement Notices (CC-BY 4.0)","scrapedAt": "2026-10-08T20:27:16.110Z"}
"Filled" comes from two uncapped runs over the last 30 days, on 2026-10-08: every open-type notice, past-deadline ones included (1,062 rows), and every contract award (3,038 rows).
| Field | What it is | Invitations and calls (1,062) | Awards (3,038) |
|---|---|---|---|
id | The World Bank's notice ID — unique per notice | 1,062 | 3,038 |
noticeType | The notice type | 1,062 | 3,038 |
noticeDate | Notice date as the World Bank writes it: 07-Oct-2026 | 1,062 | 3,038 |
submissionDeadlineDate / submissionDeadlineTime | Deadline, YYYY-MM-DD and HH:MM; no time zone is given | 1,043 (the 19 general notices carry none) | 0 |
country | Project country — or a region for multi-country projects (Eastern and Southern Africa, Western and Central Africa …) | 1,062 — 117 values, 12 of them regions, on 150 rows | 3,038 — 127 values, 16 of them regions, on 561 rows |
projectId / projectName | The World Bank project that finances the contract | 1,062 | 3,038 |
bidReference | The buyer's reference number | 1,043 | 3,038 |
bidDescription | What is being procured, in the buyer's language | 1,043 | 3,038 |
procurementCategory | CS consulting services, GO goods, CW civil works, NC non-consulting services | 1,043 | 3,038 |
procurementMethod | For example Request for Bids, Quality And Cost-Based Selection | 1,043 | 3,038 |
contactName / contactOrganization | The buyer's contact person and agency | 1,062 | 0 |
contactEmail / contactPhone | The contact's e-mail and phone, as the buyer published them | 1,062 | 0 |
contactAddress / contactCountry | The contact's address and country | 1,059 / 1,062 | 0 |
detailUrl | The notice's own record on the World Bank API (JSON, not a web page) | 1,062 | 3,038 |
noticeText | The full notice body in HTML — only with includeFullText (measured in three other runs) | 48 of 48 | 151 of 151, each with an awarded bidder and a signed contract price |
source | The Actor's own label for the origin of the row — one fixed string; see "Is it legal to use this data?" | 1,062 | 3,038 |
scrapedAt | When the row was read (UTC) | 1,062 | 3,038 |
A general procurement notice row holds the project and the contact only: no deadline, reference, description, category or method (19 of 19).
"Filled" is not always "usable": the values are what the buyer typed. In the 1,062 rows, 467 carry an e-mail address on a free webmail domain (226 different addresses) and 14 phone fields hold a placeholder such as 0 or 0000. Long values are cut: no bidDescription is longer than 255 characters (10 end exactly there), no projectName, contactAddress or contactOrganization longer than 100 (35, 40 and 2 end exactly there), no contactName longer than 40 (8 rows, all the same contact).
💼 Use cases
1. Invitations for bids — the tenders themselves, without the consultancy calls.
Input: {"noticeTypes":["Invitation for Bids"],"datePreset":"last_7_days","maxResults":100}. Output: 100 rows on 2026-10-08, each with the buyer's e-mail and a deadline after the run day (16 past-deadline bids left out). Use: for a daily feed see "Automation" under Integration — a notice shows up about a day after its date, so the hour of the run matters.
2. Country pipeline — the invitations and calls of the markets you work in.
Input: {"status":"open","countries":["India","Kenya"],"datePreset":"last_30_days","maxResults":100}. Output: 26 rows on 2026-10-08, all with contacts (13 more left out as past deadline). A country name does not find notices filed under a region: in the same 30 days 14 invitations and calls from buyers in Kenya sat under Eastern and Southern Africa. A region's name works in countries like a country's: in the 45-name run Eastern and Southern Africa returned 55 notices.
3. Award intelligence — who won, and at what price.
Input: {"status":"awarded","countries":["India"],"datePreset":"last_90_days","maxResults":50,"includeFullText":true}. Output: 50 award notices with the full text on 2026-10-08, each naming an awarded bidder and a signed contract price. The 90 days are notice dates, not award dates: 49 texts give the day the award was notified, and 20 of those days lie before the window (the oldest: 2023-02-03).
4. Keyword watch — what the World Bank's search returns for your field.
Input: {"status":"all","searchTerm":"solar","datePreset":"last_90_days","maxResults":100,"includeFullText":true}. Output: 100 rows on 2026-10-08 — 61 awards, 23 invitations for bids, 16 expressions of interest. The match is the World Bank's, not the Actor's: solar stood in a delivered column on 34 rows, only in noticeText on 23 more and nowhere in the row on 43.
🔗 Integration
JavaScript / Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('foxlabs/worldbank-tenders').call({"status":"open","countries":["India","Kenya"],"datePreset":"last_30_days","maxResults":100});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items[0]);
Python
from apify_client import ApifyClientclient = ApifyClient('YOUR_TOKEN')run = client.actor('foxlabs/worldbank-tenders').call(run_input={"status":"open","countries":["India","Kenya"],"datePreset":"last_30_days","maxResults":100})for item in client.dataset(run.default_dataset_id).iterate_items():print(item)
Written for apify-client 3 for Python, where call() returns a run object; with version 1 or 2 it returns a dict — write run['defaultDatasetId'] there.
Automation (n8n / Zapier / Make): schedule a daily run → read the status message and the JSON dataset → keep the ids you have not seen → push them to a sheet, CRM or chat. Mind the hour: a notice shows up about a day after its date. On 2026-10-08 at 20:27 UTC the newest notice was dated 07-Oct and {"datePreset":"last_24_hours","maxResults":0} returned 35 invitations and calls (8 more left out as past deadline); at 00:03 UTC on 09-Oct the same input returned none — nothing dated 08-Oct was in the API yet, and the next day's window no longer holds that day. So run last_24_hours late in the UTC day, or use last_7_days and drop the ids you already have (a notice delivered again is charged again).
📊 Pricing
Pay-per-event. Read from the Store on 2026-10-08: $0.0025 per notice written to the dataset ($2.50 per 1,000) plus Apify's run-start event of $0.00005. The default run (500 rows) is therefore $1.25005; a notice that is left out is not charged. maxResults: 0 has no ceiling of its own (the 3,038 awards of 30 days: $7.60) — set a maximum charge and the run stops there: limited to $0.06, a run delivered 23 notices and said so (the run start is paid from the limit first, so not 24); a new run starts from the newest notice again. A run that delivers nothing costs only the run start — one start event and no notice in each of the five such runs measured. View current pricing.
🚧 What it does not do
- No check of the deadline hour — the check is by day.
- Award rows carry no contact and no winner column. The awarded bidder and the price are in
noticeText. In 3 of the 151 award texts read (1 of them in the India example) the text belonged to a different contract than the row — that is how the World Bank's record has it. - Award rows can look identical. Of the 3,038 award rows, 952 equal another row in every column but
id,detailUrlandscrapedAt(221 groups, the largest of 49 rows). Where the full text was read they were separate awards: in the 11 such groups of the two keyword runs (34 rows) every text named a different lot, bidder or price. WithoutincludeFullTextthey cannot be told apart; each is charged. - The date window is the notice date, not the award date — see use case 3.
- The keyword match is not checked or shown — see use case 4 and "How is a keyword matched?".
searchTermis one search text, not a list. - A country name does not find notices filed under a region — see use case 2.
noticeDateis not ISO (07-Oct-2026), anddetailUrlis an API record, not a page for people.- Nothing is remembered between runs. If the windows of two runs overlap, the second delivers the same notices again and charges for them again. Measured once: three minutes after the end of 08-Oct (UTC) no notice of that date was in the API yet, so a
last_24_hoursrun made early in the UTC day misses that day for good (see "Automation"). - The closing status message is written when the run ends on its own. A run that is aborted or cut off by its time limit has none: the Apify SDK ends an aborted run at once. Rows written until then stay in the dataset and are charged. (By the code; no measured run was stopped.)
❓ FAQ
Do I need an account, login or API key for the World Bank? No. The Actor reads the World Bank's public API.
How current is the data? Every run queries the API live. In the runs of 2026-10-08 (20:27 UTC) the newest notice was dated the day before.
How many notices are there? In the 30 days before 2026-10-08: 1,062 invitations and calls (590 expressions of interest, 449 invitations for bids, 19 general notices, 4 prequalifications) and 3,038 contract awards — all of the type Contract Award; the two other award types returned nothing.
How fast is it? The 1,062-notice run took 3.5 seconds at 256 MB; the local read of all 110,776 open-type notices took 261 seconds.
How do I see what a run did? Read its status message: notices delivered, notices left out as past deadline, whether the cap (stopped at maxResults 500; the list was not read further) or the maximum charge stopped the run, and country names that were not recognised.
How is a keyword matched? By the World Bank's search; the Actor passes the text on and does not check the rows. Measured on 2026-10-08 over the last 90 days with {"status":"all","searchTerm":"road construction","datePreset":"last_90_days","includeExpired":true,"includeFullText":true,"maxResults":0}: 755 notices — both words stood somewhere in the row (notice text included) on 492, next to each other on 18, and neither word on 31. road AND construction counted 755 on the API as well. The same words in double quotation marks returned 128 of those 755: 17 of the 18 with the words together, 63 with both words, 20 with neither — 18 of those 20 are road works named in French, Spanish or Portuguese or with another word (infrastructures routières, carreteras, highway). So the search reaches across languages, and the quotation marks narrow the result without making every row a phrase match. The date window applies to a keyword search too.
Can I export to CSV / Excel / JSON? Yes — directly from the Apify dataset.
🐛 Troubleshooting
- Fewer rows than an earlier version returned — notices past their deadline are now left out (see the Changelog); the status message gives the count,
"includeExpired": truereturns them. - The run succeeded but the dataset is empty — the status message gives one of four reasons: no country name was recognised (
No notice read. Not a country name the World Bank uses: "Atlantis". Country names look like "India", "Viet Nam", "Turkiye", "Egypt, Arab Republic of".), nothing matched (it lists the filters applied), every match was past its deadline (it says how many), or the run's maximum charge covers the run start but not one notice (No notice delivered: the run's maximum charge does not cover one notice.— measured with a limit of $0.0025). - A country is missing from the rows — a name the World Bank does not use is named in the status message; a notice filed under a region is not found by a country's name.
- Contact fields or
submissionDeadlineDateare empty — award rows carry neither; general procurement notices carry no deadline. - The run failed — the status message carries the reason, for example
dateFrom 2026-10-05 is after dateTo 2026-10-01.Not seen in a measured run: the World Bank API not answering after three tries of up to 90 seconds each, or answering without the notices asked for; rows written before such a failure stay in the dataset and are charged.
⚖️ Is it legal to use this data?
The Actor reads procurement notices that the World Bank publishes through its public API. The source column is the Actor's own label, the same string on every row. The licence it names (CC-BY 4.0) is not a statement by the World Bank and was not confirmed for this dataset (2026-10-08): read the World Bank's terms of use for datasets before you republish the data. Notices contain personal data — the buyer's contact person, e-mail and phone are published for the purpose of the procurement. Personal data is protected by the GDPR and similar laws, so only process it with a legitimate basis. foXLabs is not affiliated with the World Bank.
🤝 Support & contact
- 🌐 Website: data.foxlabs.com.tr
- 📧 Email: info@foxlabs.com.tr
- 🐛 Issues: open a ticket in the Actor’s Issues tab
- 🧰 More clean B2B data actors: Fox Labs on Apify
Changelog
0.1.16 — 2026-10-09 — one newest-first list, closed notices left out, country names checked, lists over 100,000 notices read to the end; README rewritten
- A capped run now holds the newest notices of every type and country. The Actor read one country after another and, inside a country, one notice type after another, so a capped run filled up with the first type: the default run returned 500 expressions of interest and not one invitation for bids, although the same 30 days held 449 of them. All chosen types and countries are now read as one list, newest first. An uncapped run with
includeExpired: truereturns the notices the earlier build returned, in date order. Measured on 2026-10-08 with both builds started seconds apart: the same 1,062 invitations and calls, no field different; for awards this build returned the 3,038 notices the API counts for the 30 days, the earlier build 3,037 of them (see "Steadier reading"). - A notice whose deadline has passed is no longer delivered — unless you ask. New input
includeExpired(defaultfalse). Before,status: "open"meant the notice type only: of the 1,062 open-type notices of the last 30 days, 443 were already past their deadline. A notice with no deadline (general procurement notices, awards) always stays. Set"includeExpired": truefor every notice of the chosen types, as before. - Country names are checked against the World Bank's own list. A name that differs only in case, spacing, accents or punctuation is corrected (
vietnam→Viet Nam, together with the older spellingVietnam),Egyptis read asEgypt, Arab Republic of, and a country the World Bank's list holds under more than one name is read under all of them, whichever is typed (Turkey,TurkiyeandTürkiye;Viet NamandVietnam; the three Somalia names). A name the World Bank does not use is named in the run's status message; it used to match nothing without a word. When none of the given countries exists the run reads nothing. - A list of more than 101,000 notices is read to its end. The World Bank's API refuses to page past its 101,000th result (HTTP 400, measured 2026-10-08), and the four open types together are 110,776 notices: read as one list, an
all_timerun with no cap would have stopped there with an error. Past that point the Actor continues by date: read from the live API on 2026-10-08 (a local run of this source), all 110,776 came back, each once, newest first, the 305 undated ones last. By its code, the earlier build stops at the same limit on a single notice type longer than that (Contract Awardalone is 311,268 notices). - Notices with no date come only with
all_time. 305 notices of the open types carry no date at all (drafts, measured 2026-10-08). A dated run does not return them; before, a keyword run and acustomrange with onlydateTodid. - A run ends with a status message — how many notices were delivered, how many were left out as closed, why it stopped — and a run that delivers nothing says why. The run stops at its maximum charge and says so.
- Custom dates are validated.
dateFromanddateTocount only withdatePresetcustom; filled in next to another date range they are still not used, and the status message now says so.2026-02-30used to be read as 2 March and an impossibledateTowas ignored, so a run could deliver — and charge for — notices outside the range that was meant. Both now stop the run with a message that names the field. So docustomwith neither date (it used to mean every date) and adateFromafter thedateTo. - Steadier reading. Pages are requested in a fixed order (date, then notice id) — without one a notice can fall between two pages: of two runs with the same input started six seconds apart, the earlier build returned 3,037 of the 3,038 awards, this build all of them. The date window is also applied by the World Bank's API. A request that gets no answer, a server error, a rate-limit answer or an answer without the notices asked for is tried three times, with a 90-second limit each, before the run fails; by its code, a request the API refuses fails the run at once and says so. The one exception is the request for the World Bank's country list: when it fails, the run goes on with the country names as typed and says so. A country name is never reported as unknown because a request failed. A list now ends when the number of notices the API counts for it has been read — a page that came back short or empty used to end the list, and the run, without a word; if a page stays empty before that count is reached, the status message says how many notices are missing. These paths were exercised against a simulated API only: no measured run met them.
- The README described another Actor. Its examples used input keys this Actor does not have (
queries,maxResultsPerQuery, with a literalundefinedvalue), its input table listed phantom fields (maxConcurrency,includeRaw) and its text described a company registry ("relevance-ranked name search or exact registry-ID lookup", "legal form, formation date"). Every example now uses the real input and was run on the platform; the output table says how often each field was filled, by notice kind. - The Python example works with the current Apify client. It read the results with
run['defaultDatasetId']; withapify-client3 for Pythoncall()returns a run object and that line raisedTypeError: 'Run' object is not subscriptableafter the run had finished. It now readsrun.default_dataset_id; with client version 1 or 2, keeprun['defaultDatasetId']. - No output field or pricing change. Behaviour changes for inputs that already exist: closed notices are left out by default; a capped run over several types or countries returns the newest notices of all of them instead of the first type's; notices with no date come only with
all_time; acustomdate that does not exist,customwith neither date and adateFromafter thedateTostop the run; a country name written differently from the World Bank's is corrected instead of matching nothing; a country the World Bank files under two or three names (Viet Nam,Turkiye, Somalia) returns the notices of every one of those names — on a long date range more rows than before for the same input.
0.1 — 2026-09-07
- The README was regenerated from a template shared by many Actors; the entry of that day read "Enabled AI-agent payments (x402) + rebuilt the README to the full standard". The label is the generator's: the builds of that day were numbered like the others of this Actor. That README is the one the entry above corrects — its examples and parts of its text did not describe this Actor.
0.2 — 2026-07-05
- Reworked docs: API quick-start, full field table, real sample output, integration snippets (JS / Python / Make / MCP), FAQ and troubleshooting; clarified the difference between invitations and awards (contacts and deadline on invitations; winner and price in the notice text on awards).
0.0
- Initial release (the Actor's first changelog called it 0.1): procurement notices from the World Bank's procurement-notices API — invitations, calls for expressions of interest, prequalifications and contract awards; country, notice-type, keyword and date filters; optional full notice text.