Innovadata API Reference (Swagger) OpenAPI spec Get API key

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

BASE https://app.innovadata.eu

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.

Quick example
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:

HEADER Authorization: Bearer <your key>

Create a key in your dashboardAPI 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.

Requests without a valid key return 401 Unauthorized.
Authenticated request
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.

curl
# 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.

Keep keys server-side. Calls from a browser would expose your ik_live_… key to every visitor.
JavaScript (Node 18+)
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`);
Python (requests)
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

REST API, MCP and in-app Search all draw from one wallet balance. You pay per company row you pull; counts and metadata are free. New accounts get a 14-day free trial with unlimited, unmetered API + MCP use — when it ends you simply continue on pay-per-row credits, no cutoff. If per-row cost would exceed it, the optional Unlimited API & MCP plan (€1,500/month) waives metering for every country, and the per-country plan (€300/month per country) waives it for the countries you pick entirely; start it from your dashboard. The per-row prices below apply to all three.

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 price up 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 in every metered response
{
  "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": "…" }.

CodeMeaning
200OK
401Missing or invalid API key
402Payment required: insufficient wallet credits (top up in the dashboard)
404Not found (e.g. unknown company)
422Invalid parameter (e.g. unsupported country)
429Rate limit exceeded

Standard keys are rate-limited per key; admin/integration keys are unlimited.

402 example
{
  "detail": "insufficient credits: have 10, need 50"
}

MCP for AI agents

POST /mcp

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.

Metered from your wallet, like everything else. The MCP server and the full REST API are priced per row from the same credit balance (counts and metadata are free). New accounts get a 14-day free trial with unlimited, unmetered use; after it you continue on pay-per-row credits. The optional Unlimited API & MCP plan (€1,500/month) waives metering; the per-country plan (€300/month per country) waives it for chosen countries only. Start either from your dashboard. Create an API key, then connect below.

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 Code
claude mcp add innovadata https://app.innovadata.eu/mcp \
  --transport http \
  --header "Authorization: Bearer ik_live_…"
Claude API (MCP connector)
{
  "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

GET /api/v1/data/countries

Returns the countries you can query.

Response
{
  "countries": [
    {"code":"EE","name":"Estonia"},
    {"code":"LV","name":"Latvia"},
    … LT, NO, FI, PL, SE
  ]
}

Filters per country FREE

GET /api/v1/data/filters?country=EE

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.

FieldMeaning
paramquery param to send (or param_min/max, param_from/to)
typetext · select · tree · number_range · date_range · boolean
options_urlwhere 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.

Response (excerpt, EE)
{
  "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

GET /api/v1/data/activities?country=EE&lang=en

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.

Response (excerpt)
[
  {"code":"C","label":"Manufacturing",
   "children":[
     {"code":"10","label":"Food products"}
   ]}
]

Locations FREE

GET /api/v1/data/locations?country=EE

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).

Request
curl ".../api/v1/data/locations?country=EE&query=tallinn" -H "$AUTH"
Response (excerpt)
[
  {"kood":"0784","label":"Tallinn",
   "codes":["0784","0176","0298",…]}
]

Count companies FREE

GET /api/v1/data/count

Exact count of companies matching your filters. Call this first to size a query before spending credits. Accepts the same filters as search.

Request & response
curl ".../api/v1/data/count?country=EE&has_email=true" -H "$AUTH"
{ "country":"EE", "count":354244, "count_mode":"exact" }

Company detail METERED

GET /api/v1/data/companies/{registrikood}?country=EE

Full record for one company: contacts, activities and people. Costs one credit; refunded if the company isn't found (404).

ParamTypeNotes
registrikood pathstrregistration code
country requiredenumwhich country it belongs to
Request
curl ".../api/v1/data/companies/10000018?country=EE" \
  -H "$AUTH"
Response (excerpt)
{
  "data":{
    "registrikood":"10000018",
    "arinimi":"Acme OÜ",
    "contacts":[…], "emtak":[…], "people":[…]
  },
  "meta":{"credits_charged":1,"credit_balance":4997}
}

Company history METERED

GET /api/v1/data/companies/{registrikood}/history?country=EE

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.

ParamTypeNotes
registrikood pathstrregistration code
country requiredenumwhich country it belongs to
CoverageGranularityFields
EEquarterly, 2025 →turnover, employees, state & labour taxes
LVannual, 2006 →turnover, employees (annual report figures)
LTmixedemployees monthly (2021–2023, 2025; 2024 pending backfill); taxes & turnover as annual totals (dense 2021 →, sparse 2015–2020). Each row carries its own granularity.
NOsingle snapshotemployees
FI · PL · SEthe registries publish no series (granularity: "none", not charged)
Request
curl ".../api/v1/data/companies/10000018/history?country=EE" \
  -H "$AUTH"
Response (excerpt)
{
  "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}
}