API Dokumentation
Alt du behøver for at integrere danske virksomhedsdata i din applikation.
Kom i gang
Du kan integrere FirmaAPI i din applikation på under 5 minutter. Tre simple trin:
Tilføj API-nøgle
Send din API-nøgle som Bearer token i Authorization headeren.
Kald API'et
Foretag dit første kald og modtag virksomhedsdata som JSON.
curl https://firmaapi.dk/api/v1/company/24256790 \
-H "Authorization: Bearer cvr_live_din_api_nøgle"Autentifikation
Alle API-kald kræver en gyldig API-nøgle. Du kan sende nøglen på to måder:
Authorization: Bearer cvr_live_abc123def456...X-API-Key: cvr_live_abc123def456...Om API-nøgler
- ●Nøgler starter med
cvr_live_prefix og er 49 tegn lange - ●Du kan oprette flere nøgler fra dit dashboard
- ●Nøgler kan deaktiveres og genaktiveres uden at slette dem
- ●Hold dine nøgler hemmelige – del dem aldrig i offentlig kode
Sikkerhed: Kald aldrig API'et direkte fra klient-side kode (browser/app). Brug altid en server-side proxy, så din API-nøgle forbliver hemmelig.
Base URL
Alle API-kald foretages mod følgende base URL:
https://firmaapi.dk/api/v1/Miljøer
https://firmaapi.dk/api/v1/Alle data og infrastruktur er hosted i EU. GDPR-compliant.
Sandbox / test-nøgler
Test din integration uden at bruge betalte API-kald. Tag din live-nøgle og erstat cvr_live_ med cvr_test_:
# Din live-nøgle:
cvr_live_abc123def456...
# Din test-nøgle (samme nøgle, andet prefix):
cvr_test_abc123def456...curl https://firmaapi.dk/api/v1/company/24256790 \
-H "Authorization: Bearer cvr_test_abc123def456..."Hvad gør test-nøgler?
- ●Returnerer syntetisk data med alle enterprise-felter (ejere, regnskab, historik, GPS m.m.)
- ●Ingen rate limits, ingen kvote-forbrugt — perfekt til integrationstest
- ●Response inkluderer
X-Sandbox: trueogX-Billing-Counted: falseheaders - ●Alle endpoints understøttes: company, search, batch, person, regnskab, webhooks
CVR-opslag
Hent komplet virksomhedsdata via et 8-cifret CVR-nummer. For de fleste integrationer er dette det primære endpoint, og include= bruges kun til ekstra, tunge datablokke.
/api/v1/company/{cvr}| Parameter | Type | Beskrivelse |
|---|---|---|
cvr* | string | 8-cifret CVR-nummer (i URL path) |
curl https://firmaapi.dk/api/v1/company/24256790 \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/company/24256790?include=history,finance,production_units,relations,events" \
-H "Authorization: Bearer cvr_live_..."{
"cvr": "24256790",
"name": "Novo Nordisk A/S",
"address": "Novo Allé 1",
"zipcode": "2880",
"city": "Bagsværd",
"status": "Aktiv",
"company_type": "Aktieselskab",
"industry_code": "212000",
"industry_text": "Fremstilling af farmaceutiske præparater",
"founded": "1989-01-01",
"employees": "1000+",
"phone": "44448888",
"email": null,
"website": "https://www.novonordisk.com",
"ad_protected": false,
"secondary_industries": [
{ "code": "721100", "text": "Forskning og udvikling inden for bioteknologi" }
],
"capital": 500000000,
"capital_currency": "DKK",
"signing_rule": "Selskabet tegnes af bestyrelsens formand...",
"production_unit_count": 12,
"relations": {
"parents": [
{ "cvr": "24257630", "name": "Novo Holdings A/S", "ownership_share": 75, "voting_share": 75, "relation_type": "parent", "inferred": true }
],
"subsidiaries": [
{ "cvr": "12345678", "name": "Eksempel Datter A/S", "ownership_share": 100, "voting_share": 100, "relation_type": "subsidiary", "inferred": true }
]
},
"executives": [
{ "name": "Lars Fruergaard Jørgensen", "function": "ADM. DIR." }
],
"board_members": [
{ "name": "Helge Lund", "function": "BESTYRELSESFORMAND", "election_method": "GENERALFORSAMLING" }
],
"meta": {
"cached_at": "2025-01-15T12:00:00Z",
"source": "FirmaAPI"
}
}Tip: Response inkluderer en X-Data-Age header med alderen på cached data i sekunder.
Anbefaling: Start med /api/v1/company/{cvr}. Company-responsen indeholder allerede alle plan-tilgængelige virksomhedsfelter. Brug kun include= til ekstra blokke som historik, regnskaber, produktionsenheder, relationer og hændelser.
Anbefalede include-blokke
Disse blokke hentes kun når du eksplicit beder om dem med include=. Det holder standardresponsen hurtig og slank.
| Include | Indhold | Plan |
|---|---|---|
| history | Historik Henter historiske ændringer for navn, adresse, status, branche og ansatte. Basis+ får ejerregister-poster; Standard+ får ekstra beskæftigelse; Professionel+ får kontakt- og faxhistorik. | Basis+ |
| finance | Regnskaber Henter regnskabsdata som ekstra blok, inkl. nøgletal (Standard+), årsændringer og udvidede felter (Professionel+). | Standard+ |
| production_units | Produktionsenheder Henter P-enheder som separat blok. Standard får p_number/name; Professionel+ får adresse, branche og ansatte. | Standard+ |
| relations | Koncernrelationer Henter direkte moder-/datterselskabsrelationer som en ekstra blok. | Professionel+ |
| events | Hændelser Henter virksomhedens livscyklus, fusioner/spaltninger og en afledt hændelsestidslinje. | Standard+ |
Felter der følger automatisk med
Disse datablokke returneres automatisk på company-endpointet, når planen giver adgang. Du behøver ikke include= for dem.
| Data | Felter | Plan |
|---|---|---|
Ejere og ledelse history.register_entries til legale ejere og executives/board_members til ledelse. | history.register_entriesexecutivesboard_members | Basis+ |
Revisor-information Følger automatisk med på company-endpointet for Professionel og Enterprise. | auditors | Professionel+ |
Ejerskabshistorik Følger automatisk med på company-endpointet for Professionel og Enterprise. | ownership_history | Professionel+ |
GPS-koordinater Latitude og longitude følger automatisk med på company-endpointet. | latitudelongitude | Professionel+ |
Udvidede kontaktoplysninger Alle e-mailadresser, alle telefonnumre og fax følger automatisk med. | all_emailsall_phonesfax | Professionel+ |
Udvidede virksomhedsattributter Audit form og regnskabsårsstart følger automatisk med. | audit_formfiscal_year_start | Professionel+ |
Historik over sekundære brancher Sekundær branchehistorik følger automatisk med på company-endpointet. | secondary_industry_history | Professionel+ |
ERST-attributter (alle planer) Formål, binavne, regnskabsårsslut, regnskabsklasse, børs-/revisionsflag og årlig beskæftigelse følger automatisk med. | purposesecondary_namesfiscal_year_endaccounting_classis_listedaudit_opted_outannual_employees | Alle planer |
ERST-attributter (Standard+) Arbejdsgiver-/momsregistrering, import/eksport-status, punktafgifter og første regnskabsperiode. | employer_registration_datevat_registration_dateimport_registeredexport_registeredexcise_registrationsfirst_fiscal_period_endfirst_fiscal_period_start | Standard+ |
Navnesøgning
Søg efter virksomheder via navn. Understøtter fuzzy matching og delvis søgning.
/api/v1/company/search?q={query}| Parameter | Type | Beskrivelse |
|---|---|---|
q* | string | Søgeterm, minimum 2 tegn |
status | string | Filtrer på status, f.eks. "Aktiv". Kommasepareret for flere. |
limit | integer | Antal resultater (1-50, default 10) |
curl "https://firmaapi.dk/api/v1/company/search?q=novo+nordisk&status=Aktiv&limit=5" \
-H "Authorization: Bearer cvr_live_..."{
"results": [
{
"cvr": "24256790",
"name": "Novo Nordisk A/S",
"address": "Novo Allé 1",
"zipcode": "2880",
"city": "Bagsværd",
"status": "Aktiv",
"company_type": "Aktieselskab",
"industry_text": "Fremstilling af farmaceutiske præparater",
...,
"meta": {
"cached_at": "2025-01-15T12:00:00Z",
"source": "FirmaAPI"
}
}
],
"total": 12
}Batch-opslag
Slå op til 100–500 virksomheder op i en enkelt request (afhængigt af plan). Kræver Basis eller højere. Perfekt til databerigelse, CRM-integration og massevalidering.
/api/v1/company/batchRequest body (JSON)
| Parameter | Type | Beskrivelse |
|---|---|---|
cvr_numbers* | string[] | Array af CVR-numre (max afhænger af plan: Basis 100, Standard 200, Professionel 500) |
curl -X POST https://firmaapi.dk/api/v1/company/batch \
-H "Authorization: Bearer cvr_live_..." \
-H "Content-Type: application/json" \
-d '{"cvr_numbers": ["24256790", "54562519", "25313763"]}'{
"results": [
{ "cvr": "24256790", "name": "Novo Nordisk A/S", ..., "meta": { "cached_at": "...", "source": "FirmaAPI" } },
{ "cvr": "54562519", "name": "LEGO A/S", ..., "meta": { "cached_at": "...", "source": "FirmaAPI" } },
{ "cvr": "25313763", "name": "Nets Denmark A/S", ..., "meta": { "cached_at": "...", "source": "FirmaAPI" } }
],
"errors": [],
"total": 3,
"quota_used": 3
}Tip: Hvert CVR-nummer i et batch-kald tæller som ét opslag mod din månedlige kvote. Feltet quota_used i response viser det samlede antal opslag brugt.
Datakilde: Batch-opslag læser fra FirmaAPI's cache/database for at holde svartiden stabil. Hvis et CVR-nummer endnu ikke er synkroniseret, returneres det i errors. Brug single-opslag /api/v1/company/{cvr} når du vil tvinge et live fallback-opslag mod ERST.
Ejere og ledelse
Brug history.register_entries til legale ejere fra ejerregistret og executives/board_members til ledelse.
Plan-adgang: Gratis = ingen adgang. Basis+ får history.register_entries, executives og board_members. Professionel+ tilføjer person_id, personprofiler, ejerskabshistorik og udvidede governance-felter.
curl "https://firmaapi.dk/api/v1/company/24256790?include=history" -H "Authorization: Bearer cvr_live_..."{
"cvr": "24256790",
"name": "Novo Nordisk A/S",
"history": {
"register_entries": [{
"name": "Novo Holdings A/S",
"ownership_percentage": 0.281,
"voting_percentage": 0.765,
"valid_from": "1999-01-01",
"valid_to": null
}]
},
"executives": [
{
"name": "Lars Fruergaard Jørgensen",
"function": "ADM. DIR.",
"joined_date": "2017-01-01",
"left_date": null
}
],
"board_members": [
{
"name": "Helge Lund",
"function": "BESTYRELSESFORMAND",
"election_method": "GENERALFORSAMLING"
}
]
}Basis+: history.register_entries viser legale ejere fra ejerregistret med ejerandel og stemmeret. executives og board_members viser ledelse som separate felter. Professionel+: tilføjer person_id, personprofiler, ownership_history og udvidede governance-felter.
Deltager-CVR: participant_cvr er det bedste kendte CVR for en selskabsdeltager. Hvis ERST kun sender participant_enhedsnummer, forsøger FirmaAPI at udfylde CVR fra den lokale virksomheds-cache. Feltet kan stadig være null for personer, udenlandske enheder eller deltagere der endnu ikke kan resolves.
Personprofil-link: person_id er Professionel+ og kan sendes direkte til GET /api/v1/person/{id}. Feltet er null hvis deltageren er en virksomhed (fx et holdingselskab) eller endnu ikke er matchet til en personprofil. Samme felt findes på history.register_entries, auditors, ownership_history, executives og board_members for Professionel/Enterprise.
Personprofiler & søgning
Personprofiler
Ejer- og ledelsesdata kan kobles til personprofiler, så du kan følge samme person på tværs af flere virksomheder. Personrelationer er tilgængelige via API og vises også offentligt på /person/{id}/{slug}.
Personprofiler bruges til krydsopslag: find personen én gang, og få hele listen af virksomheder personen deltager i.
Sådan får du fat i et person_id: Kald GET /api/v1/company/{cvr} som Professionel/Enterprise-bruger. Hver post i history.register_entries, auditors, ownership_history, executives og board_members får et person_id-felt (UUID), som du kan sende direkte videre til /api/v1/person/{id}. Feltet er null, hvis deltageren er et selskab eller endnu ikke er matchet til en profil.
/api/v1/person/{id}| Parameter | Type | Beskrivelse |
|---|---|---|
id* | string (UUID) | Personprofil-UUID fra person_id-feltet på history.register_entries/auditors/ownership_history/executives/board_members i company-responsen. Dette er ikke CVR-enhedsnummeret. |
curl https://firmaapi.dk/api/v1/person/123e4567-e89b-12d3-a456-426614174000 -H "Authorization: Bearer cvr_live_..."{
"id": "123e4567-e89b-12d3-a456-426614174000",
"canonical_name": "Tommy Ahlers",
"slug": "tommy-ahlers",
"active_company_count": 12,
"historical_company_count": 8,
"total_company_count": 20,
"company_count": 20,
"affiliations": [
{
"cvr": "24256790",
"company_name": "Eksempel A/S",
"company_slug": "eksempel-as-24256790",
"role_title": "Bestyrelsesmedlem",
"joined_date": "2023-01-01",
"left_date": null,
"duration_days": 1234
}
]
}Aktive vs historiske roller: active_company_count tæller virksomheder hvor personen p.t. er registreret med en åben rolle (uden left_date) — brug det hvis du kun vil vise nuværende ledelses- og bestyrelsesposter. historical_company_count dækker tidligere, lukkede roller. For nogle advokater kan dette tal være meget højt pga. skuffeselskabs-mønsteret: en advokat står som initial direktør i et nyoprettet ApS i få dage, indtil selskabet sælges til en klient. Brug duration_days >= 180 på affiliations hvis du kun vil se reelle ledelsesrelationer.
Sådan bruger du persontilknytning i API'et
- Hent virksomhed via
/api/v1/company/{cvr}og brug personlink til videre opslag. - Hent personens samlede virksomhedsnetværk via
/api/v1/person/{id}. - Brug
affiliations[].cvrtil overvågning, scoring, relationstræ og netværksanalyse.
Plan-adgang: Gratis/Basis = ingen adgang. Professionel/Enterprise = adgang.
Personsøgning
Slå personprofiler op uden at starte i en virksomhed. Brug det til KYC-workflows, sales-prospecting og relationsgrafer hvor du kun har et navn — eller et enhedsnummer/CVR fra en anden kilde.
/api/v1/person/search?q={query}| Parameter | Type | Beskrivelse |
|---|---|---|
q* | string | Søgeterm, minimum 2 tegn. Navne normaliseres (unicode, whitespace, lowercase). Et rent numerisk query på 7+ cifre matches også direkte mod enhedsnummer/CVR fra tidligere company-respons. |
limit | integer | Antal resultater (1-50, default 10) |
curl "https://firmaapi.dk/api/v1/person/search?q=lars+sandahl&limit=5" \
-H "Authorization: Bearer cvr_live_..."{
"results": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"canonical_name": "Lars Sandahl Sørensen",
"slug": "lars-sandahl-sorensen",
"normalized_name": "lars sandahl sorensen",
"match_strategy": "strong_identity",
"match_confidence": 0.9,
"active_company_count": 5,
"historical_company_count": 2,
"total_company_count": 7,
"company_count": 7,
"match_type": "exact"
}
],
"total": 1
}Match-typer og sortering
strong_key— numerisk query matchede et enhedsnummer eller CVR fra PersonProfileAffiliation. Stærkeste match.exact— normaliseret navn er identisk med profilens kanoniske navn.alias— normaliseret navn matcher et registreret alias.prefix— navn eller alias starter med søgetermen.
Resultater sorteres efter match-type først, derefter efter virksomhedsantal (flest først), og til sidst efter canonical_name alfabetisk for stabil paginering. Hvert hit indeholder både active_company_count (nuværende roller) og historical_company_count (lukkede roller) — feltet company_count er bevaret som bagudkompatibelt alias for total_company_count.
Næste skridt: Send id videre til GET /api/v1/person/{id} for at hente fulde persontilknytninger med virksomhedsnavne, roller og perioder.
Plan-adgang: Gratis/Basis/Standard = ingen adgang. Professionel/Enterprise = adgang.
Regnskabsdata
Regnskabsdata hentes via include=finance på company-endpointet. Det holder standardresponsen hurtig og gør integrationen enklere.
Plan-adgang: Gratis/Basis = ingen adgang. Standard = seneste årsregnskab + PDF-link + nøgletal (ratios) + udvidet P&L/balance (personaleomkostninger, varebeholdninger m.m.). Professionel/Enterprise = alle regnskabsfelter, pengestrømsopgørelse, revisionspåtegning, kvartals-/delårsrapporter, nøgletal + årsændringer (growth).
curl "https://firmaapi.dk/api/v1/company/24256790?include=finance" -H "Authorization: Bearer cvr_live_..."{
"cvr": "24256790",
"has_financials": true,
"financials_pending": false,
"years_included": "5",
"financials": [
{
"year": 2024,
"period_start": "2024-01-01",
"period_end": "2024-12-31",
"report_type": "annual",
"revenue": 290324000000,
"gross_profit": 245891000000,
"profit_before_tax": 112456000000,
"net_profit": 83214000000,
"equity": 156789000000,
"total_assets": 312456000000,
"ebitda": 138200000000,
"cash": 42300000000,
"share_capital": 500000000,
"currency": "DKK",
"report_url": "http://regnskaber.virk.dk/...",
"ratios": {
"profit_margin": 0.2866,
"gross_margin": 0.8469,
"return_on_assets": 0.2663,
"return_on_equity": 0.5308,
"solvency_ratio": 0.5018,
"debt_to_equity": 0.9928
},
"growth": {
"revenue_yoy": 0.2351,
"profit_yoy": 0.1842,
"equity_yoy": 0.1523,
"total_assets_yoy": 0.0891
}
},
{
"year": 2024,
"period_start": "2024-07-01",
"period_end": "2024-09-30",
"report_type": "quarterly",
"revenue": 78500000000,
"currency": "DKK"
}
]
}Nøgletal og årsændringer: Alle værdier i ratios og growth er decimaler — gang med 100 for procent (f.eks. 0.2866 = 28,66%). Felter er null hvis de underliggende regnskabstal mangler eller divisor er 0.
{
"error": {
"code": "PLAN_UPGRADE_REQUIRED",
"include": "finance",
"message": "Regnskabsdata kræver Standard, Professionel eller Enterprise plan.",
"current_plan": "gratis",
"required_plan": "standard"
}
}Historik
Historiske ændringer hentes via include=history på company-endpointet. Her får du navn, adresse, status, branche, ansatte og virksomhedsform samlet i én blok. Basis+ får også ejerregisteret, Standard+ får kvartals-/månedsbeskæftigelse, og Professionel+ får kontakt- og faxhistorik.
Plan-adgang: Gratis = ingen adgang. Basis og højere = historik via include=history.
curl "https://firmaapi.dk/api/v1/company/24256790?include=history" -H "Authorization: Bearer cvr_live_..."{
"cvr": "24256790",
"name": "Novo Nordisk A/S",
"history": {
"names": [
{ "name": "Novo Nordisk A/S", "valid_from": "1999-01-01", "valid_to": null }
],
"addresses": [
{ "address": "Novo Allé 1", "zipcode": "2880", "city": "Bagsværd", "municipality_code": 159, "municipality_name": "Gladsaxe", "valid_from": "2002-10-05", "valid_to": null }
],
"statuses": [
{ "status": "Aktiv", "valid_from": "1989-01-01", "valid_to": null }
],
"industries": [
{ "code": "212000", "text": "Fremstilling af farmaceutiske præparater", "valid_from": "2008-01-01", "valid_to": null }
],
"employees": [
{ "employees": "1000+", "valid_from": "2024-01-01", "valid_to": null }
],
"company_forms": [
{ "form": "Aktieselskab", "valid_from": "1989-01-01", "valid_to": null }
],
"emails": [
{ "value": "[email protected]", "valid_from": "2020-01-01", "valid_to": null, "ad_protected": false }
],
"phones": [
{ "value": "44448888", "valid_from": "2020-01-01", "valid_to": null, "ad_protected": false }
],
"faxes": [
{ "value": "44448889", "valid_from": "2010-01-01", "valid_to": "2020-01-01", "ad_protected": false }
],
"websites": [
{ "value": "https://www.novonordisk.com", "valid_from": "2020-01-01", "valid_to": null, "ad_protected": false }
]
}
}{
"error": {
"code": "PLAN_UPGRADE_REQUIRED",
"include": "history",
"message": "Historikdata kræver Basis plan eller højere.",
"current_plan": "gratis",
"required_plan": "basis"
}
}Avanceret søgning
Søg i databasen med multiple filtre. Kombiner navn, by, postnummer, branche, virksomhedsform, status, ansatte, stiftelsesdato og reklamebeskyttelse.
Ydeevne: Advanced search bruger et dedikeret søgeindeks. Endpoint, filtre og response-format er de samme som før, men helt nye company-writes kan være et par minutter om at dukke op i søgningen.
/api/v1/search/advanced| Parameter | Type | Beskrivelse |
|---|---|---|
q | string | Virksomhedsnavn (delvis match). Brug * som wildcard, f.eks. "apple*". Udelad parameteren eller angiv * alene for at returnere alle. |
city | string | By (eksakt match) |
zipcode | string | Postnummer (eksakt match) |
municipality | string | Kommune som kode eller navn, f.eks. "101", "0101" eller "København" (eksakt katalogmatch) |
municipality_list | string | Kommasepareret liste af kommune-koder eller -navne (OR-match). Eksempel: "101,147,461" eller "København,Frederiksberg,Aarhus" |
region | string | Dansk region: "Hovedstaden", "Sjælland", "Syddanmark", "Midtjylland" eller "Nordjylland". Resolveres til underliggende kommunekoder. |
region_list | string | Kommasepareret liste af regioner (OR-match), fx "Hovedstaden,Sjælland" |
industry | string | Branchekode (numerisk, prefix-match, f.eks. "43" finder alle under 43xxxx) eller branchetekst (fritekst-match) |
industry_list | string | Kommasepareret liste af branchekoder (OR-match, prefix). Eksempel: "62,70,73" matcher alle IT, finans og rådgivning. |
industry_exclude | string | Kommasepareret liste af branchekoder der EKSKLUDERES (prefix-match). Brugbar til at filtrere unwanted brancher fra et bredt match. |
company_type | string | Virksomhedsform, f.eks. "Aktieselskab" (delvis match) |
status | string | Status, f.eks. "Aktiv", "Ophørt" m.fl. |
is_dissolved | boolean | true = kun opløste virksomheder; false = kun stadig aktive (ingen dissolved_date) |
employees | string | Antal ansatte — brug "5+" (minimum), "2-10" (interval), "42" (præcis), eller CVR-koder som "ANTAL_10_19" (se gyldige værdier nedenfor) |
employees_min | integer | Minimum antal ansatte (numerisk; rammer indekseret employees_max — virksomhedens øvre band skal være ≥ værdien) |
employees_max | integer | Maksimum antal ansatte (numerisk; rammer indekseret employees_min — virksomhedens nedre band skal være ≤ værdien) |
founded_after | string | Stiftet efter dato (YYYY-MM-DD) |
founded_before | string | Stiftet før dato (YYYY-MM-DD) |
founded_year | string | Præcist stiftelsesår (YYYY) — convenience for founded_after + founded_before samme år |
founded_year_min | string | Tidligste stiftelsesår (YYYY) |
founded_year_max | string | Seneste stiftelsesår (YYYY) |
age_min | integer | Minimum alder i fulde år (regnet fra stiftelsesdato til i dag) |
age_max | integer | Maksimum alder i fulde år |
dissolved_after | string | Opløst efter dato (YYYY-MM-DD). Aktive selskaber matcher aldrig dissolved_*-filtre. |
dissolved_before | string | Opløst før dato (YYYY-MM-DD) |
dissolved_year | string | Præcist opløsningsår (YYYY) |
dissolved_year_min | string | Tidligste opløsningsår (YYYY) |
dissolved_year_max | string | Seneste opløsningsår (YYYY) |
last_report_year_min | string | Tidligste år for seneste indberettede regnskab (YYYY) — brugbar til "stadig aktivt indberettende"-filter |
last_report_year_max | string | Seneste år for seneste indberettede regnskab (YYYY) |
ad_protected | boolean | Reklamebeskyttet (true/false) |
has_financials | boolean | Filtrer på om virksomheden har et kendt regnskabssignal. Kan være true, selv hvis regnskabstallene først hentes hjem ved første finance-opslag. |
revenue_min | integer | Minimum omsætning i seneste regnskabsår |
revenue_max | integer | Maksimum omsætning i seneste regnskabsår |
capital_min | integer | Minimum egenkapital eller selskabskapital i seneste regnskabsår |
capital_max | integer | Maksimum egenkapital eller selskabskapital i seneste regnskabsår |
has_phone | boolean | Filtrer på om virksomheden har mindst ét telefonnummer (primært nummer eller et nummer i all_phones) |
has_email | boolean | Filtrer på om virksomheden har mindst én email (primær email eller en email i all_emails) |
has_website | boolean | Filtrer på om virksomheden har en hjemmeside |
updated_after | string | Kun virksomheder med data-ændringer efter denne dato (YYYY-MM-DD eller ISO-timestamp). Bruges til change-detection / CRM-sync. |
updated_before | string | Kun virksomheder uden data-ændringer efter denne dato (YYYY-MM-DD eller ISO-timestamp) |
sort | string | Sortering på formen 'felt:retning'. Felter: revenue, capital, employees, founded, dissolved, last_report_year, name, updated_at. Retning: asc eller desc. Default: updated_at:desc. Cursor-pagination kræver default-sortering. |
limit | integer | Max resultater (1-100, default 20) |
offset | integer | Pagination offset (default 0) |
cursor | string | Cursor-baseret pagination — brug next_cursor fra forrige response. Virker kun med default-sortering (updated_at:desc). |
include_total | boolean | Sæt true for total antal matches (default false for lavere latency) |
Bemærk: Mindst én søgeparameter skal angives.
curl "https://firmaapi.dk/api/v1/search/advanced?city=København&company_type=Anpartsselskab&status=Aktiv&limit=10" \
-H "Authorization: Bearer cvr_live_..."Kommunefilter: municipality accepterer både kommunenavn og kommunekode. municipality=København, municipality=101 og municipality=0101 matcher samme kommune.
curl "https://firmaapi.dk/api/v1/search/advanced?municipality=København&status=Aktiv&limit=10" \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/search/advanced?status=Aktiv&has_phone=true&has_website=true&limit=10" \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/search/advanced?status=Aktiv&has_financials=true&revenue_min=1000000&limit=10" \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/search/advanced?industry=739&employees=2%2B&ad_protected=true&status=Aktiv" \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/search/advanced?city=Aarhus&founded_after=2020-01-01&status=Aktiv" \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/search/advanced?industry=62&city=Aarhus&status=Aktiv&sort=revenue:desc&limit=25" \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/search/advanced?industry=62&employees_min=10&employees_max=50&has_email=true&status=Aktiv&sort=revenue:desc" \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/search/advanced?industry=692&updated_after=2026-04-29&limit=100" \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/search/advanced?region=Hovedstaden&status=Aktiv&sort=founded:asc&limit=20" \
-H "Authorization: Bearer cvr_live_..."Sortering: sort understøtter revenue, capital, employees, founded, dissolved, last_report_year, name og updated_at. NULL-værdier sorteres altid sidst, så fx sort=revenue:desc ikke begraver profitable virksomheder bag selskaber uden regnskab. Cursor-pagination virker kun med default-sortering — brug offset sammen med ikke-default sort.
{
"results": [
{
"cvr": "...",
"name": "...",
...,
"meta": {
"cached_at": "2025-01-15T12:00:00Z",
"source": "FirmaAPI"
}
}
],
"limit": 20,
"offset": 0,
"has_more": true
}{
"results": [ ... ],
"total": 45821,
"limit": 20,
"offset": 0,
"has_more": true
}Gyldige statusværdier:
Aktiv, Ophørt, Opløst efter konkurs, Opløst efter fusion, Opløst efter spaltning, Opløst efter erklæring, Tvangsopløst, Under konkurs, Under tvangsopløsning, Under rekonstruktion, Slettet
Employees-filter — understøttede formater:
| Format | Eksempel | Betydning |
|---|---|---|
| Minimum | 5+ | 5 eller flere ansatte |
| Interval | 2-10 | Mellem 2 og 10 ansatte |
| Præcis | 42 | Præcis 42 ansatte |
| CVR-kode | ANTAL_10_19 | CVR-interval 10-19 |
CVR-koder: ANTAL_1_1, ANTAL_2_4, ANTAL_5_9, ANTAL_10_19, ANTAL_20_49, ANTAL_50_99, ANTAL_100_199, ANTAL_200_499, ANTAL_500_999, ANTAL_1000_PLUS. INTERVAL-format accepteres også (f.eks. INTERVAL_10_19).
Finansfiltre: revenue_min, revenue_max, capital_min og capital_max måles altid på seneste regnskabsår i søgeindekset.
Hændelser
Hent virksomhedens livscyklus, fusioner, spaltninger og en afledt hændelsestidslinje via include=events på company-endpointet.
Plan-adgang: Gratis/Basis = ingen adgang. Standard+ og højere = fuld adgang. Webhook-events (company.merged, company.demerged) kræver Professionel+.
Eksempel
curl "https://firmaapi.dk/api/v1/company/24256790?include=events" \
-H "X-API-Key: cvr_live_..."{
"lifecycle": [
{ "valid_from": "1923-02-01", "valid_to": null }
],
"mergers": [
{ "type": "merger", "effective_date": "1990-01-01" },
{ "type": "demerger", "effective_date": "2000-11-07" }
],
"events": [
{ "type": "founded", "occurred_at": "1923-02-01", "description": "Virksomheden stiftet" },
{ "type": "merged", "occurred_at": "1990-01-01", "description": "Fusion registreret" },
{ "type": "demerged", "occurred_at": "2000-11-07", "description": "Spaltning registreret" }
]
}| Parameter | Type | Beskrivelse |
|---|---|---|
lifecycle[] | array | Livsperioder (valid_from, valid_to). Flere perioder = virksomheden har været ophørt og genoptaget. |
mergers[] | array | Fusioner (type=merger) og spaltninger (type=demerger) med dato for registrering. |
events[] | array | Afledt tidslinje: founded, dissolved, reopened, merged, demerged med dato og beskrivelse. |
Produktionsenheder
Hent virksomhedens P-enheder via include=production_units på company-endpointet. Brug kun det separate P-nummer-endpoint, når du allerede kender den konkrete enhed.
Plan-adgang: Gratis/Basis = ingen adgang. Standard = P-enheder med p_number og name. Professionel/Enterprise = udvidede P-enhedsfelter som adresse, branche og ansatte.
curl "https://firmaapi.dk/api/v1/company/24256790?include=production_units" -H "Authorization: Bearer cvr_live_..."{
"cvr": "24256790",
"name": "Novo Nordisk A/S",
"production_unit_count": 3,
"production_units": [
{
"p_number": "1234567890",
"name": "Novo Nordisk - Bagsværd",
"address": "Novo Allé 1",
"zipcode": "2880",
"city": "Bagsværd",
"industry_code": "212000",
"industry_text": "Fremstilling af farmaceutiske præparater",
"employees": "1000+"
}
]
}{
"error": {
"code": "PLAN_UPGRADE_REQUIRED",
"include": "production_units",
"message": "Produktionsenheder kræver Standard plan eller højere.",
"current_plan": "gratis",
"required_plan": "standard"
}
}Netto-vækst (analytics)
Aggregér stiftelser og opløsninger pr. tidsbucket og se nettoudviklingen for et segment. Bruger samme filtre som /search/advanced (industry, municipality, city, employees osv.). Status-filteret ignoreres bevidst, fordi opløsningstællingen pr. definition er 0 for status=Aktiv. Mindst ét filter er påkrævet.
/api/v1/search/net-growth| Parameter | Type | Beskrivelse |
|---|---|---|
granularity | string | month | quarter | year (default: year) |
from | string | Tidligste dato (YYYY-MM-DD) |
to | string | Seneste dato (YYYY-MM-DD) |
industry | string | Branchekode, prefix-match (fx 62 = IT-tjenester) |
municipality | string | Kommune-navn eller kode |
city | string | By |
employees | string | Fx 5+, 2-10, 42 eller ANTAL_10_19 |
curl "https://firmaapi.dk/api/v1/search/net-growth?industry=62&granularity=year&from=2020-01-01" -H "Authorization: Bearer cvr_live_..."{
"granularity": "year",
"from": "2020-01-01",
"to": null,
"bucket_count": 6,
"truncated": false,
"totals": {
"founded": 12450,
"dissolved": 6810,
"net_growth": 5640
},
"results": [
{ "bucket": "2020", "founded": 1820, "dissolved": 980, "net_growth": 840 },
{ "bucket": "2021", "founded": 2140, "dissolved": 1050, "net_growth": 1090 },
{ "bucket": "2022", "founded": 2380, "dissolved": 1190, "net_growth": 1190 },
{ "bucket": "2023", "founded": 2210, "dissolved": 1240, "net_growth": 970 },
{ "bucket": "2024", "founded": 2090, "dissolved": 1310, "net_growth": 780 },
{ "bucket": "2025", "founded": 1810, "dissolved": 1040, "net_growth": 770 }
]
}Enkelt P-enhed opslag
/api/v1/production-unit/{p_number}| Parameter | Type | Beskrivelse |
|---|---|---|
p_number* | string | 10-cifret P-nummer (i URL path) |
{
"p_number": "1234567890",
"cvr": "24256790",
"name": "Novo Nordisk - Bagsværd",
"address": "Novo Allé 1",
"zipcode": "2880",
"city": "Bagsværd",
"industry_code": "212000",
"industry_text": "Fremstilling af farmaceutiske præparater",
"employees": "42",
"meta": { ... }
}Referencekataloger
Industrikatalog
Brug /api/v1/industries til kundesync, når du vil spejle hele branchetræet i dit CRM, ERP eller lead-system uden først at lave tunge virksomhed-opslag.
Endpointet returnerer hele hierarkiet, ikke kun de laveste koder. Hver post er en node i træet med både parent_code og child_count, så du kan synkronisere hele strukturens relationer.
Labels på ikke-leaf noder er afledte opsummeringer af de underliggende brancher i snapshotet. De skal derfor læses som sync-labels og ikke som officielle DB07-overskrifter.
Snapshot freshness: meta.refreshed_at fortæller, hvornår snapshot blev opdateret. Snapshotet refreshes periodisk, så tællinger vil ofte være omkring et døgn gamle. Brug derfor altid tidsstemplet som den faktiske friskhedsindikator.
curl "https://firmaapi.dk/api/v1/industries" -H "Authorization: Bearer cvr_live_..."{
"items": [
{
"code": "43",
"display_code": "43",
"label": "Forberedende byggepladsarbejder, Nedrivning, Testboring og prøvetagning m.fl.",
"level": "division",
"parent_code": null,
"company_count": 1234,
"primary_company_count": 1020,
"secondary_company_count": 214,
"child_count": 1
},
{
"code": "431",
"display_code": "43.1",
"label": "Forberedende byggepladsarbejder, Nedrivning, Testboring og prøvetagning m.fl.",
"level": "group",
"parent_code": "43",
"company_count": 420,
"primary_company_count": 390,
"secondary_company_count": 30,
"child_count": 3
}
],
"meta": {
"total": 123,
"refreshed_at": "2026-03-28T10:00:00.000Z"
}
}Kommunekatalog
Brug /api/v1/municipalities til customer sync / reference use, når du vil spejle de kommunekoder og navne, som FirmaAPI eksponerer, i dit eget system uden tunge company-opslag først.
Kataloget er et lille statisk reference-sæt. Hent det én gang, gem mappingen mellem numeric_code og municipality_code lokalt, og brug derefter kommunenavnet som displayværdi i dit CRM, ERP eller lead-system.
Company-payloads inkluderer også municipality_name ved siden af municipality_code som et convenience-felt, så du kan vise adresser uden ekstra opslag.
Bemærk: Kommunekataloget er ikke et snapshot-lag med freshness claims eller cron-opdateringer. Det er en kanonisk reference-liste over de kommunekoder og navne, FirmaAPI eksponerer.
curl "https://firmaapi.dk/api/v1/municipalities" -H "Authorization: Bearer cvr_live_..."{
"items": [
{ "code": "0101", "numeric_code": 101, "name": "København" },
{ "code": "0147", "numeric_code": 147, "name": "Frederiksberg" }
],
"meta": {
"total": 99
}
}Regionskatalog
Brug /api/v1/regions til at slå de 5 danske regioner op med fuld kommune-medlemskab. Kataloget bruges som lookup for region- og region_list-filtrene på /api/v1/search/advanced.
Kilde: Danmarks Statistik / regionsinddelingen. Bornholm og Christiansø-enklaven hører under Region Hovedstaden.
Bemærk: Regionskataloget er statisk reference-data uden freshness-claims. Hent én gang og gem lokalt.
curl "https://firmaapi.dk/api/v1/regions" -H "Authorization: Bearer cvr_live_..."{
"items": [
{
"name": "Hovedstaden",
"full_name": "Region Hovedstaden",
"municipality_count": 30,
"municipalities": [
{ "numeric_code": 101, "name": "København" },
{ "numeric_code": 147, "name": "Frederiksberg" }
]
}
],
"meta": {
"total": 5,
"total_municipalities": 99,
"source": "Danmarks Statistik / regionsinddelingen (5 regioner)"
}
}Teknisk reference
Response-format
Alle responses returneres som JSON med UTF-8 encoding. Succesfulde kald returnerer data direkte, mens fejl returnerer et standardiseret fejlobjekt.
Virksomhedsobjekt
Felter i et standard virksomhedsresponse:
| Felt | Type | Beskrivelse |
|---|---|---|
| cvr | string | 8-cifret CVR-nummer |
| name | string | Officielt virksomhedsnavn |
| address | string | Vejnavn og husnummer |
| zipcode | string | Postnummer |
| city | string | By |
| municipality_code | integer|null | Kommunekode til brug med kommunekataloget |
| municipality_name | string|null | Kommunenavn som convenience field |
| status | string | Aktiv, Ophørt, Under konkurs m.fl. |
| company_type | string|null | Virksomhedsform (ApS, A/S, IVS m.fl.) |
| industry_code | string|null | 6-cifret branchekode |
| industry_text | string|null | Branchebeskrivelse |
| founded | string|null | Stiftelsesdato (YYYY-MM-DD) |
| employees | string|null | Antal ansatte – eksakt tal som string eller normaliseret interval som 10-19 / 1000+ |
| phone | string|null | Telefonnummer |
| string|null | ||
| website | string|null | Hjemmeside |
| ad_protected | boolean | Om virksomheden er reklamebeskyttet |
| secondary_industries | array | Sekundære brancher med code og text |
| capital | number|null | Selskabskapital (i mindste valutaenhed) |
| capital_currency | string|null | Valuta for kapital (typisk DKK) |
| signing_rule | string|null | Tegningsregel |
| is_listed | boolean|null | Børsnoteret virksomhed |
| audit_opted_out | boolean|null | Revision fravalgt |
| production_unit_count | number | Antal produktionsenheder |
| purpose | string|null | Virksomhedens formål |
| secondary_names | string[] | Aktive binavne |
| fiscal_year_end | string|null | Regnskabsårsslut (MM-DD) |
| accounting_class | string|null | Regnskabsklasse (A/B/C/D) |
| annual_employees | string|null | Årlig beskæftigelse (FTE-interval) |
Ekstra felter Standard+
Disse felter er kun tilgængelige med Standard plan eller højere:
| Felt | Type | Beskrivelse |
|---|---|---|
| employer_registration_date | string|null | Første arbejdsgiverregistrering (YYYY-MM-DD) |
| vat_registration_date | string|null | Første momsregistrering (YYYY-MM-DD) |
| import_registered | boolean | Aktiv importregistrering |
| export_registered | boolean | Aktiv eksportregistrering |
| excise_registrations | string[] | Aktive punktafgiftsregistreringer |
| first_fiscal_period_end | string|null | Første regnskabsperiode slutdato |
| first_fiscal_period_start | string|null | Første regnskabsperiode startdato |
| address_full | object|null | Fuld struktureret adresse med floor, door, po_box, country_code m.fl. |
| articles_last_amended | string|null | Seneste vedtægtsændring (ISO-dato) |
| dissolution_threat | object|null | Seneste opløsningstrussel ({date, reason}) |
| subject_to_aml_law | boolean|null | Omfattet af lov om hvidvask og terrorfinansiering |
| has_share_classes | boolean|null | Har virksomheden flere aktieklasser |
| archive_registration_number | string|null | Arkivregistreringsnummer |
| name_identity | string|null | Navn-identitet (NAVN_IDENTITET fra ERST) |
| has_pseudo_cvr | boolean|null | Pseudo-CVR-flag (har virksomheden et pseudo-CVR i CVR-registeret) |
Ekstra felter Professionel+
Disse felter er kun tilgængelige med Professionel plan eller højere:
| Felt | Type | Beskrivelse |
|---|---|---|
| latitude | number|null | Breddegrad (WGS84) |
| longitude | number|null | Længdegrad (WGS84) |
| auditors | array | Revisorer med name, title, participant_cvr og joined_date |
| all_emails | string[] | Alle email-adresser |
| all_phones | string[] | Alle telefonnumre |
| fax | string|null | Faxnummer |
| history.faxes | array | Telefax-historik via include=history |
| audit_form | string|null | Revisionsform |
| fiscal_year_start | string|null | Regnskabsår start (f.eks. "01-01") |
| secondary_industry_history | array | Historik over bibrancher inkl. inaktive |
| executives | array | Direktion/direktører (DIREKTION, DIREKTØR, ADM. DIR.). Basis+; Professionel/Enterprise inkluderer person_id når personen er matchet. |
| board_members | array | Bestyrelse + formænd inkl. valgform (VALGFORM). Basis+; Professionel/Enterprise inkluderer person_id når personen er matchet. |
| alternates | array | Suppleanter — alternate_for_enhedsnummer peger på primær |
| liquidators | array | Likvidatorer (LIKVIDATOR-funktion i LEDELSESORGAN) |
| sustainability_auditors | array | Bæredygtighedsrevisorer (BÆREDYGTIGHEDSREVISION) |
| unlimited_liability_partners | array | Fuldt ansvarlige deltagere (I/S, K/S, enkeltmandsvirksomhed) |
| founders | array | Stiftere (STIFTERE-organisation) |
| register_entries | array | Ejerregister-poster med ejerandel og stemmeret (Basis+, via include=history; person_id kun Professionel+) |
| auditor_offices | array | Revisorers kontorsteder (kontorsted-adresse) |
Reklamebeskyttelse
Virksomheder kan være reklamebeskyttede i CVR. Feltet ad_protected markerer, at kontaktoplysninger ikke må bruges til direkte markedsføring, herunder uopfordrede henvendelser via email, telefon, SMS eller fysisk post. Alle FirmaAPI-kald er autentificerede, men det er stadig dit ansvar at overholde reklamebeskyttelsen.
Meta-objekt
Alle responses inkluderer et meta objekt med information om data-kilden:
"meta": {
"cached_at": "2025-01-15T12:00:00.000Z",
"data_age_seconds": 3600,
"source": "FirmaAPI",
"provenance": {
"provider": "erst",
"source": "postgres",
"resolution_rule": "db_row"
}
}Paginering
Endpoints der returnerer lister understøtter paginering med limit og offset parametre.
Sådan paginerer du
curl "https://firmaapi.dk/api/v1/search/advanced?city=Aarhus&limit=20&offset=0" \
-H "Authorization: Bearer cvr_live_..."curl "https://firmaapi.dk/api/v1/search/advanced?city=Aarhus&limit=20&offset=20" \
-H "Authorization: Bearer cvr_live_..."Brug has_more feltet i response til at afgøre om der er flere sider. Hvis du også skal vise samlet antal, tilføj include_total=true. Hver side tæller som ét opslag.
Rate limits
API'et har rate limits per sekund og månedlige kvoter afhængig af din plan.
| Plan | Requests/sek | Opslag/måned |
|---|---|---|
| Gratis | 2 | 1.000 |
| Basis | 5 | 50.000 |
| Standard | 10 | 150.000 |
| Professionel | 20 | 500.000 |
| Enterprise | Individuelt | Efter aftale |
Rate limit headers
Alle responses inkluderer rate limit headers, så du kan håndtere limits proaktivt:
X-RateLimit-Limit: 2 # Max requests per sekund for din plan
X-RateLimit-Remaining: 1 # Resterende requests i dette sekund
X-RateLimit-Reset: 1710000 # Unix timestamp for næste resetNår du rammer grænsen
Hvis du overskrider din rate limit, returnerer API'et HTTP 429 Too Many Requests med en Retry-After header der angiver hvor mange sekunder du skal vente.
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Slow down.",
"retry_after": 1
}
}Best practices
- ✓Spred dine requests jævnt ud – undgå at sende mange på én gang i bursts
- ✓Brug
X-RateLimit-Remainingheaderen til at bremse inden du rammer grænsen - ✓Ved
429– vent det antal sekunderRetry-Afterheaderen angiver - ✓Implementer exponential backoff: fordobl ventetiden ved gentagne
429-svar - ✓Brug batch-endpointet (
/api/v1/company/batch) i stedet for mange enkelte opslag
Eksempel med retry-logik (JavaScript):
async function fetchCompany(cvr, apiKey, retries = 3) {
for (let i = 0; i < retries; i++) {
const res = await fetch(`https://firmaapi.dk/api/v1/company/${cvr}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (res.ok) return res.json();
if (res.status === 429) {
const wait = parseInt(res.headers.get("Retry-After") || "1");
await new Promise((r) => setTimeout(r, wait * 1000 * (i + 1)));
continue;
}
throw new Error(`API error: ${res.status}`);
}
throw new Error("Max retries exceeded");
}Fejlhåndtering
Alle fejl returneres med en standardiseret JSON-struktur og passende HTTP statuskode.
{
"error": {
"code": "ERROR_CODE",
"message": "Menneskelæselig beskrivelse af fejlen"
}
}| HTTP | Kode | Beskrivelse | Løsning |
|---|---|---|---|
| 400 | INVALID_CVR | Ugyldigt CVR-format | Sørg for at CVR er præcis 8 cifre |
| 400 | INVALID_P_NUMBER | Ugyldigt P-nummer format | P-nummer skal være præcis 10 cifre |
| 400 | INVALID_QUERY | Ugyldig eller manglende søgeparameter | Angiv 'q' med min. 2 tegn (søgning) eller mindst én filter (avanceret søgning) |
| 401 | MISSING_API_KEY | Ingen API-nøgle angivet | Tilføj Authorization: Bearer header |
| 401 | INVALID_API_KEY | Ugyldig eller deaktiveret nøgle | Tjek at nøglen er korrekt og aktiv |
| 403 | FORBIDDEN | Adgang nægtet | Din IP eller konto er blokeret – kontakt support |
| 403 | PLAN_UPGRADE_REQUIRED | Funktionen kræver en højere plan | Opgrader din plan – se fejlens 'required_plan' felt |
| 403 | PLAN_NOT_SUPPORTED | Endpoint kræver højere plan | Se endpoint-dokumentationen for plan-krav |
| 404 | NOT_FOUND | Virksomhed ikke fundet | Tjek at CVR-nummeret eksisterer |
| 429 | RATE_LIMIT_EXCEEDED | For mange requests per sekund | Vent og prøv igen (se Retry-After) |
| 429 | MONTHLY_LIMIT_EXCEEDED | Månedlig kvote opbrugt | Opgrader din plan eller vent til næste måned. Response inkluderer 'used' og 'limit' felter. |
| 500 | SERVER_ERROR | Intern serverfejl | Prøv igen om lidt eller kontakt support |
| 400 | INVALID_REQUEST | Ugyldigt request format | Tjek at request body er gyldig JSON |
| 400 | TOO_MANY | For mange CVR-numre i batch-opslag | Reducer antallet – max afhænger af plan (Basis 100, Standard 200, Professionel 500) |
| 400 | INVALID_URL | Ugyldig webhook URL | URL skal bruge HTTPS og må ikke pege på localhost eller private IP'er |
| 403 | PLAN_NOT_ALLOWED | Webhooks er ikke tilgængelige på din plan | Opgrader til Standard eller højere for webhook-adgang |
| 403 | EVENT_NOT_ALLOWED | Hændelsestype ikke tilgængelig på din plan | Opgrader til Professionel for alle hændelsestyper |
| 403 | FILTERS_NOT_ALLOWED | Webhook-filtre kræver Professionel plan | Opgrader til Professionel for filter-support |
| 502 | ERST_ERROR | Fejl ved opslag hos datakilden | Prøv igen om lidt |
| 503 | MAINTENANCE | API er under vedligeholdelse | Prøv igen senere |
AI-integration (MCP)
Brug danske virksomhedsdata direkte i AI-assistenter som Claude Desktop, Claude Code, Cursor og andre MCP-kompatible klienter. Vores MCP-server giver din AI mulighed for at slå virksomheder op, søge og analysere regnskaber.
Lad din AI sætte serveren op
Har du en AI-klient med filadgang (f.eks. Claude Code, Claude Desktop med værktøjer, Cursor), kan du copy-paste prompten nedenfor og få den til at installere pakken og tilføje konfigurationen for dig. Prompten indeholder ikke din API-nøgle — AI'en bruger placeholderen DIN_API_NØGLE_HER, og fortæller dig til sidst præcis hvilken fil (inkl. sti) og hvilken linje du selv skal indsætte din nøgle i.
Installer og konfigurer firmaapi-mcp-server for mig.
1. Installer pakken globalt: npm install -g firmaapi-mcp-server
2. Find ud af hvilken AI-klient jeg bruger (Claude Desktop, Claude Code,
Cursor eller lignende) og mit styresystem.
3. Opret eller rediger den korrekte konfigurationsfil, og tilføj firmaapi
som MCP-server med kommandoen "firmaapi-mcp-server" og env-variablen
FIRMAAPI_KEY sat til strengen "DIN_API_NØGLE_HER" (lad placeholderen
stå — jeg udskifter den selv bagefter).
4. Fortæl mig til sidst:
- Den fulde sti til den fil du redigerede.
- Hvilken linje i filen jeg skal åbne og udskifte "DIN_API_NØGLE_HER"
med min rigtige nøgle.
- Hvordan jeg genstarter klienten så serveren bliver indlæst.
Skriv IKKE min rigtige API-nøgle nogen steder — brug kun placeholderen.Foretrækker du at sætte det op selv? Følg trinnene herunder.
1. Installer pakken
Kør med din foretrukne package manager:
npm install -g firmaapi-mcp-server # npm
bun add -g firmaapi-mcp-server # bun
pnpm add -g firmaapi-mcp-server # pnpm2. Konfigurer din AI-klient
Claude Desktop
Tilføj til din claude_desktop_config.json:
{
"mcpServers": {
"firmaapi": {
"command": "firmaapi-mcp-server",
"env": {
"FIRMAAPI_KEY": "cvr_live_din_nøgle_her"
}
}
}
}Claude Code
claude mcp add firmaapi -e FIRMAAPI_KEY=cvr_live_... -- firmaapi-mcp-serverCursor
Tilføj til .cursor/mcp.json i dit projekt:
{
"mcpServers": {
"firmaapi": {
"command": "firmaapi-mcp-server",
"env": {
"FIRMAAPI_KEY": "cvr_live_din_nøgle_her"
}
}
}
}3. Brug det
Bed bare din AI om at slå virksomheder op:
"Slå Novo Nordisk op og vis seneste regnskab"
"Find alle IT-virksomheder i Aarhus med over 50 ansatte"
"Hvem ejer CVR 34824770?"
"Søg efter Lars Larsen og vis hans virksomhedstilknytninger"
Tilgængelige MCP-tools (33 stk.)
Opslag og søgning
| Parameter | Type | Beskrivelse |
|---|---|---|
firmaapi_lookup_company | Gratis | Slå virksomhed op via CVR-nummer med valgfrie ekstrablokke (finance, history, events, production_units, relations). |
firmaapi_search_companies | Gratis | Hurtig navnesøgning med fuzzy match. |
firmaapi_advanced_search | Gratis | Avanceret søgning med 30+ filtre (by, branche, omsætning, ansatte, region, alder, regnskabsklasse m.m.). |
firmaapi_batch_lookup | Basis | Slå op til 500 CVR-numre op i én forespørgsel. |
firmaapi_validate_cvr | Gratis | Modulus-11-tjek og valgfri verificering af om CVR'et faktisk findes. |
Personer og relationer
| Parameter | Type | Beskrivelse |
|---|---|---|
firmaapi_get_person | Pro | Hent personprofil med alle virksomhedstilknytninger via UUID. |
firmaapi_search_persons | Pro | Søg personprofiler på navn, alias eller enhedsnummer. |
firmaapi_my_portfolio | Pro | Få en persons fulde portefølje af aktive og historiske roller. |
firmaapi_kyc_screen | Pro | Flag for stråmandsmønster, kortvarige roller og høj rolle-omsætning. |
Analyse og aggregering
| Parameter | Type | Beskrivelse |
|---|---|---|
firmaapi_aggregate | Gratis | Aggreger virksomheder pr. branche, kommune, selskabsform, stiftelsesår m.fl. |
firmaapi_timeseries | Gratis | Stiftelser og opløsninger over tid, opdelt pr. dag, uge, måned eller år. |
firmaapi_net_growth | Gratis | Nettovækst (stiftelser minus opløsninger) pr. tidsperiode. |
firmaapi_industry_landscape | Standard | De største virksomheder i en branche eller region, rangordnet. |
firmaapi_geographical_distribution | Gratis | Geografisk fordeling af virksomheder pr. kommune eller region. |
Finansiel analyse
| Parameter | Type | Beskrivelse |
|---|---|---|
firmaapi_financial_health_score | Standard | Samlet sundhedsscore 0-100 baseret på profitmargin, soliditet, likviditet, vækst og gearing. |
firmaapi_growth_trajectory | Standard | Flerårig vækstanalyse: stabil, volatil, fladt eller faldende. |
firmaapi_dividend_history | Standard | Samlet udbetalt udbytte over alle år og udbyttegrad pr. år. |
firmaapi_industry_benchmark | Standard | Sammenlign en virksomhed med konkurrenter i samme branche og region. |
Risiko og compliance
| Parameter | Type | Beskrivelse |
|---|---|---|
firmaapi_due_diligence | Standard | Risikoflag med samlet vurdering: lav, mellem, forhøjet eller høj. |
firmaapi_change_detector | Gratis | Find virksomheder med ændringer (status, navn, adresse, ledelse) siden en given dato. |
firmaapi_holding_structure_detector | Standard | Klassificér en virksomhed som driftselskab, holding, personligt holding eller dvale. |
firmaapi_dependency_analysis | Pro | Vis hvem der rammes økonomisk hvis en virksomhed går konkurs. |
Ejerskab og netværk
| Parameter | Type | Beskrivelse |
|---|---|---|
firmaapi_ownership_tree | Pro | Ejerskabskæde op og ned, op til fire niveauer. |
firmaapi_board_overlap | Pro | Find sammenfaldende ledelsesmedlemmer mellem to selskaber. |
firmaapi_find_target_companies | Basis | Leadgenerering med fornuftige defaults: aktive, kontaktbare, sorteret efter omsætning. |
firmaapi_lookalike_search | Basis | Find virksomheder der minder om en given CVR. |
firmaapi_compare_companies | Standard | Side-om-side sammenligning af 2-4 selskaber. |
Reference og kataloger
| Parameter | Type | Beskrivelse |
|---|---|---|
firmaapi_industries | Gratis | Branchekoder (DB07) i hierarki med antal virksomheder pr. kode. |
firmaapi_municipalities | Gratis | Liste over danske kommuner med koder. |
firmaapi_regions | Gratis | De fem danske regioner med tilhørende kommuner. |
firmaapi_production_unit | Standard | Slå produktionsenhed op via P-nummer. |
firmaapi_explain_field | Gratis | Forklaring af 60+ CVR- og ERST-termer (statusværdier, roller, attributter). |
firmaapi_explain_query | Gratis | Tæl matches uden at hente data. Sanity-check søgning før tunge kald. |
Pakken på npm: npmjs.com/package/firmaapi-mcp-server — MCP-serveren bruger din eksisterende API-nøgle og tæller mod din plans kvote.
Webhooks
Modtag automatisk besked når virksomhedsdata ændrer sig. Webhooks sender HTTP POST requests til dit endpoint med signerede payloads.
Tilgængelig fra Standard-planen. Se priser for detaljer.
Webhook-mønstre: enkelt-match og batch
Webhooks er event-drevne. Du kan bruge samme endpoint til både enkelt-match og batch-flow afhængigt af din egen routinglogik.
- Enkelt-match: filtrér i din modtager på
data.company.cvrog send videre til en dedikeret job-kø for det enkelte CVR. - Batch: saml events i vinduer (fx 1-5 min), dedupliker på CVR og kør batch-opslag mod
/api/v1/company/batch.
curl -X POST https://firmaapi.dk/api/v1/company/batch -H "Authorization: Bearer cvr_live_..." -H "Content-Type: application/json" -d '{"cvr_numbers": ["24256790", "19625095", "10940834"]}'Event-typer
| Event | Beskrivelse | Plan |
|---|---|---|
| company.status.changed | Virksomhedens status er ændret | Standard+ |
| company.bankruptcy | Konkurs eller tvangsopløsning | Standard+ |
| company.created | Ny virksomhed registreret | Professionel |
| company.name.changed | Navneændring | Professionel |
| company.address.changed | Adresseændring | Professionel |
| company.participant.changed | Ejere/ledelse ændret | Professionel |
| company.financials.published | Nyt årsregnskab offentliggjort | Professionel |
| company.industry.changed | Branchekode ændret | Professionel |
| company.type.changed | Virksomhedsform ændret | Professionel |
| company.capital.changed | Selskabskapital ændret | Professionel |
| person.profile.created | Ny erhvervsperson registreret | Professionel |
| person.affiliation.added | Person fik ny rolle i en virksomhed | Professionel |
| person.affiliation.ended | Person udtrådte af en virksomhed | Professionel |
| person.role.changed | Persons rolletitel ændret (fx direktør → bestyrelsesmedlem) | Professionel |
| person.ownership.changed | Ejer- eller stemmeandel ændret for person | Professionel |
| person.profile.merged | To personprofiler konsolideret (planlagt — abonnement muligt, men emitteres ikke endnu) | Professionel |
Opret webhook
/api/v1/webhookscurl -X POST https://firmaapi.dk/api/v1/webhooks \
-H "Authorization: Bearer din_api_nøgle" \
-H "Content-Type: application/json" \
-d '{
"url": "https://din-server.dk/webhook",
"event_types": ["company.status.changed", "company.bankruptcy"],
"description": "Status-overvågning"
}'Webhook-secret returneres kun ved oprettelse. Gem det sikkert – det bruges til at verificere signaturer.
Payload-format
{
"id": "evt_abc123",
"type": "company.status.changed",
"created_at": "2026-03-15T10:30:00.000Z",
"data": {
"company": {
"cvr": "12345678",
"name": "Firma A/S",
"status": "Under konkurs",
...
},
"changes": {
"status": { "old": "Aktiv", "new": "Under konkurs" }
}
}
}Person-events (person.*) følger samme konvolut, men data indeholder en person-reference, det CVR hvor rollen er forbundet, samt affiliation-detaljer. For role.changed og ownership.changed medsendes også et changes-objekt med old/new-deltaer.
{
"id": "evt_xyz789",
"type": "person.role.changed",
"created_at": "2026-04-24T08:15:00.000Z",
"data": {
"person": {
"id": "7d64b6da-8d23-4a1a-8ca4-4e4f1d0c0a11",
"slug": "jens-hansen",
"canonical_name": "Jens Hansen"
},
"cvr": "12345678",
"company_name": "Acme ApS",
"affiliation": {
"source_type": "ownership_history",
"role_title": "Bestyrelsesmedlem",
"joined_date": "2024-01-01",
"left_date": null,
"ownership_share": 60,
"voting_share": 60,
"confidence": 0.95
},
"changes": {
"role_title": { "old": "Direktør", "new": "Bestyrelsesmedlem" },
"ownership_share": { "old": 40, "new": 60 },
"voting_share": { "old": 40, "new": 60 }
}
}
}Signaturverifikation
Alle webhook-requests signeres med HMAC-SHA256. Verificér signaturen for at sikre at requesten kommer fra FirmaAPI.
Headers: X-FirmaAPI-Signature, X-FirmaAPI-Timestamp, X-FirmaAPI-Event, X-FirmaAPI-Delivery
const crypto = require("crypto");
function verifySignature(secret, timestamp, body, signature) {
const payload = timestamp + "." + body;
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// I din webhook handler:
app.post("/webhook", (req, res) => {
const sig = req.headers["x-firmaapi-signature"];
const ts = req.headers["x-firmaapi-timestamp"];
const body = JSON.stringify(req.body);
if (!verifySignature(WEBHOOK_SECRET, ts, body, sig)) {
return res.status(401).send("Invalid signature");
}
// Håndtér event
console.log(req.body.type, req.body.data);
res.status(200).send("OK");
});import hmac, hashlib
def verify_signature(secret, timestamp, body, signature):
payload = f"{timestamp}.{body}"
expected = hmac.new(
secret.encode(), payload.encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# I din webhook handler (Flask):
@app.route("/webhook", methods=["POST"])
def webhook():
sig = request.headers.get("X-FirmaAPI-Signature")
ts = request.headers.get("X-FirmaAPI-Timestamp")
body = request.get_data(as_text=True)
if not verify_signature(WEBHOOK_SECRET, ts, body, sig):
return "Invalid signature", 401
event = request.get_json()
print(event["type"], event["data"])
return "OK", 200Retry-politik
Hvis dit endpoint returnerer en ikke-2xx status, forsøger vi igen med eksponentiel backoff:
| Forsøg | Ventetid |
|---|---|
| 1. retry | 1 minut |
| 2. retry | 5 minutter |
| 3. retry | 30 minutter |
| 4. retry | 2 timer |
Efter 5 consecutive failures deaktiveres webhooket automatisk. Du kan genaktivere det via API.
Filtre (Professionel)
Med Professionel-planen kan du filtrere events baseret på branchekode, kommunekode eller specifikke person-IDs (person_ids accepterer både UUID og slug, op til 500 entries).
{
"url": "https://din-server.dk/webhook",
"event_types": ["company.status.changed"],
"filters": {
"industry_codes": ["620100", "620200"],
"municipality_codes": [101, 147]
}
}{
"url": "https://din-server.dk/webhook",
"event_types": [
"person.affiliation.added",
"person.affiliation.ended",
"person.role.changed",
"person.ownership.changed"
],
"filters": {
"person_ids": [
"7d64b6da-8d23-4a1a-8ca4-4e4f1d0c0a11",
"jens-hansen"
]
},
"description": "KYC-monitor på udvalgte UBO'er"
}API-endpoints
| Metode | Endpoint | Beskrivelse |
|---|---|---|
| POST | /api/v1/webhooks | Opret webhook |
| GET | /api/v1/webhooks | List webhooks |
| GET | /api/v1/webhooks/:id | Webhook detaljer |
| PATCH | /api/v1/webhooks/:id | Opdater webhook |
| DELETE | /api/v1/webhooks/:id | Slet webhook |
| GET | /api/v1/webhooks/:id/deliveries | Delivery log |
| POST | /api/v1/webhooks/:id/test | Send test-event |
Kodeeksempler
Komplet eksempler i populære sprog. Kopier og tilpas til dit projekt.
JavaScript / Node.js
// Enkelt CVR-opslag
const response = await fetch("https://firmaapi.dk/api/v1/company/24256790", {
headers: { Authorization: "Bearer cvr_live_din_nøgle" }
});
const company = await response.json();
console.log(company.name); // "Novo Nordisk A/S"
// Søgning
const searchRes = await fetch(
"https://firmaapi.dk/api/v1/company/search?q=novo+nordisk&limit=5",
{ headers: { Authorization: "Bearer cvr_live_din_nøgle" } }
);
const { results } = await searchRes.json();
results.forEach(c => console.log(c.cvr, c.name));
// Batch-opslag
const batchRes = await fetch("https://firmaapi.dk/api/v1/company/batch", {
method: "POST",
headers: {
Authorization: "Bearer cvr_live_din_nøgle",
"Content-Type": "application/json"
},
body: JSON.stringify({ cvr_numbers: ["24256790", "54562519"] })
});
const batch = await batchRes.json();
console.log(batch.results.length, "virksomheder hentet");Python
import requests
API_KEY = "cvr_live_din_nøgle"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
# Enkelt CVR-opslag
r = requests.get(
"https://firmaapi.dk/api/v1/company/24256790",
headers=HEADERS
)
company = r.json()
print(company["name"]) # "Novo Nordisk A/S"
# Søgning
r = requests.get(
"https://firmaapi.dk/api/v1/company/search",
params={"q": "novo nordisk", "limit": 5},
headers=HEADERS
)
for c in r.json()["results"]:
print(c["cvr"], c["name"])
# Batch-opslag
r = requests.post(
"https://firmaapi.dk/api/v1/company/batch",
json={"cvr_numbers": ["24256790", "54562519"]},
headers=HEADERS
)
print(f"{r.json()['total']} virksomheder hentet")PHP
<?php
$apiKey = "cvr_live_din_nøgle";
// Enkelt CVR-opslag
$ch = curl_init("https://firmaapi.dk/api/v1/company/24256790");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ["Authorization: Bearer $apiKey"],
CURLOPT_RETURNTRANSFER => true,
]);
$company = json_decode(curl_exec($ch), true);
echo $company["name"]; // "Novo Nordisk A/S"
// Batch-opslag
$ch = curl_init("https://firmaapi.dk/api/v1/company/batch");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $apiKey",
"Content-Type: application/json"
],
CURLOPT_POSTFIELDS => json_encode([
"cvr_numbers" => ["24256790", "54562519"]
]),
CURLOPT_RETURNTRANSFER => true,
]);
$batch = json_decode(curl_exec($ch), true);
echo count($batch["results"]) . " virksomheder hentet";C# / .NET
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer cvr_live_din_nøgle");
// Enkelt CVR-opslag
var response = await client.GetAsync("https://firmaapi.dk/api/v1/company/24256790");
var json = await response.Content.ReadAsStringAsync();
var company = JsonSerializer.Deserialize<JsonElement>(json);
Console.WriteLine(company.GetProperty("name").GetString());
// Batch-opslag
var batchBody = new StringContent(
JsonSerializer.Serialize(new { cvr_numbers = new[] { "24256790", "54562519" } }),
Encoding.UTF8,
"application/json"
);
var batchRes = await client.PostAsync("https://firmaapi.dk/api/v1/company/batch", batchBody);
var batchJson = await batchRes.Content.ReadAsStringAsync();Go
package main
import (
"encoding/json"
"fmt"
"net/http"
"io"
)
func main() {
req, _ := http.NewRequest("GET",
"https://firmaapi.dk/api/v1/company/24256790", nil)
req.Header.Set("Authorization", "Bearer cvr_live_din_nøgle")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var company map[string]interface{}
json.Unmarshal(body, &company)
fmt.Println(company["name"]) // "Novo Nordisk A/S"
}Gratis API-nøgle på 30 sekunder
1.000 opslag/md. Ingen kreditkort.