Integration documentation v0.4
How to call the Nordic Data API. Two surfaces — REST and MCP — backed by the same data and the same key. Most agents need fewer than twenty lines of glue.
Quick start
Three minutes from zero to your first response.
1. Get an API key
Self-serve at nordicdata.cloud/#pricing. The free tier gives you 1,000 requests per month — plenty to evaluate the data.
2. Hit the API
curl 'https://api.nordicdata.cloud/tenders/search?country=ALL&q=construction&limit=3' \ -H "X-API-Key: nrd_live_..."
3. (Optional) wire MCP
If your agent runtime speaks MCP, point it at https://api.nordicdata.cloud/mcp with the same key. 28 tools become available across procurement, company registry, financials, officer networks, news, EU grants, and compliance. See MCP setup.
Authentication
Every request to /tenders/*, /me, and /mcp requires an API key in the X-API-Key header.
X-API-Key: nrd_live_w_5SLGEXXTOk9omCneKPN_4AAsR6Kh-g
Keys are scoped to the issuing account. Each call is recorded against your monthly request budget. Hit the cap and you get 429 Too Many Requests with the budget reflected in the body.
Countries
Five Nordic registers are indexed. Use the country parameter on search:
| Code | Name | Source register |
|---|---|---|
| NOR | Norway | Doffin (above-EU-threshold notices via TED) |
| SWE | Sweden | Mercell.se / TED |
| DNK | Denmark | Udbud.dk / TED |
| FIN | Finland | Hilma / TED |
| ISL | Iceland | Útboðsvefur / TED |
| ALL | Cross-Nordic | All five, sorted by publication date |
GET /countries returns the live indexed counts and the latest publication date per country. No key required — call it from a browser to inspect.
Search tenders GET/tenders/search
Returns tenders sorted by publication date, newest first. Combine any of the filters below.
| Param | Type | Description |
|---|---|---|
| country | string | NOR, SWE, DNK, FIN, ISL, or ALL. Defaults to |
| q | string | Full-text search across title and description (PostgreSQL |
| cpv | string | CPV classification code, e.g. |
| from | YYYY-MM-DD | Earliest publication date to include. |
| to | YYYY-MM-DD | Latest publication date to include. |
| limit | integer | 1–100. Default 20. |
| page | integer | 1-based pagination. Default 1. |
Example
# Recent Swedish IT-services tenders curl 'https://api.nordicdata.cloud/tenders/search?country=SWE&cpv=72000000&limit=10' \ -H "X-API-Key: $NORDIC_KEY"
Response
{
"page": 1,
"limit": 10,
"total": 412,
"count": 10,
"results": [
{
"id": "318369-2026",
"title": "Sweden – Architectural, construction, engineering and inspection services – Ramavtal teknisk konsulttjänst Akustik",
"buyer": { "name": "Örebroporten Fastigheter AB", "city": "ÖREBRO", "country": "SWE" },
"publication_date": "2026-05-08",
"deadline": "2026-05-31",
"contract_nature": ["services"],
"cpv_codes": ["71000000"],
"description": "Örebroporten avser upphandla ramavtal för teknisk konsulttjänst akustik.",
"total_value": 4000000,
"procedure_type": "open",
"notice_url": "https://ted.europa.eu/en/notice/318369-2026/eng"
}
]
}
Get one tender GET/tenders/:id
Fetch a single tender by its TED publication number. Format: {number}-{year}, e.g. 317565-2026.
curl 'https://api.nordicdata.cloud/tenders/317565-2026' \ -H "X-API-Key: $NORDIC_KEY"
Returns the same shape as a single result inside /tenders/search. If the tender is not in the local cache, the server makes a synchronous fallthrough call to TED, caches the result, and returns it. Subsequent requests are fast.
Account info GET/me
Returns your plan, monthly limit, and usage so far this calendar month. Useful for agent runtimes that want to display remaining quota.
{ "label": "founder-test", "plan": "pro", "monthly_limit": 50000, "used_this_month": 412 }
Search companies GET/companies/search
Search the Norwegian Brønnøysund business registry by name, organisation number, or NACE industry code. Norway-only in v0.5; Sweden, Denmark, Finland, Iceland coming.
| Param | Type | Description |
|---|---|---|
| q | string | Fuzzy name match. e.g. |
| orgnr | string | 9-digit Norwegian organisation number for direct lookup. Skips name search and returns the single matching company. |
| nace | string | NACE industry code, e.g. |
| limit | integer | 1–100. Default 20. |
| page | integer | 1-based pagination. Default 1. |
# Search by name curl 'https://api.nordicdata.cloud/companies/search?q=equinor&limit=3' \ -H "X-API-Key: $NORDIC_KEY" # All companies in oil & gas extraction (NACE 06) curl 'https://api.nordicdata.cloud/companies/search?nace=06&limit=20' \ -H "X-API-Key: $NORDIC_KEY" # Direct orgnr lookup (single result) curl 'https://api.nordicdata.cloud/companies/search?orgnr=923609016' \ -H "X-API-Key: $NORDIC_KEY"
Get one company GET/companies/:orgnr
Full company profile by 9-digit organisation number.
curl 'https://api.nordicdata.cloud/companies/923609016' \ -H "X-API-Key: $NORDIC_KEY"
{
"orgnr": "923609016",
"name": "EQUINOR ASA",
"legal_form": { "code": "ASA", "description": "Allmennaksjeselskap" },
"registered": "1995-03-12",
"in_business_registry": true,
"in_vat_registry": true,
"status": "active",
"nace": [
{ "code": "06.100", "description": "Utvinning av råolje" },
{ "code": "06.200", "description": "Utvinning av naturgass" }
],
"employees": 21376,
"business_address": { "street": "Forusbeen 50", "postal_code": "4035", "city": "STAVANGER", "country": "Norge" },
"website": "www.equinor.com",
"sector": { "code": "1110", "description": "Statens forretningsdrift" },
"parent_orgnr": null,
"source": "brreg",
"source_urls": { "registry": "https://w2.brreg.no/enhet/sok/detalj.jsp?orgnr=923609016" }
}
Officers and roles GET/companies/:orgnr/officers
Current and historical board members, daily leader (CEO), deputies, signatories, and other registered roles for a company. Names, birth dates, and resigned/deregistered status.
curl 'https://api.nordicdata.cloud/companies/923609016/officers' \ -H "X-API-Key: $NORDIC_KEY"
{
"orgnr": "923609016",
"count": 16,
"officers": [
{ "role": "Daglig leder", "role_code": "DAGL",
"person": { "name": "Anders Opedal", "born": "1968-05-04" },
"resigned": false },
{ "role": "Styrets leder", "role_code": "LEDE",
"person": { "name": "Jon Erik Reinhardsen", "born": "1956-11-30" },
"resigned": false }
]
}
Common role_code values: DAGL (CEO), LEDE (Chair), NEST (Deputy chair), MEDL (Board member), VARA (Deputy member), REGN (Accountant), REVI (Auditor), KONT (Contact).
Contact info GET/companies/:orgnr/contact
Verified business emails and phone numbers, plus named key personnel (executives, IR, media) when publicly documented. Aggregated from four sources and cached per company for 30 days.
How the data is built (four layers, each adding precision):
- Website crawl — fetches the company's own contact / about / press / investor-relations pages and pulls emails, phones, and JSON-LD
Organizationschema. - 1881.no fallback — directory listing for the registered company name + city. Catches switchboards and direct lines missing from the website.
- LLM extraction (Haiku 4.5) — runs over the already-loaded HTML when layers 1+2 found nothing. Strict anti-hallucination: every returned contact must appear in the source text.
- Web-search enrichment (Sonnet 4.5) — fires for every company in the background. Uses Anthropic's
web_searchtool to find role-tagged emails (IR, media, press, pension, owner relations), phone numbers with their context, and named executives (CEO, CFO, head of IR, etc.) with full role/title attribution.
Async behavior: the first request for a company returns the layer-1+2+3 result fast (under 5s). Layer 4 runs in the background and updates the cache; the next request returns the fully-enriched data in under 100 ms. The enrichment_in_progress response field signals whether background enrichment is still running.
curl 'https://api.nordicdata.cloud/companies/923609016/contact' \ -H "X-API-Key: $NORDIC_KEY"
Response (Equinor ASA):
{
"orgnr": "923609016",
"company_name": "EQUINOR ASA",
"website": "https://www.equinor.com",
"emails": [
"apost@equinor.com",
"irpost@equinor.com",
"methanol@equinor.com",
"safetyandenvironment@equinor.com"
],
"phones": [
"+47 519 90 000",
"+47 412 60 584",
"+47 480 80 212",
"+47 918 01 791"
],
"labels": {
"email:irpost@equinor.com": "Investor Relations",
"email:apost@equinor.com": "Authorities Contact (Norway)",
"email:methanol@equinor.com": "Methanol Enquiries",
"phone:+47 519 90 000": "Investor Relations / Main Switchboard",
"phone:+47 412 60 584": "Vice President Media Relations: Sissel Rinde",
"phone:+47 480 80 212": "Press Spokesperson: Ola Morten Aanestad",
"phone:+47 918 01 791": "SVP Investor Relations: Bård Glad Pedersen"
},
"named_contacts": [
{ "name": "Anders Opedal", "role": "Chief Executive Officer" },
{ "name": "Torgrim Reitan", "role": "Chief Financial Officer" },
{ "name": "Bård Glad Pedersen", "role": "Senior Vice President Investor Relations",
"phone": "+47 918 01 791" },
{ "name": "Sissel Rinde", "role": "Vice President Media Relations",
"phone": "+47 412 60 584" },
{ "name": "Jon Erik Reinhardsen", "role": "Chairman of the Board" }
],
"candidates": [
"post@equinor.com",
"kontakt@equinor.com",
"info@equinor.com"
],
"cached": true,
"fetched_at": "2026-05-12T09:00:00.000Z"
}
Field reference:
| Field | Description |
|---|---|
emails | Verified business email addresses. Excludes form-only inboxes, recruiters, third-party SaaS domains, placeholders. |
phones | Phone numbers in +47 XXX XX XXX format. Sources: company website, JSON-LD, 1881.no, web search. |
labels | Per-contact role/department hint, keyed "email:..." or "phone:...". Examples: "Investor Relations", "Switchboard", "CEO: Jane Doe". |
named_contacts | Array of key personnel sourced via web search. Each entry: name, role, optional email, optional phone. Filtered to current-role only — no former/retired employees. |
candidates | Pattern-guessed emails (post@, kontakt@, info@) at the company's domain. Not verified — probe before transactional use. |
cached | true if served from cache (TTL 30 days), false if freshly crawled. |
enrichment_in_progress | Present and true when web-search enrichment is running in the background for this company. Refresh in 30 s for the fully-enriched data. |
Tenders by company GET/companies/:orgnr/notices
Cross-product join: every public tender in our index where this company is the contracting authority (buyer). Useful for competitive intel, lead generation, KYC, and "what is this company up to" questions.
# All tenders where Statsbygg is the buyer curl 'https://api.nordicdata.cloud/companies/971278374/notices' \ -H "X-API-Key: $NORDIC_KEY"
OPT-301-Organization-Identifier field.
Company object schema
| Field | Type | Description |
|---|---|---|
| orgnr | string | 9-digit Norwegian organisation number. Stable identifier. |
| name | string | Registered legal name. |
| legal_form | object |
|
| registered | YYYY-MM-DD | Date the entity was first registered in Enhetsregisteret. |
| in_business_registry | boolean | Listed in Foretaksregisteret (commercial entities). |
| in_vat_registry | boolean | VAT-registered (Mva-registeret). |
| status | string | One of |
| nace | array | Up to three NACE industry codes with descriptions, in declared priority order. |
| employees | integer? | Reported employee count. |
| business_address | object? | Physical address. |
| postal_address | object? | Postal address. |
| website | string? | Self-declared website. |
| sector | object? | Institutional sector code per Statistics Norway, with description. |
| parent_orgnr | string? | Organisation number of the parent entity, if any. |
| bankruptcy | boolean | True if currently in bankruptcy proceedings. |
Tender object schema
/tenders/* endpoints return notices from TED (above-EU-threshold, all 5 Nordic countries) and Doffin (Norway-only, including below-threshold municipal/county procurements that TED never sees). Each result includes a source field ("TED" or "DOFFIN"). Filter by source via ?source=DOFFIN on search, or by Norwegian buyer org-number via ?buyer_orgnr=941827195.
| Field | Type | Description |
|---|---|---|
| id | string | Stable identifier. TED format: |
| source | string |
|
| title | string | Notice title. TED titles are English-first multilingual. Doffin titles are Norwegian. |
| buyer.name | string | Contracting authority name. |
| buyer.orgnr | string? | 9-digit Norwegian organisation number. Always present for Doffin notices; null for non-Norwegian TED notices. |
| buyer.city | string | City (TED) or region (Doffin) of the contracting authority. |
| buyer.country | string | ISO 3166-1 alpha-3 country code. Doffin notices are always |
| publication_date | YYYY-MM-DD | Date the notice was first published. |
| deadline | YYYY-MM-DD? | Tender response deadline. |
| contract_nature | string[] | One or more of |
| cpv_codes | string[] | CPV (Common Procurement Vocabulary) codes. TED only — Doffin search-API does not expose CPV. |
| description | string? | Description summary. TED is multilingual joined with |
| total_value | number? | Estimated or awarded total value. TED normalises to EUR; Doffin returns the original currency (see |
| currency | string? | ISO 4217 currency code (e.g. |
| procedure_type | string? | TED: procedure code ( |
| notice_url | URL | Direct link to the original TED notice in English. |
Agent buying — quick start
Nordic Data is one of the few APIs an autonomous agent can buy and use without a human in the loop. Three endpoints are designed for this:
GET /agent/plans— discover plans + pricesPOST /agent/subscribe— subscribe with a Stripe payment method, get a working API key in the responsePOST /agent/change-plan— upgrade or downgrade in place using the API key as auth
No browser. No hosted Checkout page. No email round-trip. No Customer Portal.
pm_xxx token. Stripe requires either browser Elements, Stripe Issuing, or a virtual-card service like Privacy.com to mint a payment method. Once an agent has a card token (typically provisioned once by its operator), the rest of the lifecycle is fully autonomous.
End-to-end, an agent goes from "no account" to "calling tools" in a single API request:
curl -X POST https://api.nordicdata.cloud/agent/subscribe \ -H "Content-Type: application/json" \ -d '{ "email": "agent@example.com", "plan": "starter", "payment_method": "pm_xxx_from_stripe", "country": "NO", "agent_name": "my-procurement-agent" }' # → { "api_key": "nrd_live_...", "plan": "starter", "monthly_limit": 25000, "subscription_id": "sub_...", "customer_id": "cus_...", "next_renewal": "2026-06-09T19:48:07.000Z", "usage": "Send X-API-Key: <api_key> on every request to /tenders/*, /companies/*, /me, /mcp." }
Discover plans GET/agent/plans
Public endpoint — no auth. Use this to enumerate plans before subscribing.
curl https://api.nordicdata.cloud/agent/plans # → { "plans": [ { "id": "free", "price_eur_month": 0, "monthly_limit": 5000, "buy_via": "website (email verification required)" }, { "id": "starter", "price_eur_month": 29, "monthly_limit": 25000, "buy_via": "POST /agent/subscribe" }, { "id": "builder", "price_eur_month": 99, "monthly_limit": 60000, "buy_via": "POST /agent/subscribe" }, { "id": "pro", "price_eur_month": 149, "monthly_limit": 100000, "buy_via": "POST /agent/subscribe" }, { "id": "business", "price_eur_month": 499, "monthly_limit": 500000, "buy_via": "POST /agent/subscribe" } ], "currency": "EUR", "vat_note": "Norwegian MVA / EU VAT added at checkout based on customer country." }
Subscribe POST/agent/subscribe
Public endpoint — no auth. Creates (or upgrades from free) a Stripe subscription with auto-charge on renewal, and returns a fresh API key.
| Field | Type | Description |
|---|---|---|
| string | Required. Owner email — also used to deduplicate (one active subscription per email). | |
| plan | string | Required. One of |
| payment_method | string | Required. Stripe PaymentMethod ID ( |
| country | string | Required. ISO 3166-1 alpha-2 code (e.g. |
| agent_name | string? | Optional. Free-text label (≤80 chars) — stored on both the API key and the Stripe customer for your own bookkeeping. |
Response: {api_key, plan, monthly_limit, subscription_id, customer_id, next_renewal, usage}. The API key is shown once — store it.
Errors:
400 invalid_email/invalid_plan/invalid_payment_method— bad input402 card_declined— Stripe refused the card. Body includes Stripe'scode.402 payment_incomplete— subscription created but card needs 3DS or extra auth. Body includessubscription_idandstatus.409 already_subscribed— this email already has an active paid subscription. Use/agent/change-planwith the existing key instead.503 stripe_not_configured— server misconfiguration.
Change plan POST/agent/change-plan
Authenticated with X-API-Key. Upgrades or downgrades the subscription in place. Stripe applies a prorated charge for the current period — no Customer Portal required.
curl -X POST https://api.nordicdata.cloud/agent/change-plan \ -H "X-API-Key: nrd_live_..." \ -H "Content-Type: application/json" \ -d '{ "plan": "pro" }' # → { "ok": true, "plan": "pro", "monthly_limit": 100000, "subscription_id": "sub_...", "next_renewal": "2026-06-09T19:48:07.000Z", "message": "Plan changed from starter to pro. Prorated charge applied to current period." }
The API key is unchanged after a plan change — agents can keep using the same X-API-Key on every subsequent call. Only the plan and monthly_limit on GET /me reflect the new tier.
Model Context Protocol
The MCP server exposes the same data as the REST API, but in a form your agent can discover and call automatically. Transport is Streamable HTTP. URL: https://api.nordicdata.cloud/mcp. Authenticate with your API key in the X-API-Key header — same key as REST. 28 tools are registered, grouped below.
| Tool | Use for |
|---|---|
| Procurement (TED + Doffin) | |
| search_tenders | Search public tenders. Filter by |
| get_tender | Full details by ID. Accepts both TED format ( |
| search_awards | Search contract awards (who won what, for how much). |
| get_tender_leaderboard | Top public-sector buyers in a Nordic country. |
| get_company_contract_wins | Public-sector contracts won by a Norwegian company. |
| Norwegian company registry | |
| search_companies | Search by name, industry (NACE), or location. |
| get_company | Full registry record by orgnr. |
| get_company_contact | Verified emails + phones with role labels (Investor Relations, Switchboard, Media, etc.), plus |
| get_company_narrative | AI-generated executive summary. |
| get_company_peers | Peer-cohort benchmarks. |
| get_company_snapshot | One-call snapshot across every data source. |
| get_company_changes | Registry change history. |
| get_company_subsidiaries | Subsidiaries registered under this orgnr. |
| bulk_get_companies | Enrich up to 100 companies in one call. |
| Financials & ownership | |
| get_company_accounts | Annual accounts (revenue, profit, equity). |
| get_company_shareholders | Full shareholder list from Aksjonærregisteret (3M+ positions across 396K companies). |
| get_shareholder_portfolio | Reverse: all companies a person/entity owns shares in. |
| Officer & network graph | |
| search_persons | Search persons in the Norwegian officer network. |
| get_person | Full role history across Norwegian companies. |
| find_company_path | Shortest path between two Norwegian companies via shared officers. |
| get_person_network | Who shares boards with this person (1st-degree). |
| News | |
| get_company_news | Recent Norwegian-language news mentioning a company. |
| search_news | Free-text Norwegian news search. |
| EU R&D | |
| search_eu_grants | Search Horizon Europe grants (Cordis). |
| get_company_eu_grants | Norwegian company's Horizon Europe participations. |
| Tech intelligence | |
| find_companies_using_tech | Norwegian companies using a specific technology. |
| Compliance | |
| screen_for_sanctions | Screen any name against OFAC SDN + EU sanctions. |
| check_company_sanctions | Sanctions screening for a Norwegian company + its officers. |
tools/list call returns all 28 tools with input/output schemas. Each tool description is written for LLM consumption — every parameter has a hint explaining when to use it. Tools share auth, rate limit, and quota under one API key, so an agent can chain them freely (e.g. search_tenders → get_company on the buyer's orgnr → get_company_shareholders) without worrying about per-tool credentials.
Setup: Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, or %APPDATA%\Claude\claude_desktop_config.json on Windows:
{
"mcpServers": {
"nordic-data": {
"transport": "streamableHttp",
"url": "https://api.nordicdata.cloud/mcp",
"headers": { "X-API-Key": "nrd_live_..." }
}
}
}
Restart Claude Desktop. All 28 tools appear in the MCP picker, grouped automatically by Claude. Try a prompt like:
"Find Norwegian municipal tenders for snow clearing under 5M NOK with deadlines this month, then pull the latest accounts and shareholders for the buyer."
This single prompt will chain search_tenders (with source: DOFFIN), get_company_accounts, and get_company_shareholders automatically.
Setup: Cursor
Cursor >= 0.45 supports remote MCP servers. Edit ~/.cursor/mcp.json:
{
"mcpServers": {
"nordic-data": {
"url": "https://api.nordicdata.cloud/mcp",
"headers": { "X-API-Key": "nrd_live_..." }
}
}
}
Reload the window. The tools become available to Cursor's chat agent.
Setup: any MCP-aware client
The endpoint is standards-compliant Streamable HTTP. Any client that implements the protocol can connect:
URL: https://api.nordicdata.cloud/mcp
Method: POST
Headers: Content-Type: application/json
Accept: application/json, text/event-stream
X-API-Key: nrd_live_...
Body: JSON-RPC 2.0 messages — initialize, tools/list, tools/call, etc.
Sessions are tracked server-side via the Mcp-Session-Id response header on the initialize call. Send that header back on subsequent calls.
Build agents in code
If you're building your own agent (TypeScript, Python, etc.), connect via the official MCP SDKs.
import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; const client = new Client({ name: 'my-agent', version: '1.0' }); await client.connect(new StreamableHTTPClientTransport( new URL('https://api.nordicdata.cloud/mcp'), { requestInit: { headers: { 'X-API-Key': process.env.NORDIC_KEY } } } )); const result = await client.callTool({ name: 'search_tenders', arguments: { country: 'NOR', q: 'fishing', limit: 5 }, }); console.log(result.content);
from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client import os, asyncio async def main(): headers = {"X-API-Key": os.environ["NORDIC_KEY"]} async with streamablehttp_client("https://api.nordicdata.cloud/mcp", headers=headers) as (read, write, _): async with ClientSession(read, write) as session: await session.initialize() tools = await session.list_tools() result = await session.call_tool("search_tenders", {"country": "ALL", "q": "renewable"}) print(result.content) asyncio.run(main())
# Connect Claude directly to the MCP server. import anthropic client = anthropic.Anthropic() response = client.messages.create( model="claude-opus-4-7", max_tokens=2048, mcp_servers=[{ "type": "url", "url": "https://api.nordicdata.cloud/mcp", "name": "nordic-data", "authorization_token": "nrd_live_...", # sent as Bearer; we accept X-API-Key }], messages=[{"role": "user", "content": "Find Norwegian green-energy tenders from this month."}], ) print(response.content)
Error codes
| Status | Body | Meaning |
|---|---|---|
| 400 | invalid_email | Email is malformed or from a disposable provider. |
| 400 | invalid_id | Tender ID does not match |
| 401 | unauthorized | Missing or invalid |
| 404 | not_found | Tender ID did not match any record in TED. |
| 429 | rate_limited | Monthly request budget exhausted. |
| 502 | ted_upstream_error | TED returned an error on a fall-through fetch. Includes upstream details. |
| 503 | degraded | Database is unreachable. Status mirrored at /health. |
Rate limits
Limits are per calendar month, reset at midnight UTC on the first of each month.
| Plan | Monthly | Per-second burst | MCP sessions |
|---|---|---|---|
| Starter | 1,000 | 2 / sec | 2 concurrent |
| Pro | 50,000 | 20 / sec | 10 concurrent |
| Business | 500,000 | 200 / sec | 50 concurrent |
If you need higher limits — e.g. you're building a real-time procurement-monitoring product — email support@nordicdata.cloud with your expected RPS.
CPV reference
CPV (Common Procurement Vocabulary) is the EU's classification system for procurement. Codes are 8 digits + a 1-digit checksum. The first two digits identify the broad category. Common ones:
| Code | Category |
|---|---|
| 03000000 | Agricultural, farming, fishing, forestry products |
| 09000000 | Petroleum products, fuel, electricity, energy sources |
| 33000000 | Medical equipment, pharmaceuticals, personal care |
| 45000000 | Construction work |
| 50000000 | Repair and maintenance services |
| 71000000 | Architectural, engineering, technical consulting |
| 72000000 | IT services: consulting, software development, internet |
| 79000000 | Business services: legal, marketing, consulting |
| 80000000 | Education and training services |
| 85000000 | Health and social work services |
| 90000000 | Sewage, refuse, cleaning, environmental services |
Full reference at simap.ted.europa.eu/cpv. You can pass any leading subset: cpv=45 matches every 45xxxxxx construction code, cpv=4521 narrows to building work, cpv=45211000 is exact.
Changelog
v0.5 — 2026-05-08
- Companies API (Norway) — search, lookup, officers, tender-history join, all backed by Brønnøysund Open Data.
- Two new MCP tools:
search_companies,get_company. - Cross-product join:
GET /companies/{orgnr}/noticesreturns tenders where a company is the buyer. - Same API key works across both products. Pooled monthly limit.
v0.4 — 2026-05-08
- Cross-Nordic search:
country=ALLacross NOR/SWE/DNK/FIN/ISL. - SWE, DNK, FIN, ISL bootstrapped with 90-day history.
- Background sync now covers all 5 countries every 30 min.
/countriesendpoint with live counts per country.- Disposable-email blocking on key issuance.
- Public
/signupwith email verification + Stripe checkout for paid plans. - CPV leading-subset filter:
cpv=45matches every45xxxxxxcode. - This documentation page.
v0.3 — 2026-05-08
- API key auth on
/tenders/*and/mcp. - MCP server with stateful sessions over Streamable HTTP.
- Local DB cache with 30-min TED sync.
v0.2 / v0.1
- Initial REST API: search, get-by-id. Norwegian tenders only.