Registry API

Registry API

Svenska bolag på organisationsnummer, namn, bransch och ort. Ett JSON-API över HTTPS för anrop från din server.

Kom igång

  1. Skapa ett konto i utvecklarportalen, bekräfta din e-postadress och skapa en API-nyckel. Nyckeln visas en enda gång, kopiera den direkt.
  2. Lägg nyckeln i en miljövariabel, inte i koden:
    export REGISTRY_API_KEY=rg_live_...
  3. Kontrollera ett organisationsnummer. Det kostar ingenting:
    curl https://api.registry.gothiaai.se/v1/validate/556123-4567 \
      -H "Authorization: Bearer $REGISTRY_API_KEY"
  4. Hämta ett bolag (1 enhet):
    curl https://api.registry.gothiaai.se/v1/companies/5561234567 \
      -H "Authorization: Bearer $REGISTRY_API_KEY"

Vill du prova utan att skriva kod? Öppna API-referensen, tryck Authorize, klistra in nyckeln och tryck Try it out. Anropen är riktiga och räknas mot din kvot.

Exempelsvaren i dokumentationen är illustrativa. Organisationsnumret 556123-4567 klarar kontrollsiffran men är inte hämtat ur registret.

Autentisering

Alla anrop under /v1 kräver headern Authorization: Bearer <nyckel>. Sidorna under /docs kräver ingen nyckel.

NyckelAnvänds till
rg_live_…Skarp drift.
rg_test_…Utveckling och prov. Fungerar i dag exakt som en skarp nyckel: samma data, samma kvot, samma gränser. Det finns ännu ingen separat sandbox med fasta testbolag.

Använd aldrig nyckeln i en webbläsare eller mobilapp. API:t är till för anrop från din server. Det har ingen CORS-konfiguration, så anrop från andra webbplatser blockeras av webbläsaren, och en nyckel i frontend kan läsas av vem som helst. Låt din egen backend anropa Registry.

Vanliga anrop

Alla exempel använder $REGISTRY_API_KEY från ovan. Fullständig beskrivning av varje parameter finns i API-referensen.

Kontrollera ett nummer (0 enheter)

curl https://api.registry.gothiaai.se/v1/validate/556123-4567 -H "Authorization: Bearer $REGISTRY_API_KEY"

Svarar alltid 200 för nummer som går att läsa, med valid_format, valid_checksum, in_registry och looks_like_personal_number. Använd det för att validera inmatning innan du slår upp bolaget.

Hämta ett bolag (1 enhet)

curl https://api.registry.gothiaai.se/v1/companies/556123-4567 -H "Authorization: Bearer $REGISTRY_API_KEY"

Numret får skrivas med eller utan bindestreck. Hittas bolaget inte får du 404 company_not_found, och det kostar ingenting.

Sök på namn (1 enhet per anrop, högst 10 bolag)

curl -G https://api.registry.gothiaai.se/v1/companies \
  --data-urlencode "q=exempel" --data-urlencode "city=Göteborg" --data-urlencode "limit=10" \
  -H "Authorization: Bearer $REGISTRY_API_KEY"

Filtrera på bransch, ort och bolagsform

curl -G https://api.registry.gothiaai.se/v1/companies \
  --data-urlencode "sni=62" --data-urlencode "city=Göteborg" --data-urlencode "legal_form=AB" \
  -H "Authorization: Bearer $REGISTRY_API_KEY"

Du måste ange q eller minst ett av sni, city och legal_form. Annars får du missing_filter. Filtren kombineras med OCH, och bara aktiva bolag ingår om du inte sätter include_deregistered=true.

Filtrera med ett uttryck

curl -G https://api.registry.gothiaai.se/v1/companies \
  --data-urlencode "filter=sni LIKE '62%' AND city = 'Göteborg' AND employee_class >= 5" \
  -H "Authorization: Bearer $REGISTRY_API_KEY"

Uttryck kombinerar fält, bland annat SCB:s storleksklasser. Se Filteruttryck.

Hämta flera bolag på en gång (1 enhet per hittat bolag)

curl -X POST https://api.registry.gothiaai.se/v1/companies/batch \
  -H "Authorization: Bearer $REGISTRY_API_KEY" -H "Content-Type: application/json" \
  -d '{"orgnrs": ["556123-4567", "5560000001", "811228-9874"]}'

Högst 100 nummer. Svaret har ett resultat per nummer i samma ordning, och HTTP-statusen är 200 även om enskilda nummer inte hittas. Kontrollera found i varje resultat. Ett nummer som inte går att slå upp har found: false och ett error-objekt.

Se din förbrukning (0 enheter)

curl https://api.registry.gothiaai.se/v1/usage -H "Authorization: Bearer $REGISTRY_API_KEY"

Filteruttryck

Med filter väljer du bolag genom att kombinera villkor på fält, till exempel sni LIKE '62%' AND employee_class >= 5. Det är samma lista som GET /v1/companies ger, med samma limit och cursor, och varje returnerat bolag kostar en enhet.

Språket

Fält

FältTypBetydelse och exempel
orgnrtextOrganisationsnummer, tio siffror (bindestreck tillåts). orgnr LIKE '556%'
legal_formtextBolagsform, 'AB' eller 'AB-ORGFO'. legal_form IN ('AB','HB')
statustext'active', 'deregistered' eller 'removed_from_source'. status IN ('active','deregistered')
registration_datedatumRegistreringsdatum. registration_date >= '2025-01-01'
snitextPrimär SNI-kod, 2 till 5 siffror. = är exakt kod, LIKE '62%' är prefix.
sni_anytextNågon av bolagets högst fem SNI-koder. sni_any LIKE '62%'
citytextOrt. Matchar via postnummer, så bolag utan postnummer träffas inte. Okänd ort ger inga träffar. city = 'Göteborg'
postal_codetextPostnummer, fem siffror. postal_code LIKE '411%'
marketing_blockbooleskReklamspärr hos SCB. true: har tackat nej till reklam. marketing_block = false
employee_classheltal 1–16SCB:s anställdsklass (3: 5–9 anställda, 5: 20–49, 7: 100–199, 9: 500–999, 16: 10 000 eller fler). employee_class >= 5
turnover_classheltal 0–12SCB:s omsättningsklass (3: 1–5 mnkr, 5: 10–20 mnkr, 8: 100–500 mnkr). turnover_class >= 5
turnover_yearheltalÅret som omsättningsklassen avser. turnover_year >= 2024
company_statustextSCB:s företagsstatus: 'active', 'never_active', 'inactive'.
legal_unit_statustextSCB:s status för den juridiska enheten: 'registered', 'registered_other', 'no_longer_in_population'.

GET /v1/schema (0 enheter) listar fälten, operatorerna, exemplen och gränserna maskinläsbart, så att du kan bygga ett formulär utan att hårdkoda dem.

SCB-fälten är storleksklasser, inte exakta tal, och källan är SCB (Statistiska centralbyrån). Egen bearbetning har gjorts. Respektera marketing_block vid marknadsföring.

Exempel

sni LIKE '62%' AND city = 'Göteborg' AND employee_class >= 5
(legal_form = 'AB' OR legal_form = 'HB') AND registration_date >= '2025-01-01' AND sni_any LIKE '47%'
turnover_class >= 10 AND marketing_block = false
status = 'deregistered' AND city = 'Malmö'

Enheter och kvot

Huvudregeln är 1 enhet = 1 bolag. Undantaget är namnsökning med q: den kostar 1 enhet per anrop och ger högst 10 bolag. Varje konto har ett antal enheter per kalendermånad (UTC). Planen free har 500 enheter och 10 anrop per minut. Kvoten gäller kontot: alla nycklar på kontot delar den. Den nollställs den 1:a varje månad klockan 00:00 UTC.

AnropKostnad
GET /v1/companies/{orgnr}1 enhet när bolaget hittas, annars 0
GET /v1/companies med q (namnsökning)1 enhet per anrop, även när sidan är tom. Högst 10 bolag per anrop
GET /v1/companies utan q (lista)1 enhet för varje bolag som returneras
POST /v1/companies/batch1 enhet för varje bolag som hittas i svaret
GET /v1/validate/{orgnr}0
GET /v1/usage0

Gräns per minut

Varje nyckel har en gräns för antal anrop per minut (planen free: 10). Räkningen sker i fasta fönster som följer klockminuten (UTC).

Svarens uppbyggnad

Svar som innehåller bolag har tre delar:

Ett exempel på ett bolag (förkortat):

{
  "data": {
    "orgnr": "5561234567",
    "country": "SE",
    "name": "Exempel AB",
    "legal_form": "AB-ORGFO",
    "status": "active",
    "registration_date": "1998-03-12",
    "deregistration_date": null,
    "address": { "street": "Exempelgatan 1", "co": null, "postal_code": "41101", "city": "GÖTEBORG", "country_code": "SE" },
    "industry": { "primary_sni": "62100", "codes": [ { "code": "62100", "description": "Dataprogrammering", "rank": 1 } ] },
    "marketing_block": false
  },
  "coverage": { "identity": { "status": "available" }, "financials": { "status": "not_covered" } },
  "meta": { "request_id": "req_3f9a1c47d2b85e0a6c11", "data_as_of": "2026-11-02T03:10:00Z" }
}

Regler som gäller överallt:

Bolagsstatus

statusBetydelse
activeAktivt bolag.
deregisteredAvregistrerat. deregistration_date och oftast deregistration_reason är satta.
removed_from_sourceHar försvunnit ur källan vid en omladdning. Behandla som inaktivt.

ongoing_procedure visar pågående förfarande (till exempel likvidation eller konkurs) som Bolagsverkets kod, annars null.

Paginering

Sökningen delas upp med cursor, inte sidnummer. Svaret har pagination.has_more och pagination.next_cursor. Skicka next_cursor oförändrad som cursor i nästa anrop, med samma övriga parametrar.

Gå igenom alla sidor (JavaScript, Node 18 eller senare):

const base = "https://api.registry.gothiaai.se/v1";
const headers = { Authorization: `Bearer ${process.env.REGISTRY_API_KEY}` };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

let cursor = null;
for (;;) {
  const url = new URL(`${base}/companies`);
  url.searchParams.set("sni", "62");
  url.searchParams.set("city", "Göteborg");
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("cursor", cursor);

  const res = await fetch(url, { headers });
  if (res.status === 429) {
    const err = await res.json();
    if (err.error !== "rate_limited") throw new Error(err.error); // quota_exceeded: vänta inte, kvoten är slut
    await sleep(Number(res.headers.get("Retry-After") ?? 5) * 1000);
    continue;
  }
  if (!res.ok) throw new Error(JSON.stringify(await res.json()));

  const body = await res.json();
  for (const c of body.data) console.log(c.orgnr, c.name);
  if (!body.pagination.has_more) break;
  cursor = body.pagination.next_cursor;
}

Samma sak i Python:

import os, time, requests

BASE = "https://api.registry.gothiaai.se/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['REGISTRY_API_KEY']}"}
params = {"sni": "62", "city": "Göteborg", "limit": 100}

while True:
    r = requests.get(f"{BASE}/companies", headers=HEADERS, params=params, timeout=30)
    if r.status_code == 429:
        if r.json()["error"] != "rate_limited":   # quota_exceeded: vänta inte, kvoten är slut
            raise RuntimeError(r.text)
        time.sleep(int(r.headers.get("Retry-After", "5")))
        continue
    r.raise_for_status()
    body = r.json()
    for c in body["data"]:
        print(c["orgnr"], c["name"])
    if not body["pagination"]["has_more"]:
        break
    params["cursor"] = body["pagination"]["next_cursor"]

Att tänka på

Felhantering

Alla fel har samma form:

{
  "error": "invalid_orgnr",
  "message": "Organisationsnumret har fel kontrollsiffra.",
  "hint": "Kontrollera numret eller använd GET /v1/validate/{orgnr}.",
  "docs_url": "https://api.registry.gothiaai.se/docs/errors#invalid_orgnr",
  "request_id": "req_5c2d90a1e7b34f68a10d"
}

Rekommenderad strategi för omförsök

SvarGör så här
400, 401, 403, 404, 410Försök inte igen med samma anrop. Rätta anropet (410: starta om sökningen utan cursor).
429 rate_limitedVänta Retry-After sekunder och försök igen.
429 quota_exceededVänta inte i sekunder: kvoten är slut tills månaden byts, eller tills du byter plan.
503 control_plane_unavailable, 503 server_busy, 503 filter_unavailableVänta Retry-After sekunder och försök igen, gärna med växande väntetid. Skicka inte flera sökningar parallellt.
500 och nätverksfelFörsök igen med växande väntetid och en övre gräns. Fortsätter det, kontakta supporten med request_id.

Fel kostar aldrig enheter, så omförsök av fel är säkra ur kvotsynpunkt. Alla endpoints är läsande och kan upprepas utan bieffekter, förutom att lyckade anrop drar enheter.

Data, färskhet och licens

Begränsningar i version 1

Versioner

Versionen ligger i sökvägen (/v1). Inom en version lägger vi bara till saker: nya fält, nya endpoints, nya coverage-sektioner och nya felkoder. Att ta bort eller ändra betydelsen av något görs i en ny version. Bygg därför din klient så att den tål okända fält och okända värden i error.

Maskinläsbar beskrivning: openapi.yaml (OpenAPI 3.0). Du kan importera den i Postman, Bruno, Insomnia eller en kodgenerator.

Support

Ange alltid request_id från svaret (rubriken X-Request-Id) när du hör av dig till GothiaAI om ett fel. Då kan vi hitta exakt det anropet. Ditt konto och dina nycklar hanterar du i utvecklarportalen.