Denne guide viser dig, hvordan du integrerer DataCVR API i din applikation trin for trin. Uanset om du bygger en webapplikation, et CRM-system, eller automatiserer din faktureringsproces, finder du her komplette kodeeksempler i JavaScript, Python, PHP og C# - klar til at kopiere og tilpasse.
Forudsætninger
Før du starter, skal du have følgende på plads:
- Gratis konto: Opret en konto på datacvrapi.dk (tager under 1 minut)
- API-nøgle: Find din API-nøgle i dit dashboard under "API-nøgle"
- HTTP-klient: Dit foretrukne programmeringssprog med en HTTP-klient (fetch, requests, cURL, HttpClient)
Den gratis plan giver 25 opslag per dag - nok til at udvikle og teste din integration. Se den fulde API-dokumentation for alle tilgængelige endpoints.
Base URL
API'et er tilgængeligt via to identiske base-URLs:
https://datacvrapi.dk/api/v2(kanonisk, anbefalet)https://api.datacvrapi.dk/api/v2(subdomain-alias, samme backend)
Begge virker med alle endpoints. Eksempel: GET /dk/company/10150817 kan kaldes via enten datacvrapi.dk/api/v2/dk/company/10150817 eller api.datacvrapi.dk/api/v2/dk/company/10150817. Bemærk at /api/v2/-præfikset skal med på begge, også på api.-subdomainet.
Alle kodeeksempler nedenfor bruger den kanoniske URL. Udskift frit med subdomain-aliaset hvis du foretrækker det.
JavaScript / Node.js integration
JavaScript er det mest populære sprog til API-integrationer. Her er en komplet implementering med fejlhåndtering:
Simpelt opslag med fetch
// cvr-client.js - Simpelt CVR-opslag
async function hentVirksomhed(cvrNummer) {
const API_KEY = process.env.DATACVR_API_KEY;
const BASE_URL = 'https://datacvrapi.dk/api/v2';
const response = await fetch(
`${BASE_URL}/dk/company/${cvrNummer}`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
);
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(
`CVR opslag fejlede: ${response.status} - ${error.message || 'Ukendt fejl'}`
);
}
return response.json();
}
// Brug
const virksomhed = await hentVirksomhed('45786943');
console.log(virksomhed.name); // "Branthiq ApS"
console.log(virksomhed.address); // "Ryesgade 3A" (string)
console.log(virksomhed.zipcode); // "2200"
console.log(virksomhed.city); // "København N"
console.log(virksomhed.protected); // true (reklamebeskyttet)
console.log(virksomhed.owners); // [{ name: "Benjamin Falkentoft", role: "DIREKTION", ... }]
Komplet klient med retry og caching
// datacvr-client.js - Komplet klient
class DataCvrClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://datacvrapi.dk/api/v2';
this.cache = new Map();
this.cacheTtl = 3600000; // 1 time i millisekunder
}
async request(endpoint, options = {}) {
// Tjek cache
const cacheKey = endpoint;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTtl) {
return cached.data;
}
// Lav request med retry
let lastError;
for (let attempt = 0; attempt < 3; attempt++) {
try {
const response = await fetch(
`${this.baseUrl}${endpoint}`,
{
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers
}
}
);
if (response.status === 429) {
// Rate limited - vent og prøv igen
const ventetid = Math.pow(2, attempt) * 1000;
await new Promise(r => setTimeout(r, ventetid));
continue;
}
if (!response.ok) {
throw new Error(`API fejl: ${response.status}`);
}
const data = await response.json();
// Gem i cache
this.cache.set(cacheKey, {
data,
timestamp: Date.now()
});
return data;
} catch (error) {
lastError = error;
}
}
throw lastError;
}
// Hent virksomhedsdata
async hentVirksomhed(cvr) {
return this.request(`/dk/company/${cvr}`);
}
// Hent regnskabsdata (Business+)
async hentRegnskab(cvr) {
return this.request(`/dk/company/${cvr}/accounts`);
}
// Hent cached kontakter (Business+)
async hentKontakter(cvr) {
return this.request(`/dk/company/${cvr}/contacts`);
}
}
// Brug
const client = new DataCvrClient('DIN_API_NØGLE');
const data = await client.hentVirksomhed('45786943');
console.log(data.name); // "Branthiq ApS"
Python integration
Python er ideelt til datanalyse, scripting og backend-systemer. Her er en komplet integration med requests-biblioteket:
# datacvr_client.py - Python CVR-klient
import requests
import time
import os
from functools import lru_cache
class DataCvrClient:
def __init__(self, api_key=None):
self.api_key = api_key or os.environ.get("DATACVR_API_KEY")
self.base_url = "https://datacvrapi.dk/api/v2"
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {self.api_key}"
})
def _request(self, endpoint, method="GET", **kwargs):
"""Lav request med retry-logik."""
url = f"{self.base_url}{endpoint}"
for attempt in range(3):
response = self.session.request(method, url, **kwargs)
if response.status_code == 429:
# Rate limited - vent og prøv igen
ventetid = 2 ** attempt
time.sleep(ventetid)
continue
response.raise_for_status()
return response.json()
raise Exception("Maks antal forsøg overskredet")
def hent_virksomhed(self, cvr_nummer):
"""Hent stamdata for en dansk virksomhed."""
return self._request(f"/dk/company/{cvr_nummer}")
def hent_regnskab(self, cvr_nummer):
"""Hent regnskabsdata (kræver Business+)."""
return self._request(f"/dk/company/{cvr_nummer}/accounts")
def hent_kontakter(self, cvr_nummer):
"""Hent cached kontakter (kræver Business+)."""
return self._request(f"/dk/company/{cvr_nummer}/contacts")
def hent_norsk_virksomhed(self, orgnr):
"""Hent data for en norsk virksomhed."""
return self._request(f"/no/company/{orgnr}")
# Brug
if __name__ == "__main__":
client = DataCvrClient("DIN_API_NØGLE")
# Hent virksomhedsdata med owners
virksomhed = client.hent_virksomhed("45786943")
print(f"Navn: {virksomhed['name']}")
print(f"Virksomhedsform: {virksomhed['companydesc']}")
print(f"Branche: {virksomhed['industrydesc']}")
print(f"Reklamebeskyttet: {virksomhed['protected']}")
for owner in virksomhed.get("owners", []):
print(f" Ejer: {owner['name']} - {owner['role']}")
# Hent ændrede virksomheder (changed feed)
aendrede_dk = client._request("/dk/changed/list/company?since=2026-04-02&limit=10")
print(f"DK ændringer: {aendrede_dk['total']}")
aendrede_no = client._request("/no/changed/list/company?since=2026-04-02&limit=10")
print(f"NO ændringer: {aendrede_no['total']}")
# Hent regnskab
regnskaber = client.hent_regnskab("45786943")
seneste = regnskaber[0]
print(f"Omsætning: {seneste['figures']['revenue']} DKK")
PHP integration
PHP bruges stadig bredt i webapplikationer og e-handelsplatforme. Her er en integration med cURL:
<?php
// DataCvrClient.php - PHP CVR-klient
class DataCvrClient {
private string $apiKey;
private string $baseUrl = 'https://datacvrapi.dk/api/v2';
public function __construct(string $apiKey) {
$this->apiKey = $apiKey;
}
private function request(string $endpoint, string $method = 'GET', ?array $body = null): array {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $this->baseUrl . $endpoint,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json',
],
CURLOPT_TIMEOUT => 30,
]);
if ($method === 'POST' && $body) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
// Retry-logik
$maxForsøg = 3;
for ($i = 0; $i < $maxForsøg; $i++) {
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode === 429) {
// Rate limited - vent og prøv igen
sleep(pow(2, $i));
continue;
}
break;
}
curl_close($ch);
if ($httpCode >= 400) {
throw new Exception("API fejl: HTTP {$httpCode}");
}
return json_decode($response, true);
}
public function hentVirksomhed(string $cvr): array {
return $this->request("/dk/company/{$cvr}");
}
public function hentRegnskab(string $cvr): array {
return $this->request("/dk/company/{$cvr}/accounts");
}
public function hentKontakter(string $cvr): array {
return $this->request("/dk/company/{$cvr}/contacts");
}
}
// Brug
$client = new DataCvrClient('DIN_API_NØGLE');
$virksomhed = $client->hentVirksomhed('45786943');
echo "Navn: " . $virksomhed['name'] . "\n"; // "Branthiq ApS"
echo "Form: " . $virksomhed['companydesc'] . "\n"; // "Anpartsselskab"
echo "Beskyttet: " . ($virksomhed['protected'] ? 'Ja' : 'Nej') . "\n";
foreach ($virksomhed['owners'] ?? [] as $owner) {
echo " Ejer: {$owner['name']} - {$owner['role']}\n";
}
$regnskaber = $client->hentRegnskab('45786943');
$seneste = $regnskaber[0];
echo "Omsætning: " . $seneste['figures']['revenue'] . " DKK\n";
?>
C# / .NET integration
C# er populært til enterprise-løsninger og Windows-applikationer. Her er en integration med HttpClient:
// DataCvrClient.cs - C# CVR-klient
using System.Net.Http.Headers;
using System.Text.Json;
public class DataCvrClient : IDisposable
{
private readonly HttpClient _httpClient;
private const string BaseUrl = "https://datacvrapi.dk/api/v2";
public DataCvrClient(string apiKey)
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
}
public async Task<JsonDocument> HentVirksomhed(string cvr)
{
var response = await _httpClient.GetAsync(
$"{BaseUrl}/dk/company/{cvr}"
);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonDocument.Parse(json);
}
public async Task<JsonDocument> HentRegnskab(string cvr)
{
var response = await _httpClient.GetAsync(
$"{BaseUrl}/dk/company/{cvr}/accounts"
);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonDocument.Parse(json);
}
public void Dispose()
{
_httpClient.Dispose();
}
}
// Brug
using var client = new DataCvrClient("DIN_API_NØGLE");
var virksomhed = await client.HentVirksomhed("45786943");
var root = virksomhed.RootElement;
Console.WriteLine($"Navn: {root.GetProperty("name").GetString()}");
Console.WriteLine($"Form: {root.GetProperty("companydesc").GetString()}");
Integration med regnskabssystemer
En af de mest værdifulde integrationer er at forbinde DataCVR API med dit regnskabssystem. Her er konceptuelle eksempler for de mest populære danske regnskabssystemer.
e-conomic - Automatisk kundeoprettelse
Med e-conomic og DataCVR API kan du automatisere kundeoprettelse. Når en bruger indtaster et CVR-nummer, hentes virksomhedsdata automatisk og oprettes som kunde i e-conomic.
// e-conomic integration - auto-opret kunde fra CVR
async function opretKundeFraCvr(cvrNummer) {
// 1. Hent virksomhedsdata fra DataCVR API
const cvrData = await fetch(
`https://datacvrapi.dk/api/v2/dk/company/${cvrNummer}`,
{ headers: { 'Authorization': 'Bearer DATACVR_API_KEY' } }
).then(r => r.json());
// 2. Opret kunde i e-conomic
const kundeData = {
name: cvrData.name,
address: cvrData.address,
zip: cvrData.zipcode,
city: cvrData.city,
country: 'DK',
corporateIdentificationNumber: cvrNummer,
vatZone: {
vatZoneNumber: 1 // Danmark
},
customerGroup: {
customerGroupNumber: 1
},
paymentTerms: {
paymentTermsNumber: 1
},
currency: 'DKK'
};
const response = await fetch(
'https://restapi.e-conomic.com/customers',
{
method: 'POST',
headers: {
'X-AppSecretToken': 'E_CONOMIC_APP_TOKEN',
'X-AgreementGrantToken': 'E_CONOMIC_AGREEMENT_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(kundeData)
}
);
return response.json();
}
Dinero - CVR-opslag ved fakturering
Dinero-brugere kan integrere CVR-opslag for automatisk udfyldning af kundeoplysninger ved fakturering:
// Dinero integration - CVR-opslag ved fakturering
async function udfyldKundeoplysninger(cvrNummer) {
const cvrData = await fetch(
`https://datacvrapi.dk/api/v2/dk/company/${cvrNummer}`,
{ headers: { 'Authorization': 'Bearer DATACVR_API_KEY' } }
).then(r => r.json());
// Udfyld formularfelter automatisk
document.getElementById('kunde-navn').value = cvrData.name;
document.getElementById('kunde-adresse').value = cvrData.address;
document.getElementById('kunde-postnr').value = cvrData.zipcode;
document.getElementById('kunde-by').value = cvrData.city;
document.getElementById('kunde-cvr').value = cvrNummer;
}
Billy - Auto-udfyld virksomhedsdata
Billy understøtter CVR-opslag via deres egen integration, men du kan bruge DataCVR API til at berige data med regnskabsoplysninger og kontaktinformation, som Billy ikke leverer.
Integration med CRM-systemer
CRM-systemer som HubSpot og Salesforce kan beriges med CVR-data for at holde kundekort opdaterede.
HubSpot - Berig kontakter med CVR-data
// HubSpot integration - berig virksomhedskort
async function berigHubSpotVirksomhed(hubspotCompanyId, cvrNummer) {
// 1. Hent CVR-data
const cvrData = await fetch(
`https://datacvrapi.dk/api/v2/dk/company/${cvrNummer}`,
{ headers: { 'Authorization': 'Bearer DATACVR_API_KEY' } }
).then(r => r.json());
// 2. Opdater HubSpot virksomhedskort
await fetch(
`https://api.hubapi.com/crm/v3/objects/companies/${hubspotCompanyId}`,
{
method: 'PATCH',
headers: {
'Authorization': 'Bearer HUBSPOT_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
properties: {
name: cvrData.name,
address: cvrData.address,
zip: cvrData.zipcode,
city: cvrData.city,
industry: cvrData.industrydesc,
numberofemployees: cvrData.employees,
website: cvrData.website
}
})
}
);
}
Webhooks og kontakt-scraping
DataCVR API's kontakt-scraping bruger et asynkront mønster. Du starter et scrape-job, og derefter poller du for resultater:
// Kontakt-scraping - asynkront mønster
async function scrapeKontakter(cvrNummer, apiKey) {
// 1. Start scrape-job
const startResponse = await fetch(
`https://datacvrapi.dk/api/v2/dk/company/${cvrNummer}/contacts/scrape`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
}
);
if (startResponse.status !== 202) {
throw new Error('Kunne ikke starte scrape-job');
}
const { job_id } = await startResponse.json();
// 2. Poll for resultater (max 2 minutter)
for (let i = 0; i < 24; i++) {
await new Promise(r => setTimeout(r, 5000)); // Vent 5 sekunder
const statusResponse = await fetch(
`https://datacvrapi.dk/api/v2/dk/company/${cvrNummer}/scrape-jobs/${job_id}`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
);
const job = await statusResponse.json();
if (job.status === 'completed') {
return job.contacts; // Array af kontakter
}
if (job.status === 'failed') {
throw new Error(`Scrape fejlede: ${job.error}`);
}
}
throw new Error('Timeout - scrape tog for lang tid');
}
Automations-platforme (Make, Zapier, n8n)
DataCVR API virker direkte med no-code automation-platforme. Du behøver ikke bygge en integration, du konfigurerer bare HTTP-modulet med den korrekte URL og API-nøgle som header.
Make.com (tidligere Integromat)
Tilføj et HTTP → Make a request modul i dit scenarie og konfigurér:
| URL | https://datacvrapi.dk/api/v2/dk/company/{dit-CVR-nummer} |
| Method | GET |
| Headers | Name: X-API-KeyValue: din API-nøgle fra dashboardet |
| Query String | Tom. API-nøglen må IKKE sendes som query parameter. |
| Parse response | Yes (JSON) |
Subdomain-alias: Du kan også bruge https://api.datacvrapi.dk/api/v2/dk/company/{CVR}, samme backend, samme svar.
Eksempel: Hent alle malere i Aalborg
Brug /search/company-endpointet til lister:
https://datacvrapi.dk/api/v2/dk/search/company?industrycode=433410&city=Aalborg&limit=50
industrycode=433410, DB07-koden for "Malerforretninger" (6-cifret)city=Aalborg, by (partiel match)limit=50, max resultater pr. kald (max 1000 med API-nøgle)
Find alle branchekoder på GET /api/v2/dk/industry/counts eller på datacvrapi.dk/brancher. Kombiner med zipcode, employees_min, capital_min, founded_after osv. for mere specifikke søgninger.
Filtrering der ikke findes som API-parameter
Visse filtre, fx "kun virksomheder med hjemmeside" eller "kun selskaber med specifikt ejerforhold", findes ikke som API-parameter. Løsning i Make.com:
- Hent den rå liste med de filtre der findes (industrycode, city, osv.)
- Tilføj et Filter-modul umiddelbart efter HTTP-modulet
- Sæt betingelsen, fx:
website→ Exists, elleremail→ Contains →@
Sådan filtrerer du bagefter. Det er ofte nødvendigt, fordi Erhvervsstyrelsen ikke nødvendigvis har hjemmesider registreret på alle virksomheder, feltet er nullable.
Ikke-eksisterende parametre ignoreres silent. Hvis du tilføjer ?branche=620100 (forkert, skal være industrycode) eller ?ingenHjemmeside=true (findes ikke), får du ingen 400-fejl. I stedet returneres alle virksomheder som om filteret ikke var der. Tjek total i responsen, hvis det er suspekt højt, har du nok brugt et forkert parameter-navn.
Typiske fejl i Make.com-opsætning:
- Cloudflare 400 Bad Request: URL'en er forkert. Brug præcis
datacvrapi.dk/api/v2/...ellerapi.datacvrapi.dk/api/v2/..., ikke andre stier. - 401 Unauthorized: API-nøglen er sat som Query String i stedet for Header.
- "Basic auth"-modulet: Hvis du bruger Make a basic auth request, skal API-nøglen i Username-feltet og Password efterlades tomt.
- Resultater uden filter: Tjek at du bruger de korrekte parameter-navne (se API-dokumentationen).
branche,status,region,websitefindes ikke.
Zapier
Brug Webhooks by Zapier → Custom Request:
- Method: GET
- URL:
https://datacvrapi.dk/api/v2/dk/company/{{cvr}} - Headers:
X-API-Key: din API-nøgle - Data: tom
n8n
Brug HTTP Request-noden:
- Authentication: Generic Credential Type → Header Auth → Name:
X-API-Key, Value: din nøgle - Method: GET
- URL:
https://datacvrapi.dk/api/v2/dk/company/{{ $json.cvr }}
Test først i browser
Hvis din integration fejler, test først API'en direkte i browseren:
https://datacvrapi.dk/api/v2/dk/company/45786943
Får du JSON-data tilbage, er API'en oppe og URL'en korrekt. Så er problemet i din integrations-opsætning. Får du en fejl eller "Cloudflare 400", er URL'en forkert.
Fejlhåndtering
Robust fejlhåndtering er afgørende for en stabil integration. Her er de vigtigste scenarier og hvordan du håndterer dem:
Retry-logik med exponential backoff
// Generisk retry-funktion
async function medRetry(fn, maxForsøg = 3) {
for (let i = 0; i < maxForsøg; i++) {
try {
return await fn();
} catch (error) {
if (i === maxForsøg - 1) throw error;
// Exponential backoff: 1s, 2s, 4s
const ventetid = Math.pow(2, i) * 1000;
console.log(`Forsøg ${i + 1} fejlede, prøver igen om ${ventetid}ms...`);
await new Promise(r => setTimeout(r, ventetid));
}
}
}
// Brug med CVR-opslag
const virksomhed = await medRetry(() =>
hentVirksomhed('45786943')
);
Håndtering af specifikke fejlkoder
async function sikkertCvrOpslag(cvrNummer) {
const response = await fetch(
`https://datacvrapi.dk/api/v2/dk/company/${cvrNummer}`,
{ headers: { 'Authorization': 'Bearer DIN_API_NØGLE' } }
);
switch (response.status) {
case 200:
return await response.json();
case 400:
throw new Error('Ugyldigt CVR-nummer - skal være 8 cifre');
case 401:
throw new Error('Ugyldig API-nøgle - tjek din nøgle i dashboardet');
case 403:
throw new Error('Dit abonnement giver ikke adgang til dette endpoint');
case 404:
throw new Error('Virksomheden findes ikke i CVR-registret');
case 429:
throw new Error('Rate limit overskredet - prøv igen senere');
default:
throw new Error(`Uventet fejl: ${response.status}`);
}
}
Best practices
Følg disse anbefalinger for en optimal integration:
Caching
Virksomhedsdata ændrer sig sjældent. Cache resultater lokalt i mindst 1 time (eller 24 timer for stamdata) for at reducere API-kald og forbedre responstider i din applikation.
Batch-opslag
Hvis du skal slå mange virksomheder op, implementer en kø der sender requests sekventielt med en kort pause imellem. Det undgår rate limiting og sikrer pålidelig datahentning.
Fejlhåndtering
Implementer altid retry-logik med exponential backoff for at håndtere midlertidige fejl og rate limiting. Log fejl, så du kan identificere og løse problemer hurtigt.
Miljøvariabler
Gem aldrig din API-nøgle i kildekoden. Brug miljøvariabler (process.env.DATACVR_API_KEY i Node.js, os.environ i Python) til at holde nøglen sikker.
Validering
Validér CVR-numre (8 cifre, modulus 11) på klientsiden, inden du sender requests til API'et. Det sparer API-kald og giver hurtigere feedback til brugeren.