Tender & Procurement Intelligence API

Public tenders as clean JSON β€” normalised across TED, SAM.gov and OCDS portals, deduplicated across sources, with buyers and winners resolved on registration identifiers, delivered by search and signed webhook.

TED Β· SAM.gov Β· OCDS Entity resolution CPV ↔ NAICS crosswalk Free & public

Not an official procurement system. Not legal advice. Bids are submitted on the buyer's own portal, and the authoritative text of a notice is the one at the source link carried on every result. Coverage is best effort per source, and deduplication, entity resolution and the CPV↔NAICS crosswalk are confidence-scored inferences, not ground truth.

What this is

Public procurement is enormous, entirely public, and structurally horrible to consume. TED publishes thousands of eForms notices a day; SAM.gov publishes US federal opportunities as a 200 MB daily CSV; every member state runs its own portal with its own schema and cadence. Bid teams pay legacy vendors four figures a month to not deal with that β€” and get a daily email.

The product is not the crawl. Every source here is free and public. The product is the work nobody wants to do: normalisation, cross-source deduplication, entity resolution, award linkage, code mapping and reliable delivery.

Loading the source catalogue…

Straight from sources.json β€” the same document the API serves, including what each source does not cover.

Live demo β€” expand a code, then search

Runs the real /v1/codes/expand and /v1/notices endpoints. The expansion is computed by the same pure function the server uses, bundled into this page, so what you see here is exactly what your query will do.

Entity resolution β€” identifiers, not names

One recorded TED award notice contains two different Czech companies both trading as β€œAlliance Healthcare s.r.o.” under registration numbers 25099019 and 14707420. A resolver that matches on names merges them, and then reports that one company won contracts it never won.

So the rules are ordered to make that impossible:

1. Identifier matchthe same national registration number is the same organisation β€” merge, confidently.
2. Identifier conflictdifferent numbers of the same scheme are never merged, however similar the names.
3. Name and contextwith no identifier on either side, this records a merge candidate β€” never an automatic merge.
4. Otherwisea new organisation.

Every organisation reports its identity strength, because the sources are not equal: TED and most OCDS publishers supply registration numbers, while SAM.gov's opportunities extract gives an unparsed awardee name and address with no UEI at all. US buyers still resolve strongly on agency codes. You can filter on strength rather than discovering the asymmetry by being wrong about a merge.

Call the API

Base URL: https://apis.allanninal.dev/tenders

Search, deduplicated by default

curl -s "https://apis.allanninal.dev/tenders/v1/notices?cpv=72000000&country=CZE&limit=5"

Filter by free text, CPV or NAICS (with descendants and the crosswalk), country, NUTS region, buyer, source, notice type, procedure, value band, currency, publication and deadline windows. Add expand=notices for every source notice instead of one row per tender.

Expand a classification code

curl -s "https://apis.allanninal.dev/tenders/v1/codes/expand?code=45000000"

Watch a search and receive signed webhooks

# 1. Save a search
curl -s -X POST https://apis.allanninal.dev/tenders/v1/saved-searches \
  -H "authorization: Bearer $TENDERS_KEY" -H "content-type: application/json" \
  -d '{"name":"Czech IT","filter":{"cpv":"72000000","country":"CZE"}}'

# 2. Subscribe a webhook to it (the endpoint must echo the verification challenge)
curl -s -X POST https://apis.allanninal.dev/tenders/v1/subscriptions \
  -H "authorization: Bearer $TENDERS_KEY" -H "content-type: application/json" \
  -d '{"savedSearchId":"srch_...","url":"https://your.app/hooks/tenders"}'

Deliveries follow Standard Webhooks: webhook-id, webhook-timestamp and webhook-signature: v1,<base64 HMAC-SHA256> over id.timestamp.body. Delivery is at-least-once with exponential backoff, so handle on webhook-id idempotently. Verify with the bundled client library rather than reimplementing the comparison.

Coverage and freshness

curl -s https://apis.allanninal.dev/tenders/v1/ingest/runs

Per source: when it last succeeded, how stale it is, what it counted. A stale source says so rather than being served as current.

Access & fair use

Free. No plan, no billing, no payment code anywhere in the repository. Search, code expansion and all reference data are anonymous; saved searches, webhooks and organisation history need a free key β€” on a free service the delivery fan-out, not the query, is the scarce resource.

Anonymous callers get 60 requests/minute and 5,000/day per client IP; key holders get 5Γ— the minute budget and 10Γ— the daily one, with caps on saved searches, subscriptions and deliveries. Every response carries RateLimit-*; throttled requests return 429 with Retry-After.

Reference data (static, free, no auth)

FAQ

Which sources does it cover?

TED for the EU and EEA, the SAM.gov daily extract for US federal, and OCDS publishers starting with UK Find a Tender. More than fifty governments publish OCDS, so further portals are a configuration row plus a licence check β€” not a new scraper.

Why not use the SAM.gov API?

Because a non-federal public key is capped at roughly 10 requests per day, which cannot sustain a feed. The daily bulk extract needs no key and is the route GSA supports for data reusers.

Is the deduplication trustworthy?

It is transparent, which is better. Explicit publisher links are treated as facts; anything inferred needs corroboration from several independent signals and carries a confidence. Every source notice is kept, so a wrong grouping is visible and reversible rather than destructive.

Is there an official CPV ↔ NAICS mapping?

No. Ours is derived, versioned, confidence-scored, covers all 45 CPV divisions, and is published free β€” as a search aid, never as a legal classification.

How much does it cost?

Nothing. Code is MIT; tender data stays under its publisher's licence, and every response carries the attribution so you can comply without guessing.

Not technical? Here's the whole idea in simple words

Imagine a restaurant 🍽️. You don't march into the kitchen and start cooking. You tell the waiter what you'd like, and a few minutes later your dish arrives. An API is that waiter β€” but for software. One app asks for something, the API goes and gets it, and hands back a tidy answer. Nobody has to touch the messy kitchen.

1 What an API actually is

API stands for "Application Programming Interface," but you can forget those words. The useful idea is this: it's a polite, reliable messenger that sits between two systems so they can work together without knowing each other's secrets.

πŸ—ΊοΈ

Maps in an app

A ride app asks a maps API "where is this address?"

πŸ’³

Card payments

A shop asks a payment API "is this card good?"

🌦️

Weather widget

A site asks a weather API "what's it like today?"

πŸ”

"Log in with…"

One app asks another "is this really them?"

2 Why it matters for your business

Most businesses lose time and money in the gaps between tools β€” someone exports a spreadsheet here, retypes numbers there, emails a file to a partner, and hopes nobody made a typo. An API closes those gaps.

⏱️ Save time

Work that took hours by hand happens in a blink, on its own.

βœ… Fewer mistakes

No more typos from copying numbers between apps.

πŸ“ˆ Grow easily

Ten customers or ten thousand β€” it keeps up without extra staff.

πŸ’Ά New income

You can even sell access β€” partners pay to use your API.

πŸŒ™ Always on

It answers day and night, weekends and holidays included.

🀝 Better partnerships

Give partners a clean, safe door instead of raw access.

3 The parts that make one

πŸšͺ

A door

A clear, documented place to ask for things.

πŸ›‘οΈ

A guard

Keys and limits so only allowed callers get in.

🧠

A brain

The rules and logic that do the real work.

πŸ—„οΈ

A memory

Where your data safely lives and is looked up.

4 The process, step by step

5 What you actually get

βœ… A live, working API πŸ“– Plain docs anyone can follow πŸ” Keys & safe access control 🚦 Limits so it can't be abused πŸ§ͺ Tests that prove it works 🌐 A demo page to show it off πŸ“¦ Everything owned by you

6 Built to a secure, professional standard

A public API is part of your brand β€” it's a front door the whole internet can knock on. So it's built the way a bank builds a vault, not the way a hobby project throws something online. Every API in this family is shipped to the same standard:

πŸ”’ Encrypted by default

HTTPS everywhere with automatic certificates and HSTS β€” data is never sent in the clear.

🚦 Abuse-resistant

Per-caller rate limits at the gateway stop floods and keep it fair and available for everyone.

🧰 Standards-based

A documented OpenAPI contract, predictable JSON, and clear status codes β€” the conventions every developer already expects.

πŸ›‘οΈ Hardened surface

Locked-down servers, no version banners to probe, input validated, and no secrets in the code.

🧾 Provable & auditable

Versioned data and reproducible results, so an answer can be traced back to its source and defended.

βœ… Tested & monitored

Automated tests prove behaviour before release, and health checks keep it honest in production.

Why that's good for your brand online

A clean, reliable, well-documented API is a quiet but powerful marketing asset. It tells partners and customers you're serious β€” and it earns trust that spreads on its own:

🀝 Partners integrate faster and trust you more πŸ”Ž Public docs get found and shared ⭐ "It just works" becomes your reputation 🧩 Developers recommend tools they enjoy using 🌍 A branded endpoint (your domain) reinforces who built it

πŸ“‘ A real example: this tender API

This isn't theory β€” the page you're on is served by a real API I built. It tackles a concrete problem: public tenders are published by every government, and none of them agree on a format. A bid team that wants to know "what opened this week in my sector, anywhere" has to read several portals in several languages, and spot that the same contract appears twice.

The question your app asks:

GET /v1/notices?cpv=72000000&country=CZE

The clean answer it hands back: one row per real procurement β€” not one per portal β€” with the buyer identified by its registration number, the codes translated between European and American vocabularies, and a link back to the official notice. Then a webhook tells you the moment the next one appears.

Reading portals by hand
Hours a week 😩
With the API
One request ⚑
Same contract, two portals
Merged for you πŸ”—

The same building blocks β€” a door, a guard, a brain, a memory β€” pointed at one real problem. The same kit can become an API for your idea.

πŸ‘‹ Want an API for your idea?

This API was designed and built by Allan NiΓ±al, an AI & software engineer who ships friendly, well-made, developer-first APIs and data products. Tell me the problem in plain words β€” procurement, compliance, data plumbing, or anything else β€” and I'll turn it into something that works.