OpenTradeIntel
Open-source procurement and B2B sourcing intelligence engine.
OpenTradeIntel queries real EU public procurement notices from the official TED Search API, normalizes them into typed tenders, and ranks them against a local supplier catalog. It also accepts local JSON/CSV tender data. The project is an early-stage, self-hosted toolkit for SMEs, exporters, sourcing and procurement teams, researchers, developers, and trade-intelligence projects.
Why?
Procurement requirements and catalogs often arrive in incompatible spreadsheets or data exports. OpenTradeIntel provides a small, inspectable baseline for validating that data, normalizing common terms, and explaining why one catalog product ranks above another—without sending commercial data to an external AI service.
Features
- Typed tender/RFQ and product models using Pydantic.
- JSON and CSV ingestion behind extensible connector/parser interfaces.
- Official public TED v3 Search API connector with no API key or scraper.
- TED keyword, CPV, country, page-number, and iteration-token search support.
- Direct
TED -> normalized tender -> local catalog -> ranked matchesworkflow. - Local text, unit, category, keyword, and market normalization.
- Deterministic 0–100 matching with a complete component breakdown.
- Typer CLI and FastAPI API backed by the same application service.
- Dependency-free MCP adapter and optional provider protocol.
- Synthetic examples, tests, Docker packaging, and OSS project files.
- No API key, LLM, database, queue, or network call required by core features.
TED in 30 seconds
Search active public notices through the official TED API:
uv run opentradeintel ted search \
--query "dried fruit" \
--limit 10 \
--output json
Search by CPV/place and save normalized tenders:
uv run opentradeintel ted search \
--cpv 15897200 \
--country DE \
--limit 20 \
--output-file tenders.json
Match the returned notices directly against a local catalog:
uv run opentradeintel ted match \
--query "dried fruit" \
--catalog examples/catalogs/sample.csv \
--match-limit 3
ted match returns at most 10 catalog matches per notice by default; use --match-limit to choose a different positive bound.
TED search requires outbound HTTPS access but no credential. Local file matching and the deterministic core remain fully offline.
Quick start
Requirements: Python 3.12 or newer and uv.
git clone https://github.com/b2baitrade-ui/opentradeintel.git
cd opentradeintel
uv sync --all-groups --locked
uv run opentradeintel --help
Inspect the synthetic tender:
uv run opentradeintel inspect examples/tenders/sample.json
Match it against the synthetic catalog:
uv run opentradeintel match \
--tender examples/tenders/sample.json \
--catalog examples/catalogs/sample.csv
Example excerpt (values are computed, not hard-coded):
1. Dried Mango 500g
Score: 100/100
Reasons:
[+] Product similarity: 40/40 (name 24/24, keywords 16/16)
[+] Category: 15/15 (dried fruit)
[+] Certifications: 20/20 (2/2 covered)
[+] Market compatibility: 15/15
[+] MOQ compatibility: 10/10
Supplier, tender, and benchmark examples are synthetic. examples/ted/ is a clearly attributed, trimmed snapshot of public TED procurement data.
API
Start the API:
uv run uvicorn opentradeintel.api.app:app --reload
Available routes:
GET /healthGET /versionPOST /match- Interactive OpenAPI documentation at
/docs
Minimal request:
curl -X POST http://127.0.0.1:8000/match \
-H "Content-Type: application/json" \
-d '{
"tender": {
"id": "demo-rfq", "title": "Dried mango", "buyer": "Demo Buyer",
"description": "Organic dried fruit", "products": ["mango"],
"quantity": 1000, "unit": "kg", "destination": "Germany",
"currency": "EUR", "required_certifications": ["EU Organic"],
"source": "synthetic-demo"
},
"products": [{
"sku": "DM-1", "name": "Dried mango", "description": "Organic slices",
"category": "Dried fruit", "origin": "Exampleland",
"certifications": ["EU Organic"], "min_order_quantity": 100,
"available_markets": ["EU"], "keywords": ["mango", "organic"]
}]
}'
How matching works
The score is the integer sum of five deterministic components:
| Component | Maximum | Baseline behavior |
|---|---|---|
| Product similarity | 40 | Product-name token coverage (24) plus keyword coverage (16) |
| Category | 15 | Normalized category tokens appear in the tender |
| Certifications | 20 | Proportional coverage of required certifications |
| Market | 15 | Destination and available-market aliases intersect |
| MOQ | 10 | Tender quantity meets product minimum order quantity |
Missing quantity or MOQ receives five neutral points and a warning. Results include reasons, warnings, and the component breakdown. Exact CPV overlap is a score-neutral tie-break signal; SKU is the final stable tie-breaker. See matching documentation and the 20-opportunity benchmark.
Architecture
local JSON/CSV -> connector -> parser -------\
-> typed Tender -> OpportunityService -> matcher
official TED -> search client -> TED mapper -/ -> CLI / FastAPI / MCP adapter
The boundaries are intentionally small. New source connectors acquire text; parsers interpret formats; interfaces never reimplement matching. See architecture and connector guidance.
Data privacy
OpenTradeIntel does not require procurement or supplier data to be sent to an external AI API. The default workflow is local, deterministic, and suitable for self-hosting. Users remain responsible for access control, retention, applicable procurement rules, and the accuracy of final commercial decisions.
AI providers and MCP
AI is optional. The EnrichmentProvider protocol is a future extension point; no provider is installed or called by core. .env.example contains a blank optional key only to document that future integration. The dependency-free OpenTradeIntelMCPAdapter remains an interface only; registering it with a concrete MCP SDK is deferred.
Docker
docker compose up --build
curl http://127.0.0.1:8000/health
The runtime image uses Python 3.12, runs as a non-root user, and exposes port 8000.
Developing
uv sync --all-groups --locked
uv run ruff check .
uv run ruff format --check .
uv run mypy src tests benchmarks examples
uv run pytest
uv run python benchmarks/run.py
On systems with GNU Make, make check runs the same gates. See development documentation.
Roadmap
Version 0.2 adds the official TED public connector and a measurable matcher baseline. PDF/XLSX parsing, additional reviewed public connectors, evaluated semantic methods, and a concrete MCP server remain future candidates. See the honest, non-binding roadmap.
Contributing
Issues and pull requests are welcome. Read CONTRIBUTING.md before adding a parser or connector, and follow the Code of Conduct.
Security
Do not open a public issue for a vulnerability. Follow the private reporting instructions in SECURITY.md.
License
Licensed under the Apache License 2.0.
Metadata
Release files for opentradeintel 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| opentradeintel-0.2.0.tar.gz | 136.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| opentradeintel-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 171.8 kB
Release files / opentradeintel-0.2.0.tar.gz
| Download URL | opentradeintel-0.2.0.tar.gz |
|---|---|
| Size | 136.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a1267d6be88b6f6c79f89e1d63ebadb43874f4367f98f0235713ef314b3805bc
|
|
BLAKE2b-256 checksum How to use checksums |
88b0d8ead624b8a82879e3d781bc9517a0d7544d5885ad621bc26d67860f3d0f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.
Transparency logRelease files / opentradeintel-0.2.0-py3-none-any.whl
| Download URL | opentradeintel-0.2.0-py3-none-any.whl |
|---|---|
| Size | 35.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e5a46dbb699fcd76338b35548bdb2d72d86e5cb9a1ad7c065184d0e7149588dc
|
|
BLAKE2b-256 checksum How to use checksums |
2681dda08426edc6a088fe98db2d2e75b81053b6d8059c5d73943fc88003d1d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.
Transparency log