openapi: 3.1.0
info:
  title: FöretagsAPI
  version: 1.0.0
  description: |
    Ett enkelt API för svensk företagsdata.
    
    Använd detta API för att söka på företagsnamn, organisationsnummer, och branschkoder (SNI).
    All data kommer från officiella svenska register.
  contact:
    name: FöretagsAPI Support
    url: https://foretagsapi.se
    email: support@foretagsapi.se
  license:
    name: Proprietary
    url: https://foretagsapi.se/terms-of-service

servers:
  - url: https://data.foretagsapi.se
    description: Production API

security:
  - bearerAuth: []

tags:
  - name: Search
    description: Sök efter företag på namn eller organisationsnummer
  - name: Bulk
    description: Slå upp flera företag samtidigt
  - name: SNI
    description: Sök och slå upp SNI-koder (branschkoder)
  - name: Health
    description: Kontrollera tjänstens status

paths:
  /v1/search:
    post:
      tags:
        - Search
      summary: Sök företag
      description: |
        Sök efter företag på namn (fuzzy-sökning) eller exakt organisationsnummer.
        
        **Kreditförbrukning:** 1 kredit per sökning (autentiserade användare)
        
        **Gästläge:** Max 5 resultat utan API-nyckel
      operationId: searchCompany
      security:
        - bearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/SearchByNameRequest'
                - $ref: '#/components/schemas/SearchByOrgNumberRequest'
            examples:
              searchByName:
                summary: Sök på företagsnamn
                value:
                  q: "Spotify AB"
                  limit: 10
              searchByOrgNumber:
                summary: Sök på organisationsnummer
                value:
                  org_number: "5567037485"
      responses:
        '200':
          description: Lyckad sökning
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchResponse'
              example:
                companies:
                  - id: 521294
                    name: "Spotify AB"
                    orgNumber: "5567037485"
                    legalForm: "AB-ORGFO"
                    postalAddress:
                      street: "Regeringsgatan 19"
                      postalCode: "11153"
                      city: "STOCKHOLM"
                    registrationDate: "2006-05-10"
                    deregistrationDate: null
                    deregistrationReason: null
                    businessDescription: "Bolaget har till föremål för sin verksamhet att bedriva Internetrelaterade tjänster inom digitala medier som musik, spel och TV..."
                    ongoingRestructuring: null
                    ftgstat: 1
                    jestat: 1
                    jurform: 49
                    reklamsparr: 1
                    sniCodes:
                      sni1: "63100"
                      sni2: null
                      sni3: null
                      sni4: null
                      sni5: null
                      sni1_name: null
                      sni2_name: null
                      sni3_name: null
                      sni4_name: null
                      sni5_name: null
                    score: 1.0
                metadata:
                  searchTerm: "Spotify AB"
                  searchType: "name"
                  resultCount: 1
                  processingTimeMs: 245
                  timestamp: "2026-01-15T10:30:00.000Z"
                  mode: "api"
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/bulk:
    post:
      tags:
        - Bulk
      summary: Bulkuppslag av företag
      description: |
        Slå upp flera företag samtidigt på namn. Använder fuzzy-matchning.
        
        **Kreditförbrukning:** 1 kredit per lyckat uppslag
        
        **Gästläge:** Max 10 företag utan API-nyckel
        
        **API-läge:** Max 100 företag per anrop
      operationId: bulkLookup
      security:
        - bearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkRequest'
            example:
              names:
                - "Spotify AB"
                - "Ericsson AB"
                - "Skanska AB"
      responses:
        '200':
          description: Lyckad bulkuppslag
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/sni/lookup:
    post:
      tags:
        - SNI
      summary: SNI-uppslag
      description: |
        Slå upp SNI-koder (branschkoder) på kod eller sök på beskrivning.
        Användbart för autocomplete i formulär.
        
        **Kreditförbrukning:** 1 kredit (autentiserade användare), gratis för gäster
      operationId: sniLookup
      security:
        - bearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SNILookupRequest'
            examples:
              searchByDescription:
                summary: Sök på beskrivning
                value:
                  q: "frisör"
                  limit: 10
              lookupByCode:
                summary: Slå upp exakt kod
                value:
                  code: "96210"
      responses:
        '200':
          description: Lyckad SNI-uppslag
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SNILookupResponse'
              example:
                sniCodes:
                  - code: "96210"
                    description: "Frisörer och barberare"
                metadata:
                  searchTerm: "frisör"
                  resultCount: 1
                  creditsCharged: 1
                  processingTimeMs: 45
                  timestamp: "2026-01-15T10:30:00.000Z"
                  mode: "api"
        '400':
          $ref: '#/components/responses/BadRequest'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/sni/search:
    post:
      tags:
        - SNI
      summary: Sök företag på SNI-kod
      description: |
        Sök efter företag som har en specifik SNI-kod (branschkod).
        Stöder filtrering på stad och paginering.
        
        **OBS:** Endast 5-siffriga subklasskoder fungerar (t.ex. 62010, inte 6201).
        
        **Kreditförbrukning:** 1 kredit per returnerat företag
        
        **Gästläge:** Max 5 resultat utan API-nyckel
      operationId: sniSearch
      security:
        - bearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SNISearchRequest'
            example:
              sni_code: "70200"
              city: "Stockholm"
              limit: 10
              offset: 0
              active_only: true
      responses:
        '200':
          description: Lyckad SNI-sökning
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SNISearchResponse'
        '400':
          description: Felaktig förfrågan eller för många resultat
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ErrorResponse'
                  - $ref: '#/components/schemas/ThresholdExceededResponse'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /health:
    get:
      tags:
        - Health
      summary: Hälsokontroll
      description: Kontrollera om API:et är tillgängligt. Kräver ingen autentisering.
      operationId: healthCheck
      security: []
      responses:
        '200':
          description: Tjänsten är tillgänglig
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
              example:
                status: "ok"
                timestamp: "2025-01-15T10:30:00.000Z"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Använd din API-nyckel med `fapi_` prefix.
        
        Exempel: `Authorization: Bearer fapi_abc123...`
        
        Skapa din API-nyckel på https://foretagsapi.se/dashboard

  schemas:
    # Request schemas
    SearchByNameRequest:
      type: object
      required:
        - q
      properties:
        q:
          type: string
          description: Företagsnamn att söka på (fuzzy-matchning)
          example: "Spotify AB"
        limit:
          type: integer
          minimum: 1
          maximum: 10
          default: 10
          description: Max antal resultat (gäster max 5, API max 10)

    SearchByOrgNumberRequest:
      type: object
      required:
        - org_number
      properties:
        org_number:
          type: string
          pattern: '^\d{10}$'
          description: 10-siffrigt organisationsnummer utan bindestreck
          example: "5567037485"

    BulkRequest:
      type: object
      required:
        - names
      properties:
        names:
          type: array
          items:
            type: string
          minItems: 1
          maxItems: 100
          description: Lista med företagsnamn att slå upp (gäster max 10, API max 100)
          example: ["Spotify AB", "Ericsson AB"]

    SNILookupRequest:
      type: object
      properties:
        q:
          type: string
          description: Sökterm för beskrivning eller kodprefix
          example: "frisör"
        code:
          type: string
          pattern: '^\d{5}$'
          description: Exakt 5-siffrig SNI-kod
          example: "96210"
        limit:
          type: integer
          minimum: 1
          maximum: 50
          default: 20
          description: Max antal resultat

    SNISearchRequest:
      type: object
      required:
        - sni_code
      properties:
        sni_code:
          type: string
          pattern: '^\d{5}$'
          description: 5-siffrig SNI-kod
          example: "70200"
        city:
          type: string
          description: Filtrera på stad
          example: "Stockholm"
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 100
          description: Max antal resultat (gäster max 5)
        offset:
          type: integer
          minimum: 0
          default: 0
          description: Offset för paginering (endast API-läge)
        active_only:
          type: boolean
          default: true
          description: Endast aktiva företag (ej avregistrerade)

    # Response schemas
    SearchResponse:
      type: object
      properties:
        companies:
          type: array
          items:
            $ref: '#/components/schemas/Company'
        metadata:
          $ref: '#/components/schemas/SearchMetadata'

    BulkResponse:
      type: object
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/BulkResultItem'
        metadata:
          $ref: '#/components/schemas/BulkMetadata'

    BulkResultItem:
      type: object
      properties:
        searchTerm:
          type: string
          description: Det sökta företagsnamnet
        company:
          oneOf:
            - $ref: '#/components/schemas/Company'
            - type: 'null'
          description: Matchande företag eller null
        success:
          type: boolean
          description: Om en matchning hittades
        error:
          type: string
          description: Felmeddelande om success är false

    SNILookupResponse:
      type: object
      properties:
        sniCodes:
          type: array
          items:
            $ref: '#/components/schemas/SNICode'
        metadata:
          $ref: '#/components/schemas/SNILookupMetadata'

    SNISearchResponse:
      type: object
      properties:
        companies:
          type: array
          items:
            $ref: '#/components/schemas/Company'
        metadata:
          $ref: '#/components/schemas/SNISearchMetadata'

    # Core data schemas
    Company:
      type: object
      description: Företagsobjekt med all tillgänglig information
      properties:
        id:
          type: integer
          description: Internt databas-ID
        name:
          type: string
          description: Officiellt registrerat företagsnamn
        orgNumber:
          type: string
          description: 10-siffrigt organisationsnummer utan bindestreck
        legalForm:
          type: string
          description: "Juridisk form som textkod. Vanliga värden: AB-ORGFO (Aktiebolag), EF-ORGFO (Enskild firma), HB-ORGFO (Handelsbolag), KB-ORGFO (Kommanditbolag)"
        postalAddress:
          $ref: '#/components/schemas/PostalAddress'
        registrationDate:
          type: string
          format: date
          description: Registreringsdatum (ISO-format)
        deregistrationDate:
          type:
            - string
            - 'null'
          format: date
          description: Avregistreringsdatum om avregistrerat
        deregistrationReason:
          type:
            - string
            - 'null'
          description: "Orsakskod vid avregistrering. Vanliga: KKAV-AVORG (konkursavslut), FUAV-AVORG (fusion), LIAV-AVORG (likvidation), AKEJH-AVORG (avförd ej registreringspliktig)"
        businessDescription:
          type: string
          description: Registrerad verksamhetsbeskrivning
        ongoingRestructuring:
          type:
            - string
            - 'null'
          description: "Kod för pågående avvecklings- eller rekonstruktionsförfarande. Vanliga prefix: LI=likvidation, KK=konkurs, FR=företagsrekonstruktion, FUOL/FUOT=fusion. Format: |KOD-AVOMFO$datum. Null om inget pågår."
        ftgstat:
          type:
            - integer
            - 'null'
          description: "F-skattestatus. 0=aldrig registrerad, 1=aktiv, 9=inaktiv"
        jestat:
          type:
            - integer
            - 'null'
          description: "Status för juridisk enhet (1=aktiv, null=ej tillgänglig)"
        jurform:
          type:
            - integer
            - 'null'
          description: "Juridisk formkod (numerisk). Vanliga värden: 10=Enskild firma, 31=Handelsbolag, 49=Aktiebolag, 51=Ekonomisk förening. Se även legalForm för textkod."
        reklamsparr:
          type:
            - integer
            - 'null'
          description: Reklamspärr (0=okänd, 1=ingen spärr, 2=spärrad)
        sniCodes:
          $ref: '#/components/schemas/SniCodes'
        score:
          type: number
          format: float
          minimum: 0
          maximum: 1
          description: Matchningspoäng (endast vid sökning)

    PostalAddress:
      type: object
      properties:
        street:
          type: string
          description: Gatuadress
        postalCode:
          type: string
          description: Postnummer
        city:
          type: string
          description: Ort

    SniCodes:
      type: object
      description: SNI-koder (branschkoder) med beskrivningar
      properties:
        sni1:
          type:
            - string
            - 'null'
          description: Primär SNI-kod
        sni2:
          type:
            - string
            - 'null'
          description: Sekundär SNI-kod
        sni3:
          type:
            - string
            - 'null'
        sni4:
          type:
            - string
            - 'null'
        sni5:
          type:
            - string
            - 'null'
        sni1_name:
          type:
            - string
            - 'null'
          description: Beskrivning av primär SNI-kod
        sni2_name:
          type:
            - string
            - 'null'
        sni3_name:
          type:
            - string
            - 'null'
        sni4_name:
          type:
            - string
            - 'null'
        sni5_name:
          type:
            - string
            - 'null'

    SNICode:
      type: object
      properties:
        code:
          type: string
          description: 5-siffrig SNI-kod
        description:
          type: string
          description: Beskrivning av branschkoden

    # Metadata schemas
    SearchMetadata:
      type: object
      properties:
        searchTerm:
          type: string
        searchType:
          type: string
          enum: [name, org_number]
        resultCount:
          type: integer
        processingTimeMs:
          type: integer
        timestamp:
          type: string
          format: date-time
        mode:
          type: string
          enum: [guest, api]

    BulkMetadata:
      type: object
      properties:
        processingTimeMs:
          type: integer
        totalSearches:
          type: integer
        successfulSearches:
          type: integer
        timestamp:
          type: string
          format: date-time
        mode:
          type: string
          enum: [guest, api]

    SNILookupMetadata:
      type: object
      properties:
        searchTerm:
          type: string
        resultCount:
          type: integer
        creditsCharged:
          type: integer
        processingTimeMs:
          type: integer
        timestamp:
          type: string
          format: date-time
        mode:
          type: string
          enum: [guest, api]

    SNISearchMetadata:
      type: object
      properties:
        sniCode:
          type: string
        sniDescription:
          type:
            - string
            - 'null'
        city:
          type:
            - string
            - 'null'
        resultCount:
          type: integer
        totalCount:
          type: integer
        offset:
          type: integer
        limit:
          type: integer
        hasMore:
          type: boolean
        creditsCharged:
          type: integer
        processingTimeMs:
          type: integer
        timestamp:
          type: string
          format: date-time
        mode:
          type: string
          enum: [guest, api]

    # Error schemas
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Felmeddelande

    ThresholdExceededResponse:
      type: object
      description: Returneras när SNI-sökning ger för många resultat
      properties:
        error:
          type: string
          example: "Threshold exceeded"
        message:
          type: string
          example: "SNI-koden 70200 har 85 393 företag. Lägg till ett stadsfilter för att begränsa resultaten."
        requiresCity:
          type: boolean
          example: true
        totalCount:
          type: integer
        sniCode:
          type: string

    HealthResponse:
      type: object
      properties:
        status:
          type: string
          enum: [ok]
        timestamp:
          type: string
          format: date-time

  responses:
    BadRequest:
      description: Felaktig förfrågan
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: "Missing required parameter: q (company name) or org_number"

    Unauthorized:
      description: Ogiltig eller saknad API-nyckel
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: "Invalid API key"

    PaymentRequired:
      description: Krediter slut
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: "Krediterna är slut. Uppgradera din plan eller köp fler krediter."

    TooManyRequests:
      description: För många förfrågningar (rate limit)
      headers:
        X-RateLimit-Tier:
          schema:
            type: string
          description: Din rate limit-nivå (Guest, Free, Starter, Pro)
        X-RateLimit-Limit:
          schema:
            type: integer
          description: Max förfrågningar per minut
        X-RateLimit-Remaining:
          schema:
            type: integer
          description: Återstående förfrågningar denna minut
        X-Quota-Remaining:
          schema:
            type: integer
          description: Återstående månadskrediter
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: "För många förfrågningar. Vänta en minut."

    InternalServerError:
      description: Serverfel
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: "Internal server error"
