Hvad der virker, og hvad der ikke gør
API'et kræver, at nøglen sendes i en HTTP-header. Det sætter en grænse for, hvilke regnearks-funktioner du kan bruge:
| Metode | Virker? | Bemærkning |
|---|---|---|
| Excel: Power Query (Web.Contents) | Ja | Kan sætte headers. Anbefalet i Excel |
| Excel: WEBSERVICE-funktionen | Nej | Kan ikke sende en Authorization-header |
| Google Sheets: Apps Script (UrlFetchApp) | Ja | Kan sætte headers. Anbefalet i Sheets |
| Google Sheets: IMPORTDATA og IMPORTXML | Nej | Kan ikke sende headers |
| Ark med færdig formel uden opsætning | Nej | Der er intet indbygget CVR-opslag |
Hvis du bare skal have et par virksomheder ind en enkelt gang, er det hurtigere at kalde API'et fra en terminal eller et script og indsætte resultatet. Skal opslagene gentages, giver en forespørgsel eller et script mening.
Excel: ét opslag med Power Query
Power Query er en del af Excel under fanen Data. Vælg Hent data, Fra andre kilder og Tom forespørgsel, åbn Avanceret editor, og indsæt koden. Skift ApiKey ud med din egen nøgle. Første gang beder Excel om at vælge legitimationsoplysninger til adressen. Vælg Anonym, fordi nøglen sendes i headeren.
Power Query (M)
let
ApiKey = "cvr_live_din_api_nøgle",
Svar = Web.Contents(
"https://firmaapi.dk/api/v1/company/24256790",
[Headers = [Authorization = "Bearer " & ApiKey]]
),
Data = Json.Document(Svar),
Tabel = Record.ToTable(
Record.SelectFields(Data, {"cvr", "name", "status", "address", "zipcode", "city", "industry_text", "employees"})
)
in
TabelResultatet bliver en tabel med feltnavn og værdi for den ene virksomhed. Vil du have flere felter, tilføjer du flere navne i Record.SelectFields. Alle felter står i dokumentationen.
Excel: en liste af CVR-numre
Lav en tabel i dit ark med kolonnen CVR, og giv tabellen navnet Cvrliste. Forespørgslen herunder laver et opslag pr. række og udvider felterne som kolonner. Fejl, som 404, bliver til en tekst i kolonnen Status, så resten af listen stadig kommer med.
Power Query (M)
let
ApiKey = "cvr_live_din_api_nøgle",
Hent = (cvr as text) as record =>
let
Svar = Web.Contents(
"https://firmaapi.dk/api/v1/company/" & cvr,
[
Headers = [Authorization = "Bearer " & ApiKey],
ManualStatusHandling = {400, 401, 403, 404, 429}
]
),
Status = Value.Metadata(Svar)[Response.Status],
Tom = [name = null, status = "Fejl " & Text.From(Status), address = null, zipcode = null, city = null, industry_text = null, employees = null],
Data = if Status = 200 then Json.Document(Svar) else Tom
in
Data,
Kilde = Excel.CurrentWorkbook(){[Name = "Cvrliste"]}[Content],
Typer = Table.TransformColumnTypes(Kilde, {{"CVR", type text}}),
MedData = Table.AddColumn(Typer, "Data", each Hent([CVR])),
Udvid = Table.ExpandRecordColumn(
MedData, "Data",
{"name", "status", "address", "zipcode", "city", "industry_text", "employees"}
)
in
UdvidExcel: batch-opslag med POST
Har du Basis eller højere, kan du slå op til 100 CVR-numre op i ét kald. Det er hurtigere end ét opslag pr. række. Hvert fundet CVR-nummer tæller stadig som ét opslag. Når du angiver et Content i Web.Contents, sender Power Query en POST.
Power Query (M)
let
ApiKey = "cvr_live_din_api_nøgle",
Cvrer = {"24256790", "54562519", "25313763"},
Svar = Web.Contents(
"https://firmaapi.dk/api/v1/company/batch",
[
Headers = [Authorization = "Bearer " & ApiKey, #"Content-Type" = "application/json"],
Content = Json.FromValue([cvr_numbers = Cvrer])
]
),
Json = Json.Document(Svar),
Tabel = Table.FromRecords(Json[results], {"cvr", "name", "status", "city", "employees"}, MissingField.UseNull)
in
TabelSvaret har felterne results, errors, total og quota_used. CVR-numre, der ikke findes, ligger i errors. Over 100 numre må du dele listen op, hvis du er på Basis.
Google Sheets: en funktion med Apps Script
I Google Sheets åbner du Udvidelser og Apps Script, indsætter koden og gemmer nøglen under Projektindstillinger, Script Properties med navnet FIRMAAPI_KEY. Så kan du skrive =CVR(A2; "name") eller =CVR(A2; "city") direkte i arket. Afhængigt af dine regionale indstillinger bruges komma eller semikolon som skilletegn.
Apps Script: egen CVR-funktion
const BASE = "https://firmaapi.dk/api/v1";
function noegle_() {
return PropertiesService.getScriptProperties().getProperty("FIRMAAPI_KEY");
}
/**
* Slår en virksomhed op på CVR-nummer.
* @param {string} cvr CVR-nummer
* @param {string} felt Feltnavn, fx "name", "city" eller "status"
* @customfunction
*/
function CVR(cvr, felt) {
const nummer = String(cvr).replace(/\D/g, "");
if (nummer.length !== 8) return "Ugyldigt CVR";
const cache = CacheService.getScriptCache();
let tekst = cache.get("cvr_" + nummer);
if (!tekst) {
const res = UrlFetchApp.fetch(BASE + "/company/" + nummer, {
headers: { Authorization: "Bearer " + noegle_() },
muteHttpExceptions: true,
});
const status = res.getResponseCode();
if (status === 404) return "Ikke fundet";
if (status === 429) return "For mange opslag";
if (status !== 200) return "Fejl " + status;
const d = JSON.parse(res.getContentText());
// Cachen tager højst 100 KB pr. værdi, så gem kun de felter, vi bruger.
const slank = {
name: d.name, status: d.status, address: d.address, zipcode: d.zipcode,
city: d.city, company_type: d.company_type, industry_text: d.industry_text,
employees: d.employees, founded: d.founded, phone: d.phone,
};
tekst = JSON.stringify(slank);
cache.put("cvr_" + nummer, tekst, 21600); // 6 timer
}
const data = JSON.parse(tekst);
const vaerdi = data[felt || "name"];
return vaerdi === undefined || vaerdi === null ? "" : vaerdi;
}Funktionen gemmer svaret i seks timer, så du ikke bruger et nyt opslag, hver gang arket genberegner. Google begrænser både køretiden pr. funktionskald og antallet af eksterne kald pr. dag, så den passer bedst til få hundrede rækker ad gangen.
Google Sheets: opdatér en hel liste
Til længere lister er et script bedre end en funktion pr. celle. Scriptet herunder læser CVR-numre fra kolonne A, slår dem op i grupper på 100 med batch-endpointet og skriver navn, status, by og antal ansatte i kolonne B til E. Du kan koble det til en knap eller en menu, så du selv bestemmer, hvornår der bruges opslag.
Apps Script: batch
function opdaterListe() {
const ark = SpreadsheetApp.getActiveSheet();
const antal = ark.getLastRow() - 1;
if (antal < 1) return;
const cvrer = ark.getRange(2, 1, antal, 1).getValues()
.map(function (r) { return String(r[0]).replace(/\D/g, ""); });
const gyldige = cvrer.filter(function (c, i) { return c.length === 8 && cvrer.indexOf(c) === i; });
const fundet = {};
for (let i = 0; i < gyldige.length; i += 100) {
const res = UrlFetchApp.fetch(BASE + "/company/batch", {
method: "post",
contentType: "application/json",
headers: { Authorization: "Bearer " + noegle_() },
payload: JSON.stringify({ cvr_numbers: gyldige.slice(i, i + 100) }),
muteHttpExceptions: true,
});
if (res.getResponseCode() !== 200) {
throw new Error("FirmaAPI svarede " + res.getResponseCode() + ": " + res.getContentText());
}
JSON.parse(res.getContentText()).results.forEach(function (f) { fundet[f.cvr] = f; });
}
const raekker = cvrer.map(function (c) {
const f = fundet[c];
return f ? [f.name, f.status, f.city, f.employees] : ["Ikke fundet", "", "", ""];
});
ark.getRange(2, 2, raekker.length, 4).setValues(raekker);
}Batch kræver Basis. På den gratis plan kan du bruge funktionen ovenfor i stedet.
Gode vaner, så du ikke løber tør for opslag
- Test først med en
cvr_test_-nøgle. Den giver syntetiske data og bruger ikke af kvoten. - Fjern mellemrum og bindestreger fra CVR-numre. API'et forventer otte cifre, ellers svarer det 400 INVALID_CVR.
- Gem resultatet som værdier, når du er tilfreds, så arket ikke slår de samme virksomheder op igen.
- Del aldrig en fil eller et ark, hvor din live-nøgle står i klartekst.
- Gratis-planen giver 1.000 opslag om måneden. Se priser, hvis du har brug for mere.