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
- Skapa ett konto i utvecklarportalen, bekräfta din e-postadress och skapa en API-nyckel. Nyckeln visas en enda gång, kopiera den direkt.
- Lägg nyckeln i en miljövariabel, inte i koden:
export REGISTRY_API_KEY=rg_live_... - Kontrollera ett organisationsnummer. Det kostar ingenting:
curl https://api.registry.gothiaai.se/v1/validate/556123-4567 \ -H "Authorization: Bearer $REGISTRY_API_KEY" - 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.
| Nyckel | Anvä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. |
- Du kan ha högst fem aktiva nycklar per konto. Alla delar kontots kvot.
- Vi lagrar bara en hash av nyckeln. Tappar du den, återkalla den och skapa en ny. En återkallad nyckel slutar fungera inom cirka en minut.
- Saknad nyckel ger
missing_api_key. En okänd, återkallad, utgången eller avstängd nyckel gerinvalid_api_key, och det går medvetet inte att se skillnad.
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
- Villkor:
fält operator värde. Operatorer:=!=<<=>>=IN (...)NOT IN (...)LIKE 'prefix%'NOT LIKEIS NULLIS NOT NULL. Vilka operatorer ett fält stöder står i tabellen. - Kombinera med
AND,OR,NOToch parenteser.NOTbinder hårdast, sedanAND, sedanOR. - Värden: texter i enkelfnuttar (
'AB', en fnutt i texten skrivs dubbelt), heltal utan decimaler (5),trueochfalse, och datum som text'2025-01-31'. Fältnamn och nyckelord kan skrivas med versaler eller gemener. LIKEgodtar bara ett prefix med ett avslutande%, till exempel'62%', och bara på fält med siffror. Jokertecknet_och ett%mitt i stöds inte.- Saknade värden: ett villkor som kräver ett värde är falskt för bolag där värdet saknas.
employee_class != 5ger alltså inte bolag utan känd anställdsklass. AnvändIS NULLför att hitta dem. - Status: utan något villkor på
statusingår bara aktiva bolag (eller alla medinclude_deregistered=true). - Gränser: högst 1 000 tecken, 20 villkor, 5 nivåer av parenteser och NOT, och 100 värden i en
IN-lista, annarsfilter_too_complex. Ett uttryck som matchar nästan hela registret gerfilter_too_broad. - Kan inte kombineras med
q,sni,cityellerlegal_formsom egna parametrar. Skriv dem i uttrycket. - Fel: ett uttryck som inte går att tolka ger
invalid_filtermed position och vad som förväntades. Fel kostar inga enheter.
Fält
| Fält | Typ | Betydelse och exempel |
|---|---|---|
orgnr | text | Organisationsnummer, tio siffror (bindestreck tillåts). orgnr LIKE '556%' |
legal_form | text | Bolagsform, 'AB' eller 'AB-ORGFO'. legal_form IN ('AB','HB') |
status | text | 'active', 'deregistered' eller 'removed_from_source'. status IN ('active','deregistered') |
registration_date | datum | Registreringsdatum. registration_date >= '2025-01-01' |
sni | text | Primär SNI-kod, 2 till 5 siffror. = är exakt kod, LIKE '62%' är prefix. |
sni_any | text | Någon av bolagets högst fem SNI-koder. sni_any LIKE '62%' |
city | text | Ort. Matchar via postnummer, så bolag utan postnummer träffas inte. Okänd ort ger inga träffar. city = 'Göteborg' |
postal_code | text | Postnummer, fem siffror. postal_code LIKE '411%' |
marketing_block | boolesk | Reklamspärr hos SCB. true: har tackat nej till reklam. marketing_block = false |
employee_class | heltal 1–16 | SCB: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_class | heltal 0–12 | SCB:s omsättningsklass (3: 1–5 mnkr, 5: 10–20 mnkr, 8: 100–500 mnkr). turnover_class >= 5 |
turnover_year | heltal | Året som omsättningsklassen avser. turnover_year >= 2024 |
company_status | text | SCB:s företagsstatus: 'active', 'never_active', 'inactive'. |
legal_unit_status | text | SCB: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.
| Anrop | Kostnad |
|---|---|
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/batch | 1 enhet för varje bolag som hittas i svaret |
GET /v1/validate/{orgnr} | 0 |
GET /v1/usage | 0 |
- Fel kostar aldrig enheter. Rubriken
X-Units-Chargedpå svaret visar vad anropet drog. - Kvoten kontrolleras före anropet. Ett batch- eller listanrop som startar under gränsen genomförs helt och kan ta förbrukningen över kvoten med upp till 100 enheter. Nästa anrop som kostar enheter får då
429 quota_exceeded. validateochusagefungerar även när kvoten är slut, så att du kan se varför.- Förbrukningen i
/v1/usagekan ligga upp till en minut efter. - Samma nummer två gånger i ett batch-anrop ger två resultat och två enheter.
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).
- Alla anrop som kommer förbi nyckelkontrollen räknas, även sådana som ger 400 eller 404, och även de som är gratis. Anrop som avvisas med 429 räknas inte.
- Svaren har
X-RateLimit-Limit,X-RateLimit-RemainingochX-RateLimit-Reset(sekunder till nästa fönster). - Vid
429 rate_limitedangerRetry-Afterhur många sekunder du ska vänta. - Bygg din klient så att den sprider anropen i stället för att köra dem i en skur, särskilt på en liten plan.
Svarens uppbyggnad
Svar som innehåller bolag har tre delar:
data: bolaget, listan eller resultaten.coverage: vilka delar av informationen som finns. Statusarna äravailable,not_covered,restrictedochunavailable. Täckning är aldrig gissad.meta:request_id,data_as_of(när registret senast laddades) ochsources(källa och licens).
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:
- Ett saknat värde är
null, aldrig noll eller en tom sträng.marketing_block: nullbetyder "okänt", inte "nej". - Alla namn är
snake_case. Nullvärden skrivs alltid ut. - Datum är
ÅÅÅÅ-MM-DD, tidpunkter är ISO 8601 i UTC. Organisationsnummer returneras alltid som tio siffror utan bindestreck. - Ignorera fält du inte känner igen. Vi lägger till fält och nya
coverage-sektioner utan ny version. - Alla svar har rubriken
X-Request-Id, samma värde somrequest_id.
Bolagsstatus
status | Betydelse |
|---|---|
active | Aktivt bolag. |
deregistered | Avregistrerat. deregistration_date och oftast deregistration_reason är satta. |
removed_from_source | Har 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å
- En lista (utan
q) kostar en enhet för varje bolag som returneras. Att gå igenom 10 000 bolag kostar alltså 10 000 enheter, oavsettlimit. En namnsökning (q) kostar en enhet per anrop men ger högst 10 bolag, sålimitär högst 10 medq. - Med
qgäller encursorbara inom samma dataladdning. Laddas registret om mitt i får du410 cursor_expired: starta om utancursor. Utanqärcursorstabil över dataladdningar. - Namnsökningen når högst cirka 1 300 träffar djupt. Lägg till filter (
city,sni,legal_form) för att nå längre. - En okänd ort är inte ett fel: du får en tom lista. Med
qkostar den en enhet, utanqingenting.
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"
}
- Förgrena på
error(stabil felkod), inte påmessageellerhint, som kan ändras. - Alla koder med orsak och åtgärd finns i felkatalogen.
- Ange
request_idnär du kontaktar supporten. - Bara det första problemet rapporteras. Rättar du ett fel kan nästa komma fram.
- Fyra svar har ingen JSON-kropp: en sökväg som inte finns under
/v1ger404, fel HTTP-metod ger405, ogiltig JSON i ett batch-anrop ger400och saknadContent-Type: application/jsoni ett batch-anrop ger415. KontrolleraContent-Typepå svaret innan du läser det som JSON.
Rekommenderad strategi för omförsök
| Svar | Gör så här |
|---|---|
400, 401, 403, 404, 410 | Försök inte igen med samma anrop. Rätta anropet (410: starta om sökningen utan cursor). |
429 rate_limited | Vänta Retry-After sekunder och försök igen. |
429 quota_exceeded | Vä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_unavailable | Vä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ätverksfel | Fö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
- Källor: Bolagsverkets och SCB:s värdefulla datamängder (öppna data).
- Licens: Bolagsverkets data har licensen
CC0-1.0. SCB:s uppgifter får användas och spridas vidare om källan anges: “Källa: SCB (Statistiska centralbyrån). Egen bearbetning har gjorts.” Texten finns imeta.sources[].attribution. SCB kan ändra villkoren eller stänga av tjänsten, och SCB-fälten kan då tillfälligt saknas (coverage.scbärnot_covered). - SCB:s storleksklasser:
scb.employeesochscb.turnoverär intervall, inte exakta tal. Omsättningen är i tusental kronor (tkr) för året iyear. Ärscbnulllistar SCB inte bolaget, till exempel för avregistrerade bolag. - Färskhet: registret laddas om veckovis och kontrolleras dagligen. Ett bolag som registrerades i går kan saknas, och en ändring syns först vid nästa laddning.
meta.data_as_ofvisar när datan senast laddades. - Personer: enskilda firmor och andra nummer med personnummers form (tredje siffran 0 eller 1) ingår inte och slås aldrig upp. Det ger
not_a_company, så tjänsten kan inte användas för att kontrollera om en viss person driver verksamhet. Vi lämnar inte ut företrädare, ägare, telefon eller e-post. - Telefon:
contact.phonekommer från SCB. Betalande planer (Start och Pro) får numret helt (phone_access: full). Gratisplanen och sökningen på gothiaai.se får det maskerat, till exempel031-******(masked), så att ni ser att det finns. Bolag med telefonspärr eller reklamspärr får inget nummer på någon plan (blocked), ochnonebetyder att SCB saknar nummer. Använd inte numren för marknadsföring mot spärrade bolag, och kom ihåg att ett nummer kan vara en privatperson. - Reklamspärr:
marketing_blockärtruenär företaget har tackat nej till reklam i SCB:s register. Respektera det vid direktmarknadsföring: enligt SCB:s villkor får ni inte marknadsföra mot spärrade företag på de sätt de har tackat nej till.falsebetyder att företaget inte har tackat nej, ochnullatt uppgiften saknas. - Ort:
city-filtret slår upp ortnamnet i en ortstabell och matchar via postnummer. Bolag utan postnummer, eller med ett postnummer som inte finns i tabellen, träffas inte av ortfiltret. - SNI: filtret
snimatchar som prefix mot bolagets primära SNI-kod. Ett bolag med en sekundär kod i branschen träffas inte.
Begränsningar i version 1
- Bara Sverige (
country=SE). Andra värden gerunsupported_country. - Ingen finansiell information (
coverage.financialsärnot_covered). - Inga företrädare eller ägare (
coverage.peopleärnot_covered). - Ingen historik, inga webhooks och ingen export.
- Ingen separat sandbox:
rg_test_-nycklar går mot samma data och samma kvot som skarpa nycklar. - Inga anrop från webbläsare (ingen CORS).
- Gränsen per minut räknas per serverprocess. Ligger tjänsten bakom flera processer kan den effektiva gränsen bli högre än planens.
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.