Søg virksomhed Priser Dokumentation Kontakt

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:

  1. Gratis konto: Opret en konto på datacvrapi.dk (tager under 1 minut)
  2. API-nøgle: Find din API-nøgle i dit dashboard under "API-nøgle"
  3. 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(() => ({}));
    const fejl = new Error(
      `CVR opslag fejlede: ${response.status} - ${error.message || 'Ukendt fejl'}`
    );
    fejl.status = response.status;
    throw fejl;
  }

  return response.json();
}

// Brug
const virksomhed = await hentVirksomhed('24256790');
console.log(virksomhed.name);       // "NOVO NORDISK A/S"
console.log(virksomhed.address);    // "Novo Alle 1" (string)
console.log(virksomhed.zipcode);    // "2880"
console.log(virksomhed.city);       // "Bagsværd"
console.log(virksomhed.protected);  // false (true = reklamebeskyttet)
console.log(virksomhed.owners.filter(o => o.role === 'LEGAL EJER'));
// [{ name: "Novo Holdings A/S (CVR 24257630)", role: "LEGAL EJER", share: 25, ... }]

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) {
          // Retry-After er 60 ved for mange kald på kort tid.
          // Er kvoten brugt, er det sekunder til nulstillingen: giv op.
          const ventSek = Number(response.headers.get('Retry-After')) || 60;
          if (ventSek > 60) {
            lastError = new Error(`Kvoten er brugt. Nulstilles om ${ventSek} sekunder.`);
            break;
          }
          await new Promise(r => setTimeout(r, ventSek * 1000));
          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 (Hobby)
  async hentKontakter(cvr) {
    return this.request(`/dk/company/${cvr}/contacts`);
  }
}

// Brug
const client = new DataCvrClient('DIN_API_NØGLE');
const data = await client.hentVirksomhed('24256790');
console.log(data.name); // "NOVO NORDISK A/S"

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:
                # Retry-After er 60 ved for mange kald på kort tid.
                # Er kvoten brugt, er det sekunder til nulstillingen: giv op.
                ventetid = int(response.headers.get("Retry-After", "60"))
                if ventetid > 60:
                    raise Exception(f"Kvoten er brugt. Nulstilles om {ventetid} sekunder.")
                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 regnskaber (kræver Business). Svaret er {cvr, count, accounts}."""
        return self._request(f"/dk/company/{cvr_nummer}/accounts")

    def hent_seneste_regnskab(self, cvr_nummer):
        """Hent seneste årsregnskab (kræver Business)."""
        return self._request(f"/dk/company/{cvr_nummer}/accounts/latest")

    def hent_kontakter(self, cvr_nummer):
        """Hent cached kontakter (kræver Hobby)."""
        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("24256790")
    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 seneste årsregnskab. Listen fra hent_regnskab ligger i
    # regnskaber["accounts"] og kan også indeholde kvartals- og halvårsrapporter.
    seneste = client.hent_seneste_regnskab("24256790")
    print(f"Omsætning {seneste['fiscal_year']}: {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));
        }

        // Læs Retry-After fra svar-headerne
        $retryAfter = 60;
        curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $linje) use (&$retryAfter) {
            if (stripos($linje, 'Retry-After:') === 0) {
                $retryAfter = (int) trim(substr($linje, 12));
            }
            return strlen($linje);
        });

        // 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 && $retryAfter <= 60) {
                // For mange kald på kort tid: vent som API'et beder om.
                // Er kvoten brugt, er Retry-After tiden til nulstillingen. Så stopper
                // løkken, og fejlen kastes nedenfor.
                sleep($retryAfter);
                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 {
        // Svaret er ['cvr' => ..., 'count' => ..., 'accounts' => [...]]
        return $this->request("/dk/company/{$cvr}/accounts");
    }

    public function hentSenesteRegnskab(string $cvr): array {
        return $this->request("/dk/company/{$cvr}/accounts/latest");
    }

    public function hentKontakter(string $cvr): array {
        return $this->request("/dk/company/{$cvr}/contacts");
    }
}

// Brug
$client = new DataCvrClient('DIN_API_NØGLE');

$virksomhed = $client->hentVirksomhed('24256790');
echo "Navn: " . $virksomhed['name'] . "\n";         // "NOVO NORDISK A/S"
echo "Form: " . $virksomhed['companydesc'] . "\n";   // "A/S"
echo "Beskyttet: " . ($virksomhed['protected'] ? 'Ja' : 'Nej') . "\n";
foreach ($virksomhed['owners'] ?? [] as $owner) {
    echo "  Ejer: {$owner['name']} - {$owner['role']}\n";
}

// Seneste årsregnskab. Listen fra hentRegnskab ligger i $regnskaber['accounts']
// og kan også indeholde kvartals- og halvårsrapporter.
$seneste = $client->hentSenesteRegnskab('24256790');
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("24256790");
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());

  // employees er et interval som tekst (fx "5000+"), ikke et tal. Det nyeste
  // eksakte tal står sidst i employees_monthly, men historikken kan være ældre
  // end stamdata. Send kun et antal, når CVR har et eksakt tal, og ellers
  // intervallet i en egen egenskab, som du opretter i HubSpot.
  const nyeste = (cvrData.employees_monthly || []).at(-1);
  const antalAnsatte = nyeste?.employees_exact;

  // 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,
          ...(antalAnsatte != null && { numberofemployees: antalAnsatte }),
          ansatte_interval: cvrData.employees, // egen egenskab, fx "5000+"
          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-Key
Value: 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:

  1. Hent den rå liste med de filtre der findes (industrycode, city, osv.)
  2. Tilføj et Filter-modul umiddelbart efter HTTP-modulet
  3. Sæt betingelsen, fx: website → Exists, eller email → 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. Står de alene, fx ?branche=620100 (forkert, skal være industrycode), svarer API'et 400 INVALID_QUERY, fordi der ikke er noget gyldigt søgekriterium. Står de sammen med et gyldigt filter, ignoreres de uden fejl: den 06-10-2026 gav ?city=Aalborg&branche=620100 de samme 6.090 hits som ?city=Aalborg alene. Tjek total i responsen. Er det 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/... eller api.datacvrapi.dk/api/v2/..., ikke andre stier.
  • Nøglen virker ikke: API-nøglen er sat som Query String i stedet for Header. En nøgle i query string bliver ikke læst, så kaldet behandles som uden nøgle. Afhængigt af klientens User-Agent giver det enten 401 API_KEY_REQUIRED eller det begrænsede anonyme opslag (25 om dagen pr. IP).
  • "Basic auth"-modulet: Hvis du bruger Make a basic auth request, skal API-nøglen i Username-feltet og Password efterlades tomt.
  • Filteret virker ikke: Tjek at du bruger de korrekte parameter-navne (se API-dokumentationen). branche, status, region, website findes ikke. Alene giver de 400 INVALID_QUERY, og sammen med et gyldigt filter ignoreres de.

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/24256790

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

Exponential backoff er til midlertidige fejl (netværk og 5xx). Ved 429 skal du i stedet læse Retry-After, som i klienten ovenfor, og 4xx-fejl som 400 og 404 bliver ikke bedre af et nyt forsøg.

// Generisk retry-funktion til midlertidige fejl
async function medRetry(fn, maxForsøg = 3) {
  for (let i = 0; i < maxForsøg; i++) {
    try {
      return await fn();
    } catch (error) {
      // 4xx (fx 400, 404 og 429) prøves ikke igen her
      if (error.status && error.status < 500) throw 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('24256790')
);

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 retry-logik med exponential backoff for midlertidige fejl (netværk og 5xx), og læs Retry-After ved 429. 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.

Ofte stillede spørgsmål om CVR API integration

Hvordan integrerer jeg CVR API?

Opret en gratis konto, find din API-nøgle i dashboardet, og lav HTTP-kald til vores REST API. Data returneres som JSON, der nemt integreres i alle sprog.

Hvilke programmeringssprog understøttes?

Alle sprog der kan lave HTTP-kald. Vi har kodeeksempler i JavaScript, Python, PHP og C#/.NET, men API'et virker med alle sprog og frameworks.

Kan jeg integrere med e-conomic?

Ja, du kan kombinere DataCVR API med e-conomic's REST API for automatisk kundeoprettelse med CVR-data. Se kodeeksemplet ovenfor.

Hvordan håndterer jeg rate limiting?

Ved et 429-svar skal du læse headeren Retry-After og vente det antal sekunder. Det er 60 ved for mange kald på kort tid og tiden til nulstillingen, når kvoten er brugt. Cache desuden resultater lokalt.

Er der SDK'er tilgængelige?

Vi arbejder på officielle SDK'er til JavaScript/TypeScript og Python. Indtil da kan kodeeksemplerne i denne guide bruges som udgangspunkt.

Klar til at integrere CVR-data i dit system?

Opret en gratis konto og lav dit første API-kald på under 2 minutter.