Tender discovery platform
Continuous ingestion from dozens of public procurement sources, each with its own format, normalized into one dataset that can actually be searched and ranked.
- Procurement
- Dozens of public sources
- 2024 · ingesting daily
- 2024
The tenders existed; finding them was the work
Public procurement notices are published, but each source publishes them its own way — its own fields, its own idea of a deadline, its own notion of what a category is. Finding the ones worth bidding on meant a person opening dozens of sites, reading past most of them, and hoping nothing closed while they were looking somewhere else.
The bottleneck was never access to the data. It was that no two sources described the same thing the same way, so nothing could be compared, filtered or ranked across them.
What we could not change
- Every source formats differently. Not a translation problem but a modelling one: the same field means different things in different places, and some sources omit entirely what others treat as mandatory.
- Sources change without notice. Nobody is obliged to warn an integrator before altering a page or a feed. An ingester that assumes stability degrades silently, so ours had to detect that a source stopped making sense and say so rather than write nonsense into the dataset.
- We do not control the upstream. No API contract, no test environment, no support channel. What we could do was reduce the blast radius of any one source breaking.
- Freshness is the product. A tender found after its deadline is worthless, so ingestion had to keep running rather than run well once.
Ingestion, normalization, and one dataset to search
Continuous ingestion runs per source on a job queue, so one source failing does not stop the others and failures are visible rather than silent. Each source has its own adapter — the messy part stays at the edge — and everything past that point works on one normalized shape.
Normalization is where the value is: dates, categories, amounts and deadlines are mapped onto a single model, so the result is one dataset that can be searched, filtered and ranked instead of dozens that can only be read one at a time. The parsing and matching that carry the load run as separate services.
Ingesting daily since 2024
The platform has been ingesting daily since 2024. Searching across sources takes one query rather than an afternoon, and a source that changes shape shows up as a failure to investigate instead of quietly missing tenders.
As with our other work, we are not quoting hit rates or win rates. Those belong to the client, and a number without its method attached is not evidence.
More on this kind of system
- Declare maintenance jobs as data, not as codeA maintenance tool that can run any command is one nobody should trust with admin rights. What changes when the job is declarative data instead of code.
- Offline-first when drivers lose signal for half the dayDrivers lose signal for half the day, so the app has to hold a day of work on the device. What that costs, and the conflict nobody designs for up front.
- When a spreadsheet becomes load-bearingA spreadsheet becomes a system the moment it starts holding rules rather than numbers. The observable signs, and the honest case for leaving it alone.
Have a system shaped like this one?
Send us the shape of the problem — what it has to do, what it must not break, and what is already in place. We will tell you whether we are the right team for it.
Start a projectWe reply within 1 business day. No sales calls unless you ask for one.