{
 "openapi": "3.0.3",
 "info": {
  "title": "Company search API",
  "version": "0.1 (local test)",
  "description": "Swedish companies from the free weekly files: name, organisation number, postal address and status from Bolagsverket, industry codes (SNI) from SCB, and turnover and other key figures from digitally filed annual reports. Sole traders are not included. Credit: Källa: Bolagsverket och Statistiska centralbyrån (SCB)."
 },
 "tags": [
  {
   "name": "Company search",
   "description": "The service's own API. No key needed."
  },
  {
   "name": "CheckBiz-compatible",
   "description": "Same paths, parameters and reply shapes as the CheckBiz API, for callers that switch by changing base URL. Works with any Authorization value or none."
  },
  {
   "name": "Maintenance",
   "description": "Data status and refresh."
  }
 ],
 "paths": {
  "/api/search": {
   "get": {
    "tags": [
     "Company search"
    ],
    "summary": "Search companies by name or organisation number",
    "description": "Every word must occur in the name. å, ä and ö are their own letters. The last word may be the start of a longer word unless it is followed by a space, which makes it a whole word. Ten digits are treated as an organisation number. Results are in Swedish alphabetical order. Asking for a page past the last result gives an empty list.",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "description": "Name or organisation number. A trailing space makes the last word a whole-word match.",
      "required": false,
      "schema": {
       "type": "string"
      },
      "example": "arla"
     },
     {
      "name": "postcode",
      "in": "query",
      "description": "Postcode or the start of one, digits only.",
      "required": false,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "city",
      "in": "query",
      "description": "Town, exact, any case.",
      "required": false,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "include_deregistered",
      "in": "query",
      "description": "Set to 1 to include deregistered companies.",
      "required": false,
      "schema": {
       "type": "string",
       "enum": [
        "1"
       ]
      }
     },
     {
      "name": "limit",
      "in": "query",
      "description": "Results per page, 1–100.",
      "required": false,
      "schema": {
       "type": "integer",
       "default": 20
      },
      "example": 5
     },
     {
      "name": "offset",
      "in": "query",
      "description": "Results to skip, for paging.",
      "required": false,
      "schema": {
       "type": "integer",
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Matches. `match` says how they were found: words, part (inside a name), loose (accents ignored), orgnr, filter or none.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "match": {
           "type": "string"
          },
          "results": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Company"
           }
          },
          "count": {
           "type": "integer"
          },
          "ms": {
           "type": "integer"
          },
          "source": {
           "type": "string"
          },
          "data_date": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "limit or offset is not a number."
     }
    }
   }
  },
  "/api/company/{orgnr}": {
   "get": {
    "tags": [
     "Company search"
    ],
    "summary": "Everything about one organisation number",
    "description": "All fields the file holds: every name with type and date, status, deregistration reason, ongoing proceedings, business description. Returns a list because a few numbers carry more than one registration.",
    "parameters": [
     {
      "name": "orgnr",
      "in": "path",
      "required": true,
      "description": "Ten digits, with or without the hyphen.",
      "schema": {
       "type": "string"
      },
      "example": "556579-4400"
     }
    ],
    "responses": {
     "200": {
      "description": "The registrations on that number.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "match": {
           "type": "string"
          },
          "results": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/CompanyDetails"
           }
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "No company with that number."
     }
    }
   }
  },
  "/api/report/{orgnr}/{period_end}": {
   "get": {
    "tags": [
     "Company search"
    ],
    "summary": "The filed annual report itself",
    "description": "Fetched from Bolagsverket's files when asked for and shown as a web page (the iXBRL document the company filed). Use the links in a company's `financials` list.",
    "parameters": [
     {
      "name": "orgnr",
      "in": "path",
      "required": true,
      "description": "Ten digits, no hyphen.",
      "schema": {
       "type": "string"
      },
      "example": "5593394116"
     },
     {
      "name": "period_end",
      "in": "path",
      "required": true,
      "description": "Last day of the financial year.",
      "schema": {
       "type": "string",
       "format": "date"
      },
      "example": "2026-06-30"
     },
     {
      "name": "download",
      "in": "query",
      "required": false,
      "description": "Set to 1 to save the file instead of showing it.",
      "schema": {
       "type": "string",
       "enum": [
        "1"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "The report (text/html)."
     },
     "404": {
      "description": "No digitally filed report for that company and year in the files read so far."
     },
     "502": {
      "description": "Bolagsverket did not deliver the report right now."
     }
    }
   }
  },
  "/checkbiz/companysearch": {
   "get": {
    "tags": [
     "CheckBiz-compatible"
    ],
    "summary": "Company search, CheckBiz shape",
    "description": "Names that start with the term come first, then names holding it as a later word. Active companies only. A trailing * is accepted and ignored; unknown parameters are ignored. No hits gives 200 with an empty records array.",
    "parameters": [
     {
      "name": "Who",
      "in": "query",
      "description": "Search term; callers append *.",
      "required": false,
      "schema": {
       "type": "string"
      },
      "example": "arla*"
     },
     {
      "name": "MaxNrRecords",
      "in": "query",
      "description": "Maximum records, 1–100.",
      "required": false,
      "schema": {
       "type": "integer",
       "default": 10
      },
      "example": 10
     },
     {
      "name": "Package",
      "in": "header",
      "required": false,
      "description": "Sent by CheckBiz callers; ignored.",
      "schema": {
       "type": "string"
      },
      "example": "CompanySearch"
     },
     {
      "name": "Authorization",
      "in": "header",
      "required": false,
      "description": "Optional. Any value, or none, is accepted.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Search result.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/CheckBizSearch"
        }
       }
      }
     }
    }
   }
  },
  "/checkbiz/companyinformation": {
   "get": {
    "tags": [
     "CheckBiz-compatible"
    ],
    "summary": "Company lookup, CheckBiz shape",
    "description": "Industry codes are returned in extended.registeredSNI. Fields this service has no data for (phone, tax status, municipality, coordinates, parent company) are returned empty.",
    "parameters": [
     {
      "name": "OrganizationNumber",
      "in": "query",
      "description": "Ten digits, no hyphen.",
      "required": true,
      "schema": {
       "type": "string"
      },
      "example": "5565794400"
     },
     {
      "name": "Package",
      "in": "header",
      "required": false,
      "description": "Sent by CheckBiz callers; ignored.",
      "schema": {
       "type": "string"
      },
      "example": "CompanyInformation"
     },
     {
      "name": "Authorization",
      "in": "header",
      "required": false,
      "description": "Optional. Any value, or none, is accepted.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "The company.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/CheckBizLookup"
        }
       }
      }
     },
     "400": {
      "description": "OrganizationNumber is not 10 digits."
     },
     "404": {
      "description": "Unknown organisation number."
     }
    }
   }
  },
  "/api/health": {
   "get": {
    "tags": [
     "Maintenance"
    ],
    "summary": "Is the service up, and how fresh is the data",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Row counts, the date of Bolagsverket's file and when it was loaded."
     },
     "503": {
      "description": "No database built yet."
     }
    }
   }
  },
  "/api/update": {
   "get": {
    "tags": [
     "Maintenance"
    ],
    "summary": "Progress of the latest update",
    "parameters": [],
    "responses": {
     "200": {
      "description": "running, message, ok, updated."
     }
    }
   },
   "post": {
    "tags": [
     "Maintenance"
    ],
    "summary": "Fetch Bolagsverket's and SCB's files if newer and rebuild",
    "description": "Runs in the background: download, build beside the live database, check, swap in; then reads new annual-report files. Does nothing if the files are unchanged. At most one check every 10 minutes. Follow progress with GET.",
    "parameters": [],
    "responses": {
     "202": {
      "description": "Started, or already running."
     }
    }
   }
  },
  "/healthz": {
   "get": {
    "tags": [
     "Maintenance"
    ],
    "summary": "Is the service up and the data fresh (open to everyone)",
    "description": "For monitoring. Answers 503 with status \"stale\" when Bolagsverket's file is more than 10 days old, which means the weekly refresh is failing.",
    "parameters": [],
    "responses": {
     "200": {
      "description": "status ok, the date of the data and its age in days."
     },
     "503": {
      "description": "No data, or stale data."
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Address": {
    "type": "object",
    "properties": {
     "street": {
      "type": "string"
     },
     "co": {
      "type": "string"
     },
     "postcode": {
      "type": "string",
      "example": "169 04"
     },
     "city": {
      "type": "string",
      "example": "SOLNA"
     },
     "country": {
      "type": "string",
      "example": "SE"
     }
    }
   },
   "Company": {
    "type": "object",
    "properties": {
     "orgnr": {
      "type": "string",
      "example": "556579-4400"
     },
     "serial": {
      "type": "integer",
      "description": "0 unless several registrations share the number"
     },
     "name": {
      "type": "string"
     },
     "other_names": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "form": {
      "type": "string",
      "example": "AB"
     },
     "address": {
      "$ref": "#/components/schemas/Address"
     },
     "registered": {
      "type": "string",
      "format": "date"
     },
     "deregistered": {
      "type": "string",
      "format": "date",
      "nullable": true
     },
     "active": {
      "type": "boolean"
     },
     "sni": {
      "type": "array",
      "description": "Up to five industry codes, main industry first. Empty when SCB has none (about 14 % of active companies).",
      "items": {
       "$ref": "#/components/schemas/Industry"
      }
     }
    }
   },
   "CodeText": {
    "type": "object",
    "properties": {
     "code": {
      "type": "string"
     },
     "text": {
      "type": "string"
     }
    }
   },
   "CompanyDetails": {
    "type": "object",
    "properties": {
     "orgnr": {
      "type": "string"
     },
     "serial": {
      "type": "integer"
     },
     "name": {
      "type": "string"
     },
     "identity_type": {
      "type": "string"
     },
     "form": {
      "$ref": "#/components/schemas/CodeText"
     },
     "address": {
      "$ref": "#/components/schemas/Address"
     },
     "registered": {
      "type": "string",
      "format": "date"
     },
     "deregistered": {
      "type": "string",
      "format": "date",
      "nullable": true
     },
     "active": {
      "type": "boolean"
     },
     "deregistration_reason": {
      "allOf": [
       {
        "$ref": "#/components/schemas/CodeText"
       }
      ],
      "nullable": true
     },
     "proceedings": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "code": {
         "type": "string"
        },
        "text": {
         "type": "string"
        },
        "since": {
         "type": "string",
         "format": "date"
        }
       }
      }
     },
     "names": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "type": {
         "$ref": "#/components/schemas/CodeText"
        },
        "registered": {
         "type": "string",
         "format": "date"
        },
        "description": {
         "type": "string",
         "nullable": true
        }
       }
      }
     },
     "business_description": {
      "type": "string",
      "nullable": true
     },
     "sni": {
      "type": "array",
      "description": "Up to five industry codes, main industry first. Empty when SCB has none (about 14 % of active companies).",
      "items": {
       "$ref": "#/components/schemas/Industry"
      }
     },
     "financials": {
      "type": "array",
      "description": "Up to six financial years, newest first. Empty when the company has not filed digitally or its report has not been read yet.",
      "items": {
       "$ref": "#/components/schemas/FinancialYear"
      }
     }
    }
   },
   "CheckBizSearch": {
    "type": "object",
    "properties": {
     "basicResult": {
      "type": "object",
      "properties": {
       "recordsReturned": {
        "type": "integer"
       },
       "totalRecordsFound": {
        "type": "integer",
        "description": "Counted up to 1,000."
       },
       "records": {
        "type": "array",
        "items": {
         "type": "object",
         "properties": {
          "orgNr": {
           "type": "string",
           "example": "556579-4400"
          },
          "companyName": {
           "type": "string"
          },
          "companyCode": {
           "type": "string"
          },
          "co": {
           "type": "string"
          },
          "street": {
           "type": "string"
          },
          "zipCode": {
           "type": "string",
           "example": "16904"
          },
          "city": {
           "type": "string",
           "example": "Solna"
          },
          "position": {
           "type": "object",
           "properties": {
            "latitude": {
             "type": "string"
            },
            "longitude": {
             "type": "string"
            }
           }
          }
         }
        }
       }
      }
     }
    }
   },
   "CheckBizLookup": {
    "type": "object",
    "properties": {
     "responseCode": {
      "type": "string"
     },
     "responseMessage": {
      "type": "string"
     },
     "basic": {
      "type": "object",
      "properties": {
       "companyName": {
        "type": "string"
       },
       "organizationNumber": {
        "type": "string"
       },
       "status": {
        "type": "string",
        "enum": [
         "ACTIVE",
         "INACTIVE"
        ]
       },
       "co": {
        "type": "string"
       },
       "street": {
        "type": "string"
       },
       "zipCode": {
        "type": "string"
       },
       "city": {
        "type": "string"
       },
       "companyCode": {
        "type": "string"
       },
       "companyCodeText": {
        "type": "string"
       },
       "registrationDate": {
        "type": "string",
        "format": "date",
        "nullable": true
       },
       "phoneNumber": {
        "type": "string"
       },
       "relatedOrg": {
        "type": "string"
       },
       "public": {
        "type": "boolean"
       },
       "financialYear": {
        "type": "string"
       }
      }
     },
     "extended": {
      "type": "object",
      "description": "Mostly empty in this service; see the description of the endpoint.",
      "additionalProperties": true
     }
    }
   },
   "Industry": {
    "type": "object",
    "description": "One SNI 2025 industry code from SCB.",
    "properties": {
     "code": {
      "type": "string",
      "example": "10519"
     },
     "text": {
      "type": "string",
      "example": "Annan mejerivarutillverkning"
     },
     "section": {
      "$ref": "#/components/schemas/CodeText"
     },
     "main": {
      "type": "boolean",
      "description": "True for the main industry (listed first)."
     }
    }
   },
   "FinancialYear": {
    "type": "object",
    "description": "Key figures for one financial year, read from a digitally filed annual report. Amounts are in whole units of the currency. null means the report did not state the figure.",
    "properties": {
     "period_start": {
      "type": "string",
      "format": "date",
      "nullable": true
     },
     "period_end": {
      "type": "string",
      "format": "date"
     },
     "currency": {
      "type": "string",
      "example": "SEK"
     },
     "turnover": {
      "type": "integer",
      "nullable": true,
      "description": "Nettoomsättning."
     },
     "operating_result": {
      "type": "integer",
      "nullable": true,
      "description": "Rörelseresultat."
     },
     "result_after_financial_items": {
      "type": "integer",
      "nullable": true,
      "description": "Resultat efter finansiella poster."
     },
     "net_result": {
      "type": "integer",
      "nullable": true,
      "description": "Årets resultat."
     },
     "equity": {
      "type": "integer",
      "nullable": true,
      "description": "Eget kapital."
     },
     "total_assets": {
      "type": "integer",
      "nullable": true,
      "description": "Summa tillgångar."
     },
     "employees": {
      "type": "integer",
      "nullable": true,
      "description": "Medelantal anställda."
     },
     "equity_ratio": {
      "type": "number",
      "nullable": true,
      "description": "Soliditet, per cent."
     },
     "report": {
      "type": "object",
      "nullable": true,
      "description": "Links to the report filed for this year, when it is in the files read so far.",
      "properties": {
       "preview": {
        "type": "string",
        "example": "/api/report/5565794400/2025-12-31"
       },
       "download": {
        "type": "string"
       }
      }
     }
    }
   }
  }
 }
}