Innovadata API
Query verified company data across 7 countries: Estonia, Latvia, Lithuania, Norway, Finland, Poland and Sweden. One simple REST API.
Filter by industry, location, size, financials, contact details and more, then pull the matching companies. Everything is JSON over HTTPS.
Start free: new accounts get 30 credits (€30 of data) on completing registration, enough for ≈ 750 Estonian company rows or hundreds of counterparty checks. No card required; credits are valid for 30 days.
Base URL
Each country exposes a slightly different filter set (Estonia has exact employee and turnover figures; Sweden has size bands instead, and no board members at all). The filters endpoint tells your app exactly which filters apply per country, so you never hardcode them.
curl https://app.innovadata.eu/api/v1/data/companies?country=EE&page_size=3 \ -H "Authorization: Bearer YOUR_API_KEY"
Authentication
Every request needs an API key, sent as a Bearer token:
Create a key in your dashboard → API keys → Create API key.
The full key (ik_live_…) is shown once. Store it securely. Keep keys server-side;
never embed them in a browser or mobile app.
401 Unauthorized.curl https://app.innovadata.eu/api/v1/data/countries \
-H "Authorization: Bearer ik_live_xxx"
Quickstart
From zero to data in three calls:
1 · See which filters a country supports
Returns the filter list for the country and the exact query params to send.
2 · Check how many companies match
A free count (0 credits, rate-limited only) to size your query before pulling rows.
3 · Pull the companies
Page through the results. You're charged per returned row.
# 1: filters for Estonia curl ".../api/v1/data/filters?country=EE" -H "$AUTH" # 2: count manufacturers with email curl ".../api/v1/data/count?country=EE&has_email=true" -H "$AUTH" # 3: pull 50 of them curl ".../api/v1/data/companies?country=EE&has_email=true&page_size=50" \ -H "$AUTH"
JavaScript & Python
Plain HTTPS + JSON — no SDK needed. The examples pull one page of Estonian companies with an email address and print the name, code and billing meta.
Parameter names. The original query parameters use internal
(Estonian) column names. Since 2026-08-02 every one of them has an English alias —
company_name, registration_code, industry_codes,
legal_form, status, share_capital_min/max,
registered_from/to. Both spellings work and will keep working; existing
integrations need no changes. Prefer the English names in new code — the
Estonian ones are marked deprecated in the
OpenAPI spec.
ik_live_… key to every visitor.const res = await fetch(
"https://app.innovadata.eu/api/v1/data/companies?" + new URLSearchParams({
country: "EE", has_email: "true", page_size: "50",
}),
{ headers: { Authorization: `Bearer ${process.env.INNOVADATA_API_KEY}` } },
);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const { data, meta } = await res.json();
for (const c of data.items) console.log(c.registrikood, c.arinimi);
console.log(`charged ${meta.credits_charged} credits, ${meta.credit_balance} left`);
import os, requests
r = requests.get(
"https://app.innovadata.eu/api/v1/data/companies",
params={"country": "EE", "has_email": True, "page_size": 50},
headers={"Authorization": f"Bearer {os.environ['INNOVADATA_API_KEY']}"},
timeout=30,
)
r.raise_for_status()
body = r.json()
for c in body["data"]["items"]:
print(c["registrikood"], c["arinimi"])
print("charged", body["meta"]["credits_charged"], "credits")
Credits & metering
1 credit = €1. Rows are priced per country: Estonia, Finland and
Poland €0.04 per row, Latvia and Lithuania €0.08, Norway €0.10, Sweden €0.24
(the same prices as the main site; they can change, so treat the
meta block in each response as authoritative).
- Search reserves
page_size × row priceup front and refunds the unused portion based on rows actually returned. - Company detail costs one row's per-country price (e.g. €0.04 for an Estonian company).
- Discovery and sizing are FREE: countries, filters, activities, locations and count cost nothing, so you can size a query before spending.
- New accounts get 30 free credits on completing registration (valid 30 days). Admin/integration keys are unlimited.
Top up or check your balance in the dashboard (top-ups from €20).
Each metered response includes a meta block with credits charged and your
remaining balance.
{
"meta": {
"rows_returned": 50,
"credits_charged": 2.0,
"credits_reserved": 2.0,
"credit_balance": 78.0
}
}
Errors & limits
Standard HTTP status codes. Error bodies are { "detail": "…" }.
| Code | Meaning |
|---|---|
| 200 | OK |
| 401 | Missing or invalid API key |
| 402 | Payment required: insufficient wallet credits (top up in the dashboard) |
| 404 | Not found (e.g. unknown company) |
| 422 | Invalid parameter (e.g. unsupported country) |
| 429 | Rate limit exceeded |
Standard keys are rate-limited per key; admin/integration keys are unlimited.
{
"detail": "insufficient credits: have 10, need 50"
}
MCP for AI agents
Innovadata is also a remote MCP server (Model Context Protocol,
Streamable HTTP). Connect it to Claude Code, Claude Desktop or any
MCP-capable agent and it gets tools to count, search and look up companies
across all 7 countries directly. Authenticate with your
ik_live_… key as a Bearer token.
Tools exposed: list_countries, list_filters,
list_activities, count_companies,
search_companies, get_company (single-company
verification lookup by registration code). Counts and metadata are free; search and detail are metered per row.
claude mcp add innovadata https://app.innovadata.eu/mcp \
--transport http \
--header "Authorization: Bearer ik_live_…"
{
"mcp_servers": [{
"type": "url",
"url": "https://app.innovadata.eu/mcp",
"name": "innovadata",
"authorization_token": "ik_live_…"
}],
"tools": [{ "type": "mcp_toolset",
"mcp_server_name": "innovadata" }]
}
List countries FREE
Returns the countries you can query.
{
"countries": [
{"code":"EE","name":"Estonia"},
{"code":"LV","name":"Latvia"},
… LT, NO, FI, PL, SE
]
}
Filters per country FREE
The heart of the API: returns the filter set for a country, exactly like the website's filter
panel. Each filter tells you the param to send to search, its
type, and (for tree filters) an options_url.
| Field | Meaning |
|---|---|
| param | query param to send (or param_min/max, param_from/to) |
| type | text · select · tree · number_range · date_range · boolean |
| options_url | where to fetch choices (tree filters) |
Differences are real: EE uses ehak_kood + EMTAK + employees/turnover; LT uses
lt_locations; NO/SE use free-text location; FI/NO/PL/SE add
vat_active; NO/PL add share capital.
{
"country":"EE", "industry_system":"EMTAK",
"filters":[
{"param":"emtak_codes","type":"tree",
"options_url":"/api/v1/data/activities?country=EE"},
{"param":"ehak_kood","type":"tree",
"options_url":"/api/v1/data/locations?country=EE"},
{"param_min":"last_quarter_employees_min",
"param_max":"last_quarter_employees_max","type":"number_range"},
{"param":"has_email","type":"boolean"}
]
}
Activity codes FREE
The activity-classification tree for a country (EMTAK for EE, NACE/PKD/EVRK/SNI for the
others). Use the leaf code values as emtak_codes when searching.
[
{"code":"C","label":"Manufacturing",
"children":[
{"code":"10","label":"Food products"}
]}
]
Locations FREE
Location tree for EE, LV, LT, FI, PL. NO and SE have no tree; they use the free-text
location filter, so this endpoint returns 400 for them.
How to filter by a node: EE/LV/FI/PL nodes carry a codes array (the node
plus all its descendants) — send that array as ehak_kood. A bare parent
kood matches only rows tagged with exactly that code (Tallinn 0784
alone ≈ 338 rows; its full codes ≈ 158 000). LT nodes carry a
locations array of name variants instead — send it as lt_locations.
Full trees are large (EE ~800 KB). query= searches nodes by name and returns
them flat (max 100); max_depth= prunes the tree (1 = top level only).
curl ".../api/v1/data/locations?country=EE&query=tallinn" -H "$AUTH"
[
{"kood":"0784","label":"Tallinn",
"codes":["0784","0176","0298",…]}
]
Count companies FREE
Exact count of companies matching your filters. Call this first to size a query before spending credits. Accepts the same filters as search.
curl ".../api/v1/data/count?country=EE&has_email=true" -H "$AUTH"
{ "country":"EE", "count":354244, "count_mode":"exact" }
Search companies METERED
Search and page through company data. country is required; all other filters are
optional and country-specific (see filters).
| Param | Type | Notes |
|---|---|---|
| country required | enum | EE, LV, LT, NO, FI, PL, SE |
| emtak_codes | list | activity codes (any country) |
| ehak_kood / lt_locations / location | list/text | location (per country) |
| arinimi | list | company name match |
| staatus | list | status (omit = active only) |
| has_email / has_phone / has_website | bool | contact filters |
| has_domain | bool | EE/FI: only companies with a registered domain (registry-sourced domain registered to the company, separate from website). Exported as the registered_domains column; per-company list in get_company. |
| has_tax_debt | bool | EE only: true = has disclosed tax debt, false = no tax debt (EMTA public list) |
| annual_revenue_min/max | number | EE/NO/LV: annual-report revenue (reporting currency: EUR for EE, NOK for NO) |
| annual_net_profit_min/max | number | EE/NO/LV: annual net profit (negative = loss; 0 = profitable only) |
| annual_equity_min/max · annual_total_assets_min/max | number | EE/NO/LV: balance-sheet figures |
| vat_active | bool | FI/NO/PL/SE |
| last_quarter_turnover_min/max | number | EE/LV/LT |
| last_quarter_employees_min/max | number | EE/LV/LT |
| kapital_min/max | number | NO/PL |
| esmaregistreerimise_from/to | date | SE (YYYY-MM-DD) |
| page / page_size | int | page_size ≤ 500 |
| sort_by / sort_dir | str | asc / desc |
curl ".../api/v1/data/companies?country=EE\ &emtak_codes=10&has_email=true&page_size=2" -H "$AUTH"
{
"data":{
"items":[
{"registrikood":"10000018",
"arinimi":"Acme OÜ",
"staatus":"R", "emails":["info@acme.ee"]}
],
"total":1234, "page":1, "page_size":2
},
"meta":{"rows_returned":2,"credits_charged":2,"credit_balance":4998}
}
Company detail METERED
Full record for one company: contacts, activities and people. Costs one credit; refunded if the
company isn't found (404).
| Param | Type | Notes |
|---|---|---|
| registrikood path | str | registration code |
| country required | enum | which country it belongs to |
curl ".../api/v1/data/companies/10000018?country=EE" \ -H "$AUTH"
{
"data":{
"registrikood":"10000018",
"arinimi":"Acme OÜ",
"contacts":[…], "emtak":[…], "people":[…]
},
"meta":{"credits_charged":1,"credit_balance":4997}
}
Company history METERED
Time series of turnover and employee counts for one company, straight from the
official registers, in chronological order — see growth or decline over time. Each row carries its own granularity (monthly / quarterly / annual / snapshot); the top-level value is mixed when they differ. Costs one credit at the country's
row price; free when the company has no history, and refunded on 404.
| Param | Type | Notes |
|---|---|---|
| registrikood path | str | registration code |
| country required | enum | which country it belongs to |
| Coverage | Granularity | Fields |
|---|---|---|
| EE | quarterly, 2025 → | turnover, employees, state & labour taxes |
| LV | annual, 2006 → | turnover, employees (annual report figures) |
| LT | mixed | employees monthly (2021–2023, 2025; 2024 pending backfill); taxes & turnover as annual totals (dense 2021 →, sparse 2015–2020). Each row carries its own granularity. |
| NO | single snapshot | employees |
| FI · PL · SE | the registries publish no series (granularity: "none", not charged) | |
curl ".../api/v1/data/companies/10000018/history?country=EE" \ -H "$AUTH"
{
"data":{
"registrikood":"10000018",
"granularity":"quarterly",
"periods":[
{"period":"2025-Q1", "turnover":412805.6,
"employees":12, "labour_taxes":48210.9},
{"period":"2025-Q2", "turnover":455102.3,
"employees":13, "labour_taxes":51877.4}
]
},
"meta":{"credits_charged":1,"credit_balance":4996}
}