Skip to main content

Engineering

The Tender Index, Open to Agents

Davor Jerković, Founder·September 23, 2026·Updated 23 September 2026·9 min read

Lucius has always had two halves. One is an index of public procurement notices, pulled from the official portals of the United States, the European Union, the United Kingdom, Australia, Canada, Ireland, New Zealand, Singapore, India, the UN agencies and the World Bank. The other is a verification engine that reads a tender pack and returns every requirement with the page it came from. Until today both lived behind our own screens. From today the index is open to agents.

If you run an agent, a copilot or a workflow that has to answer "is there a tender for this, and when does it close", you can now ask Lucius directly. Three tools, one key, two transports: plain REST, and an MCP server so Claude Code, Claude Desktop, Cursor and any other MCP client can use the index as a tool with one line of configuration. This post says what is in the index, what an agent gets back, how a search runs, and the rules.

What is in the index

Horizontal bar chart of the 235,415 notices in the Lucius index by source portal on 23 September 2026. SAM.gov 103,209; TED 88,070; AusTender 20,773; Find a Tender 5,946; CanadaBuys 4,254; Grants.gov 2,910; eTenders 2,378; UNGM 1,824; GETS 1,660; Contracts Finder 1,560; CPPP 1,076; GeBIZ 826; World Bank 748; Public Contracts Scotland 180.
The catalog on 23 September 2026, by source. SAM.gov and TED are 81 percent of the rows. The other twelve portals are where an agent saves the most time, because each has its own search, its own format and its own idea of a deadline.

On 23 September 2026 the catalog held 235,415 notices from 15 feeds on 14 official portals (AusTender arrives as two feeds), tagged with 181 country and region codes. 42,572 of them were still open by deadline. 13,374 were added in the previous seven days, which is the pace the portals publish at. 235,278 rows carry a 3,072-dimension embedding, so the whole catalog is searchable by meaning as well as by words. The Grants.gov rows are funding opportunities rather than contracts; they stay in because the agents we talk to ask for both.

Each row is what a bid manager needs before deciding whether to open the pack: title, buyer, country, deadline, value where the buyer published one, CPV codes, the source portal and a link to the notice there. The full notice text is not part of the index we serve. The portals publish it, and the link takes you to it. More on that under the rules.

Three tools

search_tenders. Send a query, optional countries, a source, a CPV prefix, a closing window, a limit up to 25 and a cursor. Get ranked notices with their metadata, a snippet of up to 700 characters and a relevance score, plus a cursor for the next page. Spends one embedding call.

get_tender. Send a notice id. Get the notice's metadata, and if your own workspace has analysed that notice, its summary and bid verdict too. Spends no model time.

similar_tenders. Send a notice id, optional countries and a limit up to 25. Get the nearest notices by stored embedding. Spends no model time.

The same three tools sit behind both transports. Over REST they are three GET endpoints. Over MCP they are three tools with the same names and the same arguments, so a prompt that says "find open solar pumping tenders in East Africa closing in the next sixty days" turns into one call, and the agent reads the rows back as a tool result.

One request, end to end

Diagram of one request to the Lucius Agent API. An agent sends a call over MCP or REST with a bearer key. The gate checks the key, the per-minute limit and the daily result cap. One of three tools runs against the index on AlloyDB, which holds 235,415 notices with full-text and ScaNN vector indexes. The response carries metadata plus a snippet; the full notice text stays on the source portal, which the link points to.
The gate sits in front of the tools. A key is hashed at rest, carries a scope, a per-minute limit and a daily result cap, and the cap fails closed: when the counter cannot be read, the call is refused rather than served.

A key looks like lk_live_ followed by 32 characters and travels in the Authorization header. Each key belongs to a company workspace, so what an agent can see is exactly what a person in that workspace can see: the public catalog, plus that workspace's own analysed copies. The defaults are 60 requests a minute and 2,000 results a day, both adjustable per partner without a deploy. Usage is one row per key per day in the database, and the cap check and the count are one statement, so two calls racing each other cannot both slip through.

How a search runs

Diagram of how search_tenders runs. The query is embedded once and sent down two legs in parallel, a full-text leg over the tsvector index and a vector leg over the ScaNN index. Rank fusion merges the two lists. Catalog scope and the country, source, CPV and deadline filters run on the merged list, and rows are shaped into metadata plus a snippet. Measured on production on 22 September 2026: the vector leg averaged 141 milliseconds on the exact path and 180 milliseconds on the ScaNN path; one end-to-end REST call on 23 September took 0.7 seconds.
Two legs, one list. The vector leg is the one the Google Cloud write-up describes; the market routing that decides between an exact scan and the ScaNN index was re-cut on measured cost the day before this launch.

Search is hybrid. The query is embedded once. A full-text leg runs over a stored tsvector of the title and text and ranks by term match. A vector leg runs over the ScaNN index by cosine distance, and for a market with fewer than 2,000 rows it takes an exact scan instead, because at that size exact is cheaper than approximate. Rank fusion merges the two lists into one, the catalog scope and the filters run on the merged list, and each row is cut to a snippet. On production on 22 September the vector leg averaged 141 milliseconds on the exact path and 180 milliseconds on the ScaNN path, measured from the database's own statement statistics. One end-to-end REST search over the public internet on 23 September, five results, took 0.7 seconds. That last number is a single call, not a benchmark; the two means are thousands of calls.

Quick start

MCP, from Claude Code, one line:

claude mcp add --transport http lucius-tenders https://api.ailucius.com/api/v1/mcp \
  --header "Authorization: Bearer lk_live_..."

Any MCP client that takes a JSON config:

{
  "mcpServers": {
    "lucius-tenders": {
      "type": "http",
      "url": "https://api.ailucius.com/api/v1/mcp",
      "headers": { "Authorization": "Bearer lk_live_..." }
    }
  }
}

REST, the same three tools:

curl -H "Authorization: Bearer lk_live_..." \
  "https://api.ailucius.com/api/v1/ext/tenders/search?query=solar+water+pumping&countries=NE,KE,UG,ET&closing_within_days=60&limit=5"

curl -H "Authorization: Bearer lk_live_..." \
  "https://api.ailucius.com/api/v1/ext/tenders/{id}"

curl -H "Authorization: Bearer lk_live_..." \
  "https://api.ailucius.com/api/v1/ext/tenders/{id}/similar?countries=DE&limit=5"

What a search returns, shortened to one row:

{
  "results": [
    {
      "id": "…",
      "title": "Procurement of Solar Powered Water Pump under UNJP/ETH/140/UNJ project",
      "buyer": "FAO",
      "country": "ET",
      "deadline_at": "2026-09-30T…",
      "value": null,
      "cpv_codes": [],
      "source": "UNGM",
      "source_url": "https://www.ungm.org/Public/Notice/…",
      "snippet": "…",
      "relevance": 0.83
    }
  ],
  "mode": "hybrid",
  "next_cursor": "NToyOTc3N2FlMThh"
}

The full reference, with every argument and limit, is at ailucius.com/developers.

The rules

  • Keys are issued by hand. There is no self-serve key page yet. Write to davor@ailucius.com with the company name and what you are building, and a key comes back with the limits set for that use.
  • Read only. This release exposes the index. Nothing an agent does through it changes a row.
  • Metadata, a snippet and a link. The response never carries the full notice text. Every row links to the notice on the portal that published it, and the portals' own terms apply to what you do with it from there.
  • Limits are real. 60 requests a minute and 2,000 results a day by default. A key that hits its cap gets a clear refusal, not a degraded answer.
  • Your workspace, your data. A key sees the public catalog and its own workspace's analysed copies. It never sees another workspace.

Why open it

Two reasons. The first is that every agent builder we talk to who touches procurement data has written the same thing: a scraper for one portal, then another, then a cron job that breaks when the portal changes its HTML. The index is the part of Lucius that gets more useful the more people query it, and it is already the part that costs the least to serve. The second is that most of the funded tools in this market are single-country or drafting-first, and an agent that can qualify tenders across fourteen portals is a better front door for the verification engine than a person who has to paste a PDF.

The engineering behind the index, AlloyDB with a ScaNN index and an MCP agent on the operations side, is written up on the Google Cloud blog under my byline. The companion piece on this blog, Let the Vector Index Drive, is the long version of the one query that made the vector leg fast enough to serve to strangers.

What is next

The verification engine is the second release: hand the API a pack, get back the page-cited requirement matrix and a bid or no-bid verdict. It goes to design partners first, because every call spends real model time and the quotas need to be prepaid and hard before it is public. If you are building on procurement data and want a key, or you want to be one of the partners who tries the verification tools before anyone else, write to davor@ailucius.com and say what you are building.

Questions

Does the API return the full text of a notice?

No. Each row carries title, buyer, country, deadline, value, CPV codes, the source portal, a link to the notice on that portal, and a snippet of up to 700 characters. The full text and the documents live on the portal, and the link takes the agent there.

What does a call cost?

Nothing to start. Keys are issued by hand to design partners, read only, with a per-minute and a per-day limit. A search spends one embedding call on our side; fetching a notice or finding similar ones spends no model time at all.

Can an agent analyse a tender pack through the API?

Not in this release. The page-cited requirement matrix and the bid verdict are the second release and go to design partners first. Write to davor@ailucius.com if you want to be one of them.