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. On top of that, a 7-day trial gives unlimited API and MCP use. No card required; credits are valid for 7 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; Poland returns no person names; Swedish and Polish sole traders are held but not sold). 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 7 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 or unknown parameter (e.g. unsupported country, or a misspelt filter name: nothing is run or charged) |
| 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, list_locations, count_companies,
search_companies, get_company (single-company
verification lookup by registration code), get_company_history and, for Sweden,
get_company_workplaces. Counts and metadata are free; search, detail, history and
workplaces 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/LT add share capital; PL adds the KRS filing filters
annual_report_filed and annual_report_audited.
{
"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_report_filed | bool | PL only: true = at least one annual report on file in KRS; false = a KRS company with none on file. Sole traders never file one and match neither side. The fact of the filing, not the figures. |
| annual_report_audited | bool | PL only: true = the latest filed report came with a statutory auditor's report. An audit is compulsory only above two of three size thresholds (staff, total assets, net revenue) and for every SA, so it works as a size tier. No report on file = matches neither side. |
| 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 | all countries. SE: SCB's Momsstatus, dormant companies included — a company registered but not active in SCB's register has its own answer, so false covers them too. |
| last_quarter_turnover_min/max | number | EE/LV/LT: EE the latest declared quarter; LV and LT the latest financial year |
| last_quarter_employees_min/max | number | EE/LV/LT/NO: EE latest quarter, LV latest annual report, LT latest month (Sodra), NO the number Brønnøysund publishes now (none for units under five employees). A company with no figure never matches. |
| kapital_min/max | number | NO/PL/LT |
| esmaregistreerimise_from/to | date | all countries (YYYY-MM-DD). PL: legal persons by their KRS (or REGON) entry date; rows without a date match neither bound. |
| Sweden only — SCB Företagsregistret (REST API, MCP and the portal; a company without SCB data matches none of these) | ||
| se_export_import | bool | true = exports or imports goods, false = does not |
| se_owner_category | list[int] | 10 state · 20 municipality · 30 region · 41 private Swedish, not in a group · 42 private Swedish, in a group · 50 foreign-owned |
| se_sme_class | list[int] | EU SME class by employees: 0 = 0 · 1 = 1-9 · 2 = 10-49 · 3 = 50-249 · 4 = 250-499 · 5 = 500+ |
| se_listing | list[int] | 0 not a limited company · 10 private AB · 20 public AB, not listed · 21 Nasdaq Stockholm Large Cap · 22 Mid Cap · 23 Small Cap · 24 Spotlight · 25 First North |
| se_turnover_class | list[int] | SCB's 21 fine turnover classes in thousands of SEK (1 < 1 … 21 > 9 999 999); finer than turnover_band, bands not figures |
| se_activity_start_from/to | date | when the business started operating (SCB Startdatum), not its registration |
| se_trade_name | str | part of a registered trade name (SCB Firma), 3+ characters |
| se_workplace_kommun | list[str] | companies with a workplace (branch) in these municipalities, whatever their seat; 4-digit SCB kommunkod, e.g. 0180 Stockholm, 1480 Göteborg, 1280 Malmö |
| 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 row at the country's price; refunded if the
company isn't found (404). Poland: person names come masked to initials (the register
redacts them) and sole-trader owners are left out.
Each figure in latest_metrics has its own period: turnover_period and
employees_period ({year, quarter, month, as_of}; quarter 0 and month 0 = an
annual figure, as_of = the period end where the source gives one). The top-level
year/month belong to whichever figure is newer, so read the two periods
before comparing them.
Each contact has a source: null = the country's business register;
brreg_subunit = listed in the Norwegian register for one of the company's own sub-units;
pl_regon / pl_krs / pl_ceidg = the Polish register it came from;
own_site = read from the company's own website (role addresses such as info@ only);
other values name an open dataset (for example uzt_lt, bis_lv,
overture).
Sweden: se_register carries SCB's attributes — export_import,
owner_category, sme_class, listing, turnover_class,
activity_start_date, trade_names — each code with its label, and the
source. Source attribution when you pass the data on: "Källa: SCB. Egen bearbetning av Innovadata OÜ."
| 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 / year_to_date / snapshot) and, where the source gives it, as_of = the period end; the top-level value is mixed when they differ. Costs one row at the country's
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). The current year's taxes are year_to_date (VMI publishes them cumulatively); a turnover row's as_of is the end of the company's financial year, which need not be 31 December. 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}
}
Workplaces (Sweden) METERED
Every active workplace (branch, SCB arbetsställe) of a Swedish company, the main one first:
name, postal and visiting address, municipality, size class, activity codes, phone, e-mail and the
address coordinate in SWEREF99 TM. Each workplace has its own marketing block, honoured like the
company's: addresses and coordinates are null under a postal block
(marketing_optout), the phone under a telephone block (phone_optout) — of
the workplace or of its company. Costs one row at the Swedish price; free when the company has
no workplace on file, refunded on 404. MCP: get_company_workplaces.
To find companies by branch location use se_workplace_kommun in search or count.
| Param | Type | Notes |
|---|---|---|
| registrikood path | str | Swedish registration code (10 digits) |
| country | enum | SE (the only country with workplaces) |
curl ".../api/v1/data/companies/5560000001/workplaces?country=SE" \ -H "$AUTH"
{
"data":{
"registrikood":"5560000001",
"workplaces":[
{"cfar_nr":42244590, "name":"Huvudkontor", "is_main":true,
"visit_address":"Exempelgatan 1", "kommun_code":"0180",
"employees_band_code":5, "marketing_optout":false}
],
"source":"SCB Företagsregistret, arbetsställen. Källa: SCB. Egen bearbetning av Innovadata OÜ."
},
"meta":{"credits_charged":"one SE row", "credit_balance":…}
}