openapi: 3.1.0
info:
  title: Compabase API
  version: 1.0.0
  description: |
    ### Introduction
    Versioned JSON API for Polish company data: KRS search and full profiles, CEIDG (sole
    proprietors) by NIP, financial statements and document inventory, peer statistics,
    company connections, news/UGC, Warsaw Stock Exchange (GPW) listings, and watchlist management.

    ### Base URL
    `https://compabase.com/api/v1/`

    ### Authentication
    Send your key in header `X-API-Key: <key>` or `Authorization: Bearer <key>`.

    API keys are self-service: create a free account, then generate and manage your keys in the
    <a href="/api-keys" target="_blank" rel="noopener noreferrer">API key dashboard</a>.
    Usage is billed according to your plan. See the
    <a href="/pricing" target="_blank" rel="noopener noreferrer">pricing page</a> for details.

    Authenticated requests return **contact email** on company profiles and search rows when
    available (same data as the logged-in web UI). Public unauthenticated site pages still gate emails.

    ### Error Model
    `{ "statusCode": 400, "statusMessage": "...", "message": "...", "data": { "error": { "code": "...", "message": "...", "details": { ... } } } }`

    The outer envelope (`statusCode`, `statusMessage`, `message`) is set by the Nitro server framework.
    The domain error object lives under `data.error` with a machine-readable `code`, a human-readable
    `message`, and optional `details`.

    ### Validation Rules
    - invalid numeric ranges (`*_min > *_max`) -> `400 INVALID_RANGE`
    - invalid booleans (`primary_only`, `has_email`, `has_website`, `status_active`) -> `400 INVALID_BOOLEAN`
    - invalid cursor UUID -> `400 INVALID_CURSOR`
    - invalid KRS format -> `400 INVALID_KRS`
    - invalid NIP format -> `400 INVALID_NIP`
    - too-short keyword token (<2 chars) -> `400 INVALID_KEYWORDS`
    - mutually exclusive parameters supplied together (e.g. `cursor` and `offset`) -> `400 CONFLICTING_PARAMETERS`

    ### Quota Accounting
    Each key is tied to your account plan and has a monthly `api_requests` limit that resets
    with the UTC calendar month (see `GET /usage` for `monthUtc` / `resetsAtUtc`). Limits by
    plan: Free 100, Pro 5 000, Scale 100 000, Enterprise – custom.

    - quota is consumed by authenticated `/api/v1` requests, except `GET /usage`
    - requests that fail before authentication succeeds (`401`) do not consume quota
    - requests rejected because quota is already exhausted (`429`) do not consume additional quota
    - other authenticated responses, including `200`, `400`, and `404`, consume quota

    ### Bulk pulls
    Use `GET /companies/export` (or `GET /companies` with `limit` up to **500**) plus cursor
    pagination (`cursor` = last `entity_id`) to enrich large lists. Each page consumes **one**
    `api_requests` unit — not the portal/MCP `export_companies` meter. For a full profile,
    call `GET /companies/krs/{krs}` once per company.

servers:
- url: https://compabase.com/api/v1
  description: Full URL
tags:
- name: Health
- name: Usage
- name: Companies
- name: Watchlist
- name: Persons
paths:
  /health:
    get:
      tags:
      - Health
      summary: API v1 health
      description: Public uptime/status endpoint for API v1 (no API key required).
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  api:
                    type: string
                    example: v1
                  timestamp:
                    type: string
                    format: date-time
                    example: '2026-03-25T12:00:00Z'
  /companies:
    get:
      tags:
      - Companies
      summary: Search and list companies
      description: |
        Uses the same search dataset as the public site. Filters combine with **AND** where applicable.

        **Response:** `data` is one page of rows; `pagination` has `limit`, `offset`, `total`, `hasMore`, and optionally `cursor` when cursor-based paging is used.

        For a single company’s full profile, use `GET /companies/krs/{krs}` (KRS court number).

        Parameter details are documented on each query field below.

        **Pagination mode**

        `offset` and `cursor` are mutually exclusive in client usage. If both are sent, the server returns `400 CONFLICTING_PARAMETERS`.

        Use `offset` for shallow interactive browsing and `cursor` for deep paging/export flows.

        Cursor pagination is ordered by `entity_id` ascending; `cursor` is the last `entity_id` from the prior page.

        Because datasets can change between requests, deep paging may still observe occasional duplicates or skips.

        With cursor mode, `pagination.total` may be unavailable and returned as `-1`; rely on `hasMore`.

        **Default ordering (offset mode)**

        - without `q`: `has_email` desc, then `has_website` desc, then `revenue_total` desc, then `extracted_at` desc, then `entity_id` asc.
        - with `q`: same ordering as above (not TF-IDF/full-text relevance ranking).
        - offset pagination is intended for interactive browsing; for deterministic deep traversal use cursor mode.

        **Financial filter semantics** (for `/companies` and `/companies/count`)

        - financial range filters are evaluated on the latest indexed financial snapshot per company (search index row).
        - companies with `null` value for a filtered metric do not match that metric filter.
        - when `last_report_year_*` is combined with financial metric filters, both are evaluated against the same indexed reporting snapshot.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - $ref: '#/components/parameters/CompanyFilterQ'
      - $ref: '#/components/parameters/CompanyFilterKeywords'
      - $ref: '#/components/parameters/CompanyFilterLimit'
      - $ref: '#/components/parameters/CompanyFilterOffset'
      - $ref: '#/components/parameters/CompanyFilterCursor'
      - $ref: '#/components/parameters/CompanyFilterPkd'
      - $ref: '#/components/parameters/CompanyFilterPrimaryOnly'
      - $ref: '#/components/parameters/CompanyFilterCurrency'
      - $ref: '#/components/parameters/CompanyFilterVoivodeship'
      - $ref: '#/components/parameters/CompanyFilterPostalCode'
      - $ref: '#/components/parameters/CompanyFilterCity'
      - $ref: '#/components/parameters/CompanyFilterRevenueMin'
      - $ref: '#/components/parameters/CompanyFilterRevenueMax'
      - $ref: '#/components/parameters/CompanyFilterRevenueOperatingMin'
      - $ref: '#/components/parameters/CompanyFilterRevenueOperatingMax'
      - $ref: '#/components/parameters/CompanyFilterProfitOperatingMin'
      - $ref: '#/components/parameters/CompanyFilterProfitOperatingMax'
      - $ref: '#/components/parameters/CompanyFilterProfitNetMin'
      - $ref: '#/components/parameters/CompanyFilterProfitNetMax'
      - $ref: '#/components/parameters/CompanyFilterIncomeTaxMin'
      - $ref: '#/components/parameters/CompanyFilterIncomeTaxMax'
      - $ref: '#/components/parameters/CompanyFilterCostWagesMin'
      - $ref: '#/components/parameters/CompanyFilterCostWagesMax'
      - $ref: '#/components/parameters/CompanyFilterCostAmortizationMin'
      - $ref: '#/components/parameters/CompanyFilterCostAmortizationMax'
      - $ref: '#/components/parameters/CompanyFilterTotalAssetsMin'
      - $ref: '#/components/parameters/CompanyFilterTotalAssetsMax'
      - $ref: '#/components/parameters/CompanyFilterEbitdaMin'
      - $ref: '#/components/parameters/CompanyFilterEbitdaMax'
      - $ref: '#/components/parameters/CompanyFilterCapitalMin'
      - $ref: '#/components/parameters/CompanyFilterCapitalMax'
      - $ref: '#/components/parameters/CompanyFilterEstimatedValueMin'
      - $ref: '#/components/parameters/CompanyFilterEstimatedValueMax'
      - $ref: '#/components/parameters/CompanyFilterRegistrationYearMin'
      - $ref: '#/components/parameters/CompanyFilterRegistrationYearMax'
      - $ref: '#/components/parameters/CompanyFilterRegistrationDateMin'
      - $ref: '#/components/parameters/CompanyFilterRegistrationDateMax'
      - $ref: '#/components/parameters/CompanyFilterLastReportYearMin'
      - $ref: '#/components/parameters/CompanyFilterLastReportYearMax'
      - $ref: '#/components/parameters/CompanyFilterStatusActive'
      - $ref: '#/components/parameters/CompanyFilterLegalForm'
      - $ref: '#/components/parameters/CompanyFilterHasEmail'
      - $ref: '#/components/parameters/CompanyFilterHasWebsite'
      - $ref: '#/components/parameters/CompanySort'
      - $ref: '#/components/parameters/CompanySortDir'
      responses:
        '200':
          description: 'Paginated search results: **`data`** is an array of companies (one object per row on this page).

            Fields per row depend on filters and internal path; optional keys may be omitted or null.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyListResponse'
              example:
                data:
                - entity_id: ad2ae683-ccbd-49c6-a85c-648024435cb5
                  company_name: ORLEN SPÓŁKA AKCYJNA
                  company_name_display: Orlen
                  nip: null
                  regon: null
                  registry_number: '0000028860'
                  legal_form: SPÓŁKA AKCYJNA
                  is_active: true
                  latest_snapshot_id: 1956bcf0-34f4-4ab1-98af-eddf75c0d40a
                  extracted_at: '2026-03-19T13:41:20.998732+00:00'
                  state_date: null
                  postal_code: 09-411
                  city: PŁOCK
                  region: mazowieckie
                  revenue_total: 304668000000
                  revenue_total_usd: 76338762214.98372
                  profit_net: 1383000000
                  profit_net_usd: 346529691.80656475
                  operating_costs_total: 301809000000
                  operating_costs_total_usd: 75622400400.90202
                  total_assets: 255368000000
                  total_assets_usd: 63985968428.96517
                  fixed_assets: 186761000000
                  fixed_assets_usd: 46795539964.921074
                  current_assets: 68607000000
                  current_assets_usd: 17190428464.044098
                  equity: 146689000000
                  equity_usd: 36754948634.42746
                  liabilities_and_provisions: 108679000000
                  liabilities_and_provisions_usd: 27231019794.53771
                  has_email: true
                  has_website: true
                  full_address: 09-411 Płock, Poland
                  email: zarzad@orlen.pl
                  website: orlen.pl
                  logo_url: https://db.compabase.com/storage/v1/object/public/company-logos/ad2ae683-ccbd-49c6-a85c-648024435cb5.ico
                  currency: PLN
                  period_to: null
                - entity_id: e51db361-6d35-4e91-af8c-5cb8115e1475
                  company_name: TAURON POLSKA ENERGIA SPÓŁKA AKCYJNA
                  company_name_display: Tauron Polska Energia
                  nip: null
                  regon: null
                  registry_number: '0000271562'
                  legal_form: SPÓŁKA AKCYJNA
                  is_active: true
                  latest_snapshot_id: 3742a994-dfd1-49a7-bb76-033bf524373d
                  extracted_at: '2026-03-16T23:08:00.733285+00:00'
                  state_date: null
                  postal_code: 40-202
                  city: KATOWICE
                  region: śląskie
                  revenue_total: 35573000000
                  revenue_total_usd: 8913304936.10624
                  profit_net: 590000000
                  profit_net_usd: 147832623.402656
                  operating_costs_total: 32922000000
                  operating_costs_total_usd: 8249060385.868203
                  total_assets: 45714000000
                  total_assets_usd: 11454272112.25257
                  fixed_assets: 38069000000
                  fixed_assets_usd: 9538712102.230017
                  current_assets: 7645000000
                  current_assets_usd: 1915560010.0225508
                  equity: 17754000000
                  equity_usd: 4448509145.57755
                  liabilities_and_provisions: 27960000000
                  liabilities_and_provisions_usd: 7005762966.675019
                  has_email: true
                  has_website: true
                  full_address: 40-202 Katowice, Poland
                  email: kancelaria@tauron.pl
                  website: tauron.pl
                  logo_url: https://db.compabase.com/storage/v1/object/public/company-logos/e51db361-6d35-4e91-af8c-5cb8115e1475.ico
                  currency: PLN
                  period_to: null
                - entity_id: 264b49ba-4c4d-4a04-9eb1-1010a57fecdf
                  company_name: GRUPA LOTOS SPÓŁKA AKCYJNA
                  company_name_display: Grupa Lotos
                  nip: '5830000960'
                  regon: '19054163600000'
                  registry_number: '0000106150'
                  legal_form: SPÓŁKA AKCYJNA
                  is_active: true
                  latest_snapshot_id: a368e255-5022-4f02-998b-379609886f51
                  extracted_at: '2025-12-13T21:23:49.997737+00:00'
                  state_date: '2025-11-20'
                  postal_code: 80-718
                  city: GDAŃSK
                  region: pomorskie
                  revenue_total: 33616400000
                  revenue_total_usd: 8697870578.798935
                  profit_net: 3211800000
                  profit_net_usd: 831017620.1195374
                  operating_costs_total: 28871200000
                  operating_costs_total_usd: 7470102719.345908
                  total_assets: 25964800000
                  total_assets_usd: 6718103961.29266
                  fixed_assets: 14525600000
                  fixed_assets_usd: 3758337861.2641983
                  current_assets: 11439200000
                  current_assets_usd: 2959766100.0284615
                  equity: 14793900000
                  equity_usd: 3827757509.8967633
                  liabilities_and_provisions: 11170900000
                  liabilities_and_provisions_usd: 2890346451.3958964
                  has_email: true
                  has_website: true
                  full_address: 80-718 Gdańsk, Poland
                  email: lotos@grupalotos.pl
                  website: lotos.pl
                  logo_url: https://db.compabase.com/storage/v1/object/public/company-logos/264b49ba-4c4d-4a04-9eb1-1010a57fecdf.png
                  currency: PLN
                  period_to: null
                pagination:
                  limit: 3
                  offset: 0
                  total: 1045904
                  hasMore: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/count:
    get:
      tags:
      - Companies
      summary: Count companies for current filters
      description: |
        Returns how many companies match the same **filter set** as `GET /companies`, without returning rows.
        Use the **same query parameters** as list/search (except pagination: no `limit`, `offset`, or `cursor`).
        Filters combine with **AND** where applicable.

        **Response:** a single integer `count`; total matching companies for the current filters.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - $ref: '#/components/parameters/CompanyFilterQ'
      - $ref: '#/components/parameters/CompanyFilterKeywords'
      - $ref: '#/components/parameters/CompanyFilterPkd'
      - $ref: '#/components/parameters/CompanyFilterPrimaryOnly'
      - $ref: '#/components/parameters/CompanyFilterCurrency'
      - $ref: '#/components/parameters/CompanyFilterVoivodeship'
      - $ref: '#/components/parameters/CompanyFilterPostalCode'
      - $ref: '#/components/parameters/CompanyFilterCity'
      - $ref: '#/components/parameters/CompanyFilterRevenueMin'
      - $ref: '#/components/parameters/CompanyFilterRevenueMax'
      - $ref: '#/components/parameters/CompanyFilterRevenueOperatingMin'
      - $ref: '#/components/parameters/CompanyFilterRevenueOperatingMax'
      - $ref: '#/components/parameters/CompanyFilterProfitOperatingMin'
      - $ref: '#/components/parameters/CompanyFilterProfitOperatingMax'
      - $ref: '#/components/parameters/CompanyFilterProfitNetMin'
      - $ref: '#/components/parameters/CompanyFilterProfitNetMax'
      - $ref: '#/components/parameters/CompanyFilterIncomeTaxMin'
      - $ref: '#/components/parameters/CompanyFilterIncomeTaxMax'
      - $ref: '#/components/parameters/CompanyFilterCostWagesMin'
      - $ref: '#/components/parameters/CompanyFilterCostWagesMax'
      - $ref: '#/components/parameters/CompanyFilterCostAmortizationMin'
      - $ref: '#/components/parameters/CompanyFilterCostAmortizationMax'
      - $ref: '#/components/parameters/CompanyFilterTotalAssetsMin'
      - $ref: '#/components/parameters/CompanyFilterTotalAssetsMax'
      - $ref: '#/components/parameters/CompanyFilterEbitdaMin'
      - $ref: '#/components/parameters/CompanyFilterEbitdaMax'
      - $ref: '#/components/parameters/CompanyFilterCapitalMin'
      - $ref: '#/components/parameters/CompanyFilterCapitalMax'
      - $ref: '#/components/parameters/CompanyFilterEstimatedValueMin'
      - $ref: '#/components/parameters/CompanyFilterEstimatedValueMax'
      - $ref: '#/components/parameters/CompanyFilterRegistrationYearMin'
      - $ref: '#/components/parameters/CompanyFilterRegistrationYearMax'
      - $ref: '#/components/parameters/CompanyFilterRegistrationDateMin'
      - $ref: '#/components/parameters/CompanyFilterRegistrationDateMax'
      - $ref: '#/components/parameters/CompanyFilterLastReportYearMin'
      - $ref: '#/components/parameters/CompanyFilterLastReportYearMax'
      - $ref: '#/components/parameters/CompanyFilterStatusActive'
      - $ref: '#/components/parameters/CompanyFilterLegalForm'
      - $ref: '#/components/parameters/CompanyFilterHasEmail'
      - $ref: '#/components/parameters/CompanyFilterHasWebsite'
      responses:
        '200':
          description: Total number of companies matching the request filters.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyCountResponse'
              example:
                count: 1045904
        '401':
          $ref: '#/components/responses/Unauthorized'
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}:
    get:
      tags:
      - Companies
      summary: Full company profile
      description: |
        Returns full company profile addressed by **KRS** (Polish court registry number).
        Digits only; leading zeros are optional (normalized to 10 digits server-side).

        Integrations that only have a KRS number can call this directly.
        One request typically covers enrichment for a company. The payload includes:
        - `company` (identifiers, address, PKD, logo, capital),
        - `details.contacts` (email, phone, website, deliverability flags; email/phone revealed for API keys),
        - `details.vat` (VAT whitelist status + bank accounts when known),
        - `details.filings` / attributes / operational status,
        - `roles` and `ownership`,
        - `financials.byYear` (metrics + `esf_statement_lines` when present; XML files are on the documents endpoint),
        - `krz` (restructuring/insolvency proceedings summary),
        - `sanctions` (MSWiA sanctions listing with measures and grounds; omitted when the company is not listed; dedicated read is `/companies/krs/{krs}/sanctions`),
        - `sudop` (public aid / de minimis when checked),
        - `bzp` (public procurement summary when present),
        - `ure` (URE energy concessions when checked),
        - `bdo` (BDO waste/packaging register when present),
        - `gpw` (Warsaw Stock Exchange listing snapshot when the NIP is matched — no full OHLCV; use `/companies/krs/{krs}/gpw`),
        - `crbr` (beneficial owners when present and RODO-eligible),
        - `changeHistory` when non-empty,
        - `similarCompanies`, `subsidiaryCompanies`, `pkdRankings`, `faq`, `insightsByYear`,
        - `hasStatistics` (boolean; full peer benchmarks via `/statistics`).
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        description: KRS input is normalized by stripping non-digits and left-padding to 10 digits.
          After normalization, KRS must contain exactly 10 digits; otherwise request fails with `400 INVALID_KRS`.
        example: "0000028860"
      responses:
        '200':
          description: Full company profile object resolved from KRS.
          content:
            application/json:
              schema:
                type: object
              example:
                company:
                  entity_id: ad2ae683-ccbd-49c6-a85c-648024435cb5
                  registry_number: "0000028860"
                  company_name: ORLEN SPÓŁKA AKCYJNA
                  company_name_display: Orlen
                  logo_url: https://db.compabase.com/storage/v1/object/public/company-logos/ad2ae683-ccbd-49c6-a85c-648024435cb5.ico
                  nip: "7740001454"
                  regon: "61018820100000"
                  full_address: CHEMIKÓW 7, 09-411 Płock, PL
                  street: CHEMIKÓW
                  building_no: "7"
                  city: PŁOCK
                  region: mazowieckie
                  country: PL
                  legal_form: SPÓŁKA AKCYJNA
                  activities:
                  - code_full: "19.20.Z"
                    is_primary: true
                    description: Manufacture of refined petroleum products
                  - code_full: "20.1"
                    is_primary: false
                    description: Manufacture of basic chemicals, fertilisers and nitrogen compounds, plastics and synthetic rubber in primary forms
                  - code_full: "35.1"
                    is_primary: false
                    description: Electric power generation, transmission, distribution and trade
                details:
                  legalForm: SPÓŁKA AKCYJNA
                  operational_status: active
                  contacts:
                    email: zarzad@orlen.pl
                    emailGated: false
                    phone: null
                    phoneGated: false
                    website: orlen.pl
                    emailDeliverabilityVerified: true
                    emailInactive: false
                    websiteInactive: false
                  vat:
                    status: Czynny
                    status_date: "2024-03-01"
                    accounts:
                    - "25114020040000380284611780"
                  filings: []
                companySummary:
                  pl: ORLEN S.A. to jedna z największych zintegrowanych grup multienergetycznych w Europie Środkowej.
                  en: ORLEN S.A. is one of the largest integrated multi-energy groups in Central Europe.
                  de: ORLEN S.A. ist eine der größten integrierten Multi-Energie-Gruppen in Mitteleuropa.
                roles:
                - party:
                    name: "ALEJANDRO  MARTINEZ RUIZ"
                    display_name: Alejandro Martinez Ruiz
                  role_name: President of the Management Board
                - party:
                    name: MARCIN ANDRZEJ DREKSLER
                    display_name: Marcin Andrzej Dreksler
                  role_name: Management Board Member
                - party:
                    name: "IVÁN  LIRÓN PORTILLA"
                    display_name: Iván Lirón Portilla
                  role_name: Management Board Member
                ownership:
                - owner:
                    name: RYSZARD CIEŚLAR
                    type: person
                    display_name: Ryszard Cieślar
                  holding_text: 100 shares with a total value of PLN 5,000.00
                  has_all_shares: true
                  holding_percent: 100
                  holding_value_pln: 5000
                  holding_value_usd: 1350.621285791464
                  holding_value_eur: 1204.2389210019267
                  holding_share_count: 100
                financials:
                  years:
                  - "2024"
                  - "2023"
                  - "2022"
                  byYear:
                    "2024":
                    - sf_period_from: "2024-01-01"
                      sf_period_to: "2024-12-31"
                      currency: PLN
                      roa: 0.5415713793427525
                      asset_turnover: 1.1930547288618778
                      revenue_total: 304668000000
                      revenue_total_usd: 76338762214.98372
                      revenue_total_eur: 70672233820.45929
                      profit_net: 1383000000
                      profit_net_usd: 346529691.80656475
                      profit_net_eur: 320807237.2999304
                      total_assets: 255368000000
                      total_assets_usd: 63985968428.96517
                      total_assets_eur: 59236372071.44514
                      fixed_assets_eur: 43321967061.00673
                      current_assets_eur: 15914405010.438414
                      equity_eur: 34026675945.25632
                      liabilities_and_provisions_eur: 25209696126.18882
                    "2023":
                    - sf_period_from: "2023-01-01"
                      sf_period_to: "2023-12-31"
                      currency: PLN
                      roa: 7.845846361165577
                      asset_turnover: 1.4862592645867558
                      revenue_total: 392637000000
                      revenue_total_usd: 93438280859.57022
                      revenue_total_eur: 86430615479.43999
                      profit_net: 20727000000
                      profit_net_usd: 4932533733.133433
                      profit_net_eur: 4562604561.063662
                      total_assets: 264178000000
                      total_assets_usd: 62868089764.64149
                      total_assets_eur: 58153121422.91098
                      fixed_assets_eur: 37596856564.23351
                      current_assets_eur: 20556264858.677467
                      equity_eur: 33719292066.566875
                      liabilities_and_provisions_eur: 24433829356.344105
                      has_esf_xml: true
                      esf_statement_lines:
                        rzis:
                          A: 304668000000
                        bilans:
                          Aktywa: 255368000000
                        statement_profile:
                          bilans_form: jednostka_inna
                          rzis_layout: jednostka_inna
                hasStatistics: true
                krz:
                  checked_at: "2026-07-18T03:00:49.323Z"
                  has_proceedings: false
                  has_active_proceedings: false
                  proceedings_count: 0
                  proceedings: []
                  source_url: https://krz.ms.gov.pl/
                similarCompanies:
                - entity_id: 39c4a3d7-144c-45c2-9713-ecd3e8895d4a
                  registry_number: "0000287699"
                  company_name: WARTER FUELS SPÓŁKA AKCYJNA
                  company_name_display: Warter Fuels
                  legal_form: SPÓŁKA AKCYJNA
                  website: warterfuels.pl
                  logo_url: https://db.compabase.com/storage/v1/object/public/company-logos/39c4a3d7-144c-45c2-9713-ecd3e8895d4a.png
                  revenue_total: 224545986
                  revenue_total_usd: 56263088.449010275
                  revenue_total_eur: 52086751.565762006
                  revenue_operating: 223609024
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}/financial-statements:
    get:
      tags:
      - Companies
      summary: Financial statements
      description: |
        Returns financial metrics only (without company profile or document files),
        grouped by statement year. Values include PLN/USD/EUR columns from
        `entity_financial_metrics` and structured `esf_statement_lines` when extracted.
        XML files are on the documents endpoint.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        description: KRS input is normalized by stripping non-digits and left-padding to 10 digits.
          After normalization, KRS must contain exactly 10 digits; otherwise request fails with `400 INVALID_KRS`.
        example: "0000028860"
      responses:
        '200':
          description: Financial metrics grouped by year.
          content:
            application/json:
              schema:
                type: object
              example:
                financials:
                  years:
                  - "2024"
                  - "2023"
                  byYear:
                    "2024":
                    - sf_period_from: "2024-01-01"
                      sf_period_to: "2024-12-31"
                      currency: PLN
                      ebit: 2484000000
                      ebitda: 0
                      roa: 0.5415713793427525
                      net_margin: 0.4538198626668145
                      asset_turnover: 1.1930547288618778
                      revenue_operating: 303192000000
                      revenue_total: 304668000000
                      operating_costs_total: 301809000000
                      financial_income: 1476000000
                      financial_costs: 1908000000
                      profit_gross: 1571000000
                      income_tax: null
                      revenue_total_usd: 76338762214.98372
                      revenue_total_eur: 70672233820.45929
                      revenue_operating_usd: 75969071685.3297
                      revenue_operating_eur: 70329936094.86815
                      operating_costs_total_usd: 75622400400.90202
                      operating_costs_total_eur: 70009128857.56822
                      profit_net: 1383000000
                      profit_net_usd: 346529691.80656475
                      profit_net_eur: 320807237.2999304
                      financial_income_usd: 369869938.4584869
                      financial_income_eur: 342297725.591141
                      financial_costs_usd: 478544714.9568987
                      financial_costs_eur: 442881221.5274784
                      total_assets: 255368000000
                      total_assets_usd: 63985968428.96517
                      total_assets_eur: 59236372071.44514
                      fixed_assets_eur: 43321967061.00673
                      current_assets_eur: 15914405010.438414
                      equity_eur: 34026675945.25632
                      liabilities_and_provisions_eur: 25209696126.18882
                    "2023":
                    - sf_period_from: "2023-01-01"
                      sf_period_to: "2023-12-31"
                      currency: PLN
                      roa: 7.845846361165577
                      asset_turnover: 1.4862592645867558
                      revenue_operating: 389584000000
                      revenue_total: 392637000000
                      operating_costs_total: 362463000000
                      profit_gross: 26365000000
                      income_tax: null
                      revenue_total_usd: 93438280859.57022
                      revenue_total_eur: 86430615479.43999
                      revenue_operating_usd: 92711915658.9751
                      revenue_operating_eur: 85758485784.99783
                      operating_costs_total_usd: 86223593016.49176
                      operating_costs_total_eur: 79759037499.87363
                      profit_net: 20727000000
                      profit_net_usd: 4932533733.133433
                      profit_net_eur: 4562604561.063662
                      total_assets: 264178000000
                      total_assets_usd: 62868089764.64149
                      total_assets_eur: 58153121422.91098
                      fixed_assets_eur: 37596856564.23351
                      current_assets_eur: 20556264858.677467
                      equity_eur: 33719292066.566875
                      liabilities_and_provisions_eur: 24433829356.344105
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}/structure-people:
    get:
      tags:
      - Companies
      summary: Structure & People
      description: |
        Returns ownership and management structure only (without profile or financial data):
        `roles` and `ownership`.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        description: KRS input is normalized by stripping non-digits and left-padding to 10 digits.
          After normalization, KRS must contain exactly 10 digits; otherwise request fails with `400 INVALID_KRS`.
        example: "0000028860"
      responses:
        '200':
          description: Roles and ownership structure.
          content:
            application/json:
              schema:
                type: object
              example:
                roles:
                - party:
                    name: "ALEJANDRO  MARTINEZ RUIZ"
                    display_name: Alejandro Martinez Ruiz
                  role_name: President of the Management Board
                - party:
                    name: MARCIN ANDRZEJ DREKSLER
                    display_name: Marcin Andrzej Dreksler
                  role_name: Management Board Member
                ownership:
                - owner:
                    name: RYSZARD CIEŚLAR
                    type: person
                    display_name: Ryszard Cieślar
                  holding_text: 100 shares with a total value of PLN 5,000.00
                  has_all_shares: true
                  holding_percent: 100
                  holding_value_pln: 5000
                  holding_value_usd: 1350.621285791464
                  holding_value_eur: 1204.2389210019267
                  holding_share_count: 100
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}/connections:
    get:
      tags:
      - Companies
      summary: Company connections
      description: |
        Graph of related companies and people linked through shared roles/ownership
        (same payload as the profile Connections UI).
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        description: KRS input is normalized by stripping non-digits and left-padding to 10 digits.
        example: "0000028860"
      responses:
        '200':
          description: Connections payload.
          content:
            application/json:
              schema:
                type: object
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}/statistics:
    get:
      tags:
      - Companies
      summary: Peer statistics & rankings
      description: |
        Industry / region / country peer benchmarks and the company's rank positions.
        Use when `hasStatistics` on the full profile is `true`.

        Response shape:
        - `company` – latest indexed figures (PLN/EUR/USD) for the company
        - `scopes` – rank in each peer group (`pkd_full`, `pkd_division`, `pkd_section`, `region`, `country`)
        - `benchmarks` – peer distribution per scope key (`median`, `p25`/`p75`/`p90`, `n_companies`, …)
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        example: "0001088645"
      - name: locale
        in: query
        required: false
        schema:
          type: string
          default: en
      responses:
        '200':
          description: |
            Rankings (`scopes`) plus peer distributions (`benchmarks` keyed by
            `scope_type::scope_key`). Company figures are the latest indexed snapshot.
          content:
            application/json:
              schema:
                type: object
              example:
                hasData: true
                company:
                  registry_number: "0001088645"
                  primary_pkd: "73.11.Z"
                  region: wielkopolskie
                  period_to: "2025-12-31"
                  revenue_total:
                    PLN: 723679.14
                    EUR: 171325.55
                    USD: 181737.60
                  profit_net:
                    PLN: -220414.61
                    EUR: -52181.49
                    USD: -55352.74
                  total_assets:
                    PLN: 1132090.39
                    EUR: 268013.82
                    USD: 284301.96
                  operating_costs_total:
                    PLN: 932271.28
                    EUR: 220708.16
                    USD: 234121.37
                scopes:
                - scope_type: pkd_full
                  scope_key: "73.11.Z"
                  label: Advertising agencies
                  rank_position: 2811
                  total_in_scope: 6860
                  top_percent: 40.98
                  period_to: "2025-12-31"
                - scope_type: pkd_division
                  scope_key: "73"
                  label: Advertising, market research and public relations
                  rank_position: 3104
                  total_in_scope: 7518
                  top_percent: 41.29
                  period_to: "2025-12-31"
                - scope_type: pkd_section
                  scope_key: N
                  label: "N – Professional, scientific and technical activities"
                  rank_position: 8606
                  total_in_scope: 21505
                  top_percent: 40.02
                  period_to: "2025-12-31"
                - scope_type: region
                  scope_key: wielkopolskie
                  label: Wielkopolskie
                  rank_position: 24802
                  total_in_scope: 47950
                  top_percent: 51.72
                  period_to: "2025-12-31"
                - scope_type: country
                  scope_key: PL
                  label: Polska
                  rank_position: 237072
                  total_in_scope: 479838
                  top_percent: 49.41
                  period_to: "2025-12-31"
                benchmarks:
                  pkd_full::73.11.Z:
                    label: Advertising agencies
                    metrics:
                      revenue_total:
                        PLN:
                          n_companies: 6859
                          median: 437040.24
                          mean: 4143982.87
                          winsor_mean: 2544201.61
                          p25: 108193.55
                          p75: 1894511.26
                          p90: 6629544.52
                        EUR:
                          n_companies: 6857
                          median: 101158.92
                          mean: 963908.02
                          winsor_mean: 590328.70
                          p25: 25095.97
                          p75: 437657.96
                          p90: 1533970.41
                      profit_net:
                        PLN:
                          n_companies: 6859
                          median: 15200.40
                          mean: 288817.10
                          winsor_mean: 160485.18
                          p25: -6171.84
                          p75: 118829.62
                          p90: 495890.18
                      total_assets:
                        PLN:
                          n_companies: 6811
                          median: 230483.30
                          mean: 2547678.84
                          winsor_mean: 1277411.54
                          p25: 52561.04
                          p75: 930692.26
                          p90: 3296511.39
                  country::PL:
                    label: Polska
                    metrics:
                      revenue_total:
                        PLN:
                          n_companies: 479837
                          median: 695181.35
                          mean: 15874161.59
                          winsor_mean: 6549627.07
                          p25: 104280.08
                          p75: 3937961.66
                          p90: 16531070.23
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}/financial-documents:
    get:
      tags:
      - Companies
      summary: Financial document inventory
      description: |
        Metadata list of financial documents for the company (periods, types, names).
        Returns metadata only. Structured metrics and `esf_statement_lines` are on the
        profile and financial-statements endpoints.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        example: "0000028860"
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: Document metadata page.
          content:
            application/json:
              schema:
                type: object
              example:
                krs: "0000028860"
                entity_id: ad2ae683-ccbd-49c6-a85c-648024435cb5
                documents:
                - id: abe77c20-f518-4f20-a57a-a78cae7e135d
                  document_type_ui: Dokument RDF (rodzaj 18)
                  period_to: "2024-12-31"
                  files: []
                pagination:
                  docsLimit: 25
                  docsOffset: 0
                  hasMore: false
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}/news:
    get:
      tags:
      - Companies
      summary: Company news & UGC
      description: |
        Published news articles and optional claimed description / logo from company UGC.
        Coverage is uneven (only companies with portal/UGC content).
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        example: "0000028860"
      - name: locale
        in: query
        required: false
        schema:
          type: string
          default: en
      responses:
        '200':
          description: News and optional UGC fields (empty arrays/nulls when none).
          content:
            application/json:
              schema:
                type: object
              example:
                krs: "0001088645"
                entity_id: "00000000-0000-0000-0000-000000000000"
                description: null
                logo_url: null
                newsArticles: []
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}/sanctions:
    get:
      tags:
      - Companies
      summary: MSWiA sanctions listing
      description: |
        Official Polish Ministry of the Interior and Administration (MSWiA) sanctions listing
        for a KRS company matched by KRS, NIP, or REGON.

        `sanctions` is `null` when the company is not on the list. When present, `entries`
        include the name on the list, listed and removed dates, restrictive measures, and
        the legal grounds. Polish text stays in `measures` / `justification`; English and
        German are in `measures_i18n` / `justification_i18n`.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        description: KRS input is normalized by stripping non-digits and left-padding to 10 digits.
      responses:
        '200':
          description: Sanctions payload. `sanctions` is null when the company is not listed.
          content:
            application/json:
              schema:
                type: object
                required: [krs, entity_id]
                properties:
                  krs:
                    type: string
                  entity_id:
                    type: string
                    format: uuid
                  sanctions:
                    nullable: true
                    type: object
                    properties:
                      entries:
                        type: array
                        items:
                          type: object
                          properties:
                            source:
                              type: string
                              example: pl_mswia
                            name:
                              type: string
                            measures:
                              type: string
                              nullable: true
                              description: Restrictive measures, Polish source text. Lettered items are separated by newlines.
                            justification:
                              type: string
                              nullable: true
                              description: Legal grounds, Polish source text. Paragraphs are separated by a blank line.
                            measures_i18n:
                              type: object
                              nullable: true
                              properties:
                                en:
                                  type: string
                                de:
                                  type: string
                            justification_i18n:
                              type: object
                              nullable: true
                              properties:
                                en:
                                  type: string
                                de:
                                  type: string
                            listed_on:
                              type: string
                              format: date
                              nullable: true
                            removed_on:
                              type: string
                              format: date
                              nullable: true
                            source_url:
                              type: string
                              nullable: true
              example:
                krs: "0000795863"
                entity_id: "58142114-9193-42c0-a7e3-1ed189327f2e"
                sanctions:
                  entries:
                    - source: pl_mswia
                      name: EUROCHEM POLSKA Sp. z o.o.
                      listed_on: "2022-04-26"
                      removed_on: null
                      source_url: https://www.gov.pl/web/mswia/lista-osob-i-podmiotow-objetych-sankcjami
                      measures: |
                        a. zamrożenie wszystkich środków finansowych i zasobów gospodarczych,
                        b. zakaz udostępniania podmiotowi wpisanemu na listę – bezpośrednio lub pośrednio – jakichkolwiek środków finansowych lub zasobów gospodarczych,
                        c. zakaz świadomego i umyślnego udziału w działaniach, których celem lub skutkiem jest ominięcie środków wskazanych w lit. a i b,
                        d. wykluczenie z postępowania o udzielenie zamówienia publicznego lub konkursu
                      justification: |
                        A. Melniczenko jest rosyjskim przemysłowcem, właścicielem przedsiębiorstwa EuroChem, będącego jednym z głównych producentów nawozów i przedsiębiorstwa węglowego SUEK. Należy do kręgu najbardziej wpływowych rosyjskich przedsiębiorców, mających bliskie powiązania z rządem rosyjskim. Działa zatem w sektorach gospodarczych zapewniających istotne źródło dochodów rządowi Federacji Rosyjskiej, odpowiedzialnemu za aneksję Krymu i agresję przeciwko Ukrainie. Andriej Melniczenko objęty jest sankcjami na podstawie rozporządzenia Rady (UE) nr 269/2014, zał. I, poz. 721. A ponadto sankcje na wymienionego nałożyła Wielka Brytania oraz Ukraina.

                        EUROCHEM POLSKA sp. z o. o. wytransferowała do spółki EuroChem Group AG ponad 30% przychodu osiągniętego w 2020 r.

                        Nałożenie sankcji na spółkę EuroChem Polska sp. z o.o. przyczyni się do zmniejszenia przychodów wymienionej firmy oraz podmiotów osiągających korzyści z jej działalności, a tym samym pośrednio wpłynie na zmniejszenie przychodów podmiotu powiązanego z A. Melniczenką.
                      measures_i18n:
                        en: |
                          a. freezing of all funds and economic resources,
                          b. prohibition on making available to the listed entity – directly or indirectly – any funds or economic resources,
                          c. prohibition on knowingly and intentionally participating in activities whose purpose or effect is to circumvent the measures specified in lit. a and b,
                          d. exclusion from a public procurement procedure or competition
                        de: |
                          a. Einfrieren aller Gelder und wirtschaftlichen Ressourcen,
                          b. Verbot der unmittelbaren oder mittelbaren Bereitstellung jeglicher Gelder oder wirtschaftlicher Ressourcen für die gelistete Einrichtung,
                          c. Verbot der wissentlichen und vorsätzlichen Beteiligung an Handlungen, deren Zweck oder Wirkung die Umgehung der in lit. a und b genannten Maßnahmen ist,
                          d. Ausschluss von einem Verfahren zur Vergabe eines öffentlichen Auftrags oder von einem Wettbewerb
                      justification_i18n:
                        en: |
                          A. Melniczenko is a Russian industrialist and the owner of EuroChem, one of the main fertiliser producers, and the coal company SUEK. He is among the most influential Russian businesspeople with close ties to the Russian government. He is therefore active in economic sectors that provide a significant source of revenue to the government of the Russian Federation, which is responsible for the annexation of Crimea and the aggression against Ukraine. Andriej Melniczenko is subject to sanctions under Council Regulation (EU) No 269/2014, Annex I, entry 721. In addition, the United Kingdom and Ukraine have imposed sanctions on him.

                          EUROCHEM POLSKA sp. z o. o. transferred more than 30% of the revenue it earned in 2020 r. to EuroChem Group AG.

                          Imposing sanctions on EuroChem Polska sp. z o.o. will reduce the revenue of that company and of entities benefiting from its activities, and will thereby indirectly reduce the revenue of an entity linked to A. Melniczenko.
                        de: |
                          A. Melniczenko ist ein russischer Industrieller und Eigentümer von EuroChem, einem der wichtigsten Düngemittelhersteller, sowie des Kohleunternehmens SUEK. Er gehört zum Kreis der einflussreichsten russischen Unternehmer mit engen Verbindungen zur russischen Regierung. Er ist somit in Wirtschaftssektoren tätig, die der Regierung der Russischen Föderation, die für die Annexion der Krim und die Aggression gegen die Ukraine verantwortlich ist, eine wesentliche Einnahmequelle bieten. Andriej Melniczenko unterliegt Sanktionen gemäß der Verordnung (EU) Nr. 269/2014 des Rates, Anhang I, Eintrag 721. Darüber hinaus haben das Vereinigte Königreich und die Ukraine Sanktionen gegen ihn verhängt.

                          EUROCHEM POLSKA sp. z o. o. überwies mehr als 30 % der im Jahr 2020 r. erzielten Einnahmen an EuroChem Group AG.

                          Die Verhängung von Sanktionen gegen EuroChem Polska sp. z o.o. wird dazu beitragen, die Einnahmen dieses Unternehmens und der Einrichtungen, die von seiner Tätigkeit profitieren, zu verringern und damit mittelbar die Einnahmen einer mit A. Melniczenko verbundenen Einrichtung zu senken.
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}/gpw:
    get:
      tags:
      - Companies
      summary: Warsaw Stock Exchange (GPW)
      description: |
        Listing snapshot for a KRS company matched to GPW Main Market, NewConnect, or GlobalConnect:
        ticker, ISIN, last close and change, market cap, shares, EPS, P/E, dividend and yield,
        plus a few recent daily bars. Quotes delayed ~15 minutes, in PLN.

        Not an official GPW licensed feed. `gpw` is `null` when the company is not listed / not matched.

        Optional history (still capped — never the full MAX chart):
        - `include=series` — daily OHLCV, default 250 bars, max 2000 (`series_limit`)
        - `include=intraday` — 1-minute bars for the last 10 calendar days
        - combine as `include=series,intraday`
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        description: KRS input is normalized by stripping non-digits and left-padding to 10 digits.
        example: "0000028860"
      - name: include
        in: query
        required: false
        schema:
          type: string
        description: Comma-separated extras. Supported values `series`, `intraday`.
        example: series
      - name: series_limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          maximum: 2000
          default: 250
        description: Daily bars when `include` contains `series`. Ignored otherwise.
      responses:
        '200':
          description: Listing snapshot (`gpw` may be null).
          content:
            application/json:
              schema:
                type: object
              example:
                krs: "0000028860"
                entity_id: ad2ae683-ccbd-49c6-a85c-648024435cb5
                nip: "7740001454"
                gpw:
                  ticker: PKN
                  isin: PLPKN0000018
                  market: main
                  last_close: 78.12
                  last_close_date: "2026-09-01"
                  pe_ratio: 9.29
                  currency: PLN
                  delayed_minutes: 15
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/krs/{krs}/status:
    get:
      tags:
      - Companies
      summary: Legal status
      description: Quick legal-status check (Active, Bankruptcy, Liquidation).
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        description: KRS input is normalized by stripping non-digits and left-padding to 10 digits.
          After normalization, KRS must contain exactly 10 digits; otherwise request fails with `400 INVALID_KRS`.
        example: "0000028860"
      responses:
        '200':
          description: Legal status for a company.
          content:
            application/json:
              schema:
                type: object
              example:
                krs: "0000028860"
                status: active
                label: Active
                is_open: true
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /companies/export:
    get:
      tags:
      - Companies
      summary: Bulk company search export page
      description: |
        Same filters as `GET /companies`, with page size up to **500** (vs 100 on the
        standard list). Contact emails are included when available.
        For large pulls, page with `cursor` (`cursor` = last row `entity_id`). Large `offset` values get slow.
        Each page consumes **1** monthly quota request.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - $ref: '#/components/parameters/CompanyFilterQ'
      - $ref: '#/components/parameters/CompanyFilterKeywords'
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 500
          default: 100
        description: Page size for bulk export (max 500).
      - $ref: '#/components/parameters/CompanyFilterOffset'
      - $ref: '#/components/parameters/CompanyFilterCursor'
      - $ref: '#/components/parameters/CompanyFilterPkd'
      - $ref: '#/components/parameters/CompanyFilterPrimaryOnly'
      - $ref: '#/components/parameters/CompanyFilterHasEmail'
      - $ref: '#/components/parameters/CompanyFilterHasWebsite'
      - $ref: '#/components/parameters/CompanyFilterStatusActive'
      - $ref: '#/components/parameters/CompanyFilterRevenueMin'
      - $ref: '#/components/parameters/CompanyFilterRevenueMax'
      responses:
        '200':
          description: One page of companies (same shape as `GET /companies`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /ceidg/nip/{nip}:
    get:
      tags:
      - Companies
      summary: CEIDG profile by NIP
      description: |
        Sole-proprietor (CEIDG) profile by **NIP**. Subject to public CEIDG catalog
        visibility. Contact email/phone revealed for API-key auth when present and allowed.
        Personal owner fields follow the same RODO gating as the website.

        Typical payload:
        - `company` – identifiers, address, PKD activities, contacts, logo, `roles` (owner)
        - `primaryPkdDescription` – localized primary PKD label
        - `companySummary` – AI description for the requested `locale` only (e.g. `{ "en": "…" }`)
        - `details.vat` – VAT whitelist status + bank accounts when known
        - `similarCompanies` – peer JDGs from the same enrichment cache
        - `changeHistory` – CEIDG change events when available
        - `sudop` – public-aid (SUDOP) snapshot when checked (`null` if unavailable)
        - `bzp` – public-procurement (BZP) summary when present (`null` if none)
        - `ure` – URE energy concessions when checked (`null` if unavailable)
        - `bdo` – BDO waste/packaging register when present (`null` if none)
        - `gpw` – Warsaw Stock Exchange listing snapshot when the NIP is matched (`null` if not listed)
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: nip
        in: path
        required: true
        schema:
          type: string
        description: NIP normalized to exactly 10 digits.
        example: "7712488697"
      - name: locale
        in: query
        required: false
        schema:
          type: string
          enum: [pl, en, de]
          default: en
        description: Language for PKD labels and `companySummary` (one locale key in the response).
        example: en
      responses:
        '200':
          description: CEIDG company profile.
          content:
            application/json:
              schema:
                type: object
              example:
                company:
                  ceidg_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  nip: "7712488697"
                  regon: "022456789"
                  company_name: Michał Ryszka MCR
                  company_name_display: Michał Ryszka MCR
                  legal_form: JEDNOOSOBOWA DZIAŁALNOŚĆ GOSPODARCZA
                  status: AKTYWNY
                  is_active: true
                  registration_date: "2015-03-12"
                  suspended_on: null
                  closure_date: null
                  deregistration_date: null
                  resumed_on: null
                  succession_mgmt_start_date: null
                  succession_mgmt_end_date: null
                  address:
                    street: ul. Legnicka
                    building_no: "48"
                    building_unit: null
                    city: Wrocław
                    postal_code: "54-206"
                    country: PL
                    region: dolnośląskie
                    county: Wrocław
                    municipality: Wrocław-Fabryczna
                    full: "ul. Legnicka 48, 54-206 Wrocław, PL"
                  additional_addresses: []
                  correspondence_address: null
                  primary_pkd: "66.19.Z"
                  primary_pkd_description: Other activities auxiliary to financial services, except insurance and pension funding
                  all_pkd_codes:
                    - "66.19.Z"
                    - "62.01.Z"
                  primary_activities:
                    - code_full: "66.19.Z"
                      description: Other activities auxiliary to financial services, except insurance and pension funding
                      is_primary: true
                  secondary_activities:
                    - code_full: "62.01.Z"
                      description: Computer programming activities
                      is_primary: false
                  contacts:
                    email: kontakt@mcr-example.pl
                    emailGated: false
                    phone: null
                    phoneGated: true
                    website: mcr-example.pl
                    websiteInactive: false
                    emailDeliverabilityVerified: true
                    emailInactive: false
                    emailInactiveReason: null
                  logo_url: null
                  show_logo: true
                  roles:
                    - role_name: WŁAŚCICIEL
                      party:
                        type: person
                        display_name: Michał Ryszka
                        first_name: Michał
                        last_name: Ryszka
                primaryPkdDescription: Other activities auxiliary to financial services, except insurance and pension funding
                companySummary:
                  en: Michał Ryszka MCR provides financial intermediation and IT services for SMEs in Lower Silesia.
                details:
                  vat:
                    status: Czynny
                    status_date: "2024-06-15"
                    removal_date: null
                    removal_basis: null
                    checked_at: "2026-08-01T10:15:00.000Z"
                    accounts:
                      - "87105014451000002364817493"
                similarCompanies:
                  - ceidg_id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    nip: "8171895344"
                    company_name: EL-CAR Elżbieta Janik
                    company_name_display: EL-CAR Elżbieta Janik
                    legal_form: JEDNOOSOBOWA DZIAŁALNOŚĆ GOSPODARCZA
                    city: Złotniki
                    region: podkarpackie
                    postal_code: "36-060"
                    primary_pkd: "46.90.Z"
                    website: null
                    logo_url: null
                    show_logo: true
                    status: AKTYWNY
                changeHistory:
                  - id: "c3d4e5f6-a7b8-9012-cdef-123456789012"
                    history_entry_id: "CEIDG-HIST-20231102-001"
                    operation_type: zmiana
                    application_number: "W/1234567/2023"
                    author: CEIDG
                    change_effective_on: "2023-11-02"
                    entry_at: "2023-11-02T14:22:11.000Z"
                    has_diff: true
                    details:
                      - section: Adres wykonywania działalności
                        section_operation: zmiana
                        field: ulica
                        operation: zmiana
                        old_value: ul. Stara 1
                        new_value: ul. Legnicka 48
                sudop:
                  checked_at: "2026-07-20T08:00:00.000Z"
                  has_aid: false
                  cases_count: 0
                  total_nominal_pln: null
                  total_gross_pln: null
                  total_nominal_usd: null
                  total_gross_usd: null
                  total_nominal_eur: null
                  total_gross_eur: null
                  beneficiary_name: null
                  source_url: https://sudop.uokik.gov.pl
                  cases: []
                bzp:
                  checked_at: "2026-07-22T12:30:00.000Z"
                  as_contractor_awards_count: 2
                  as_contractor_notices_count: 3
                  as_buyer_notices_count: 0
                  as_contractor_awards_total_pln: 185000
                  as_contractor_awards_total_usd: 46250
                  as_contractor_awards_total_eur: 42500
                  first_publication_date: "2022-04-11"
                  last_publication_date: "2025-09-03"
                  source_url: https://ezamowienia.gov.pl
                  recent_awards:
                    - object_id: "ocds-148610-example-001"
                      lot_index: 1
                      notice_type: ContractAwardNotice
                      notice_number: "2025/BZP 00012345/01"
                      bzp_number: "2025/BZP 00012345"
                      publication_date: "2025-09-03T10:00:00.000Z"
                      order_object: Delivery of IT equipment
                      cpv_code: "30200000-1"
                      procedure_result: awarded
                      role: contractor
                      is_award: true
                      award_value_pln: 120000
                      award_value_usd: 30000
                      award_value_eur: 27600
                      estimated_value_pln: 130000
                      estimated_value_usd: 32500
                      estimated_value_eur: 29900
                      amounts_currency: PLN
                      counterparty_name: Gmina Example
                      counterparty_nip: "5250000000"
                      counterparty_krs: "0000123456"
                      counterparty_ceidg: false
                      source_url: https://ezamowienia.gov.pl
                  recent_as_buyer: []
                ure:
                  checked_at: "2026-08-16T12:00:00.000Z"
                  has_concessions: false
                  concessions_count: 0
                  applications_count: 0
                  active_count: 0
                  items_count: 0
                  dkn: null
                  company_name: null
                  source_url: https://www.ure.gov.pl
                  items: []
                bdo:
                  checked_at: "2026-08-20T12:00:00.000Z"
                  has_entry: true
                  bdo_number: "000007103"
                  company_name: PKN ORLEN
                  application_id: "045fe2b9-9077-4e2e-ba62-7e07aeaba020"
                  eup_count: 2177
                  decisions_count: 5
                  activity_flags: ["waste_generation", "waste_treatment"]
                  source_url: https://rejestr-bdo.mos.gov.pl/Registry/Index
                  items: []
                gpw:
                  checked_at: "2026-09-01T16:30:00.000Z"
                  ticker: PKN
                  isin: PLPKN0000018
                  market: main
                  listed_name: ORLEN
                  shares_outstanding: 1163856079
                  last_close: 78.12
                  last_close_date: "2026-09-01"
                  prev_close: 77.40
                  change_abs: 0.72
                  change_pct: 0.93
                  market_cap: 90919000000
                  last_volume: 1250000
                  eps: 8.41
                  pe_ratio: 9.29
                  dividend_per_share: 4.15
                  dividend_yield: 5.31
                  dividend_year: 2025
                  dividend_ex_date: "2025-08-13"
                  source_url: https://www.gpw.pl/list-of-companies
                  currency: PLN
                  delayed_minutes: 15
                  recent:
                    - trade_date: "2026-09-01"
                      open: 77.50
                      high: 78.40
                      low: 77.20
                      close: 78.12
                      volume: 1250000
                msig:
                  checked_at: "2026-08-24T12:00:00.000Z"
                  notices_count: 1
                  first_publication_date: "2026-08-24"
                  last_publication_date: "2026-08-24"
                  source_url: https://wyszukiwarka-msig.ms.gov.pl/
                  items:
                    - id: 19396258
                      issue_id: 8958
                      notice_number: "39248"
                      publication_date: "2026-08-24"
                      monitor_number: "163/2026"
                      notice_type: dissolution_without_liquidation
                      entity_name: AGS TEAM POLAND SPÓŁKA Z OGRANICZONĄ ODPOWIEDZIALNOŚCIĄ
                      krs: "0000813406"
                      source_url: https://wyszukiwarka-msig.ms.gov.pl/
                      pdf_url: https://wyszukiwarka-msig.ms.gov.pl/api/Monitor/Download?id=8958&fileId=true
                ted:
                  checked_at: "2026-08-17T12:00:00.000Z"
                  as_winner_awards_count: 2
                  as_winner_notices_count: 2
                  as_buyer_notices_count: 0
                  as_winner_awards_total_pln: 64504059.09
                  as_winner_awards_total_usd: 16126014.77
                  as_winner_awards_total_eur: 14828526.00
                  first_publication_date: "2022-03-11"
                  last_publication_date: "2026-08-10"
                  source_url: https://ted.europa.eu/
                  recent_awards:
                    - publication_number: "564833-2026"
                      notice_type: can-standard
                      title: Przebudowa drogi startowej
                      role: winner
                      is_award: true
                      award_value: 64504059.09
                      award_value_usd: 16126014.77
                      award_value_eur: 14828526.00
                      amounts_currency: PLN
                      counterparty_name: Port Lotniczy Gdynia
                      source_url: https://ted.europa.eu/pl/notice/-/detail/564833-2026
                  recent_as_buyer: []
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /watchlist:
    get:
      tags:
      - Watchlist
      summary: List watchlist
      description: |
        Lists companies on the watchlist of the **user account linked to the API key**.
        Requires `user_id` on the key.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 20
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: Watchlist page.
          content:
            application/json:
              schema:
                type: object
              example:
                data: []
                pagination:
                  total: 0
                  hasMore: false
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: API key is not linked to a user account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /watchlist/krs/{krs}:
    post:
      tags:
      - Watchlist
      summary: Add company to watchlist
      description: |
        Adds a company to the **watchlist of the user account linked to the API key**.

        The company is identified by **KRS** (Polish court registry number). Digits only;
        leading zeros are optional (normalized to 10 digits server-side).

        If the company is already on the watchlist, the response returns `already_following: true`
        and does not create a duplicate entry.

        Requires an API key associated with a Compabase portal user account (`user_id` on the key).
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        description: KRS input is normalized by stripping non-digits and left-padding to 10 digits.
          After normalization, KRS must contain exactly 10 digits; otherwise request fails with `400 INVALID_KRS`.
        example: "0000028860"
      responses:
        '200':
          description: Company added to watchlist (or already followed).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WatchlistAddResponse'
              examples:
                added:
                  summary: Newly added
                  value:
                    krs: "0000028860"
                    entity_id: ad2ae683-ccbd-49c6-a85c-648024435cb5
                    already_following: false
                    created_at: "2026-05-20T14:30:00Z"
                alreadyFollowing:
                  summary: Already on watchlist
                  value:
                    krs: "0000028860"
                    entity_id: ad2ae683-ccbd-49c6-a85c-648024435cb5
                    already_following: true
                    created_at: "2026-05-15T09:00:00Z"
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: API key is not linked to a user account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: INVALID_API_KEY
                  message: This API key is not linked to a user account.
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
      - Watchlist
      summary: Remove company from watchlist
      description: |
        Removes a KRS company from the watchlist of the user linked to the API key.
        Idempotent: `removed: false` when the company was not on the list.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: krs
        in: path
        required: true
        schema:
          type: string
        example: "0000028860"
      responses:
        '200':
          description: Removal result.
          content:
            application/json:
              schema:
                type: object
              example:
                krs: "0000028860"
                entity_id: ad2ae683-ccbd-49c6-a85c-648024435cb5
                removed: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: API key is not linked to a user account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /persons/search:
    get:
      tags:
      - Persons
      summary: Search persons by name
      description: |
        Searches for natural persons by name (first name, last name, or both).
        Returns a deduplicated list of matching persons with their `person_id` key
        and the number of companies they are linked to.

        Use the returned `person_id` with `GET /persons/{personId}/connections`
        to fetch full connection details.

        Identification is restricted to name-based queries (first name, last name, or both).
        Protected data, including PESEL and exact birth dates, is never exposed.
        To assist with identification, every result includes a `birth_year` (integer).

        **Result capping:** internally the search scans up to 2 000 matching link
        rows before aggregation. For very common names the response may include
        a `note` field indicating the result set was truncated; use a more specific
        query to narrow results.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: q
        in: query
        required: true
        schema:
          type: string
          minLength: 2
        description: |
          Name substring to search for (case-insensitive). Must be at least
          **2 characters**. Matched against the stored display name using
          `ILIKE %q%`.
        example: Kowalski
      - name: limit
        in: query
        schema:
          type: integer
          default: 20
          minimum: 1
          maximum: 50
        description: 'Maximum number of persons to return. Capped at **50**.'
      responses:
        '200':
          description: List of matching persons.
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                - total
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PersonSearchItem'
                  total:
                    type: integer
                    description: Number of distinct persons found (before `limit` slicing).
                  note:
                    type: string
                    nullable: true
                    description: Present when the internal scan limit was reached and results may be incomplete.
              example:
                data:
                - person_id: 033e6964-179b-4039-86a2-0e0590c56918
                  display_name: Krzysztof Mateusz Kowalski
                  birth_year: 1985
                  company_count: 3
                - person_id: 0063e9c1-265f-47d4-a3ad-6cb698869f7a
                  display_name: Sebastian Piotr Kowalski
                  birth_year: null
                  company_count: 2
                - person_id: 048bd9e8-8b0e-446f-8b36-e60f518d3abe
                  display_name: Henry Kowalski
                  birth_year: 1972
                  company_count: 1
                total: 3
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /persons/{personId}/connections:
    get:
      tags:
      - Persons
      summary: Company connections for a person
      description: |
        Returns all companies linked to the given person together with the
        relationship type and role label for each link.

        Use `GET /persons/search` to resolve a name to a `person_id` first.
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      parameters:
      - name: personId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: UUID of the person; obtained from `GET /persons/search`.
        example: 033e6964-179b-4039-86a2-0e0590c56918
      responses:
        '200':
          description: Person connections payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PersonConnectionsResponse'
              example:
                person_id: 033e6964-179b-4039-86a2-0e0590c56918
                companies:
                - entity_id: 1a2b3c4d-0000-0000-0000-000000000001
                  registry_number: '0000123456'
                  company_name: Example Company Ltd.
                  relationships:
                  - kind: management
                    role: President of the Management Board
                - entity_id: 1a2b3c4d-0000-0000-0000-000000000002
                  registry_number: '0000654321'
                  company_name: Second Company Ltd.
                  relationships:
                  - kind: ownership
                    role: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /usage:
    get:
      tags:
      - Usage
      summary: Monthly quota usage for this API key
      description: 'Returns how many requests this key has used in the current UTC calendar month against its limit.

        This endpoint does not consume quota (usage counter is not incremented).

        '
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      responses:
        '200':
          description: Usage snapshot
          content:
            application/json:
              schema:
                type: object
                required:
                - used
                - limit
                - remaining
                - monthUtc
                - resetsAtUtc
                properties:
                  used:
                    type: integer
                  limit:
                    type: integer
                  remaining:
                    type: integer
                  monthUtc:
                    type: string
                    description: Current billing period date (ISO 8601 date).
                    example: '2026-08-31'
                  resetsAtUtc:
                    type: string
                    format: date-time
                    description: When the billing period resets (ISO 8601 datetime with timezone).
                    example: '2026-09-01T00:00:00+00:00'
        '401':
          $ref: '#/components/responses/Unauthorized'
components:
  responses:
    BadRequest:
      description: Invalid request parameters.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            invalidKrs:
              summary: Invalid KRS value
              value:
                statusCode: 400
                statusMessage: Invalid krs
                message: Invalid krs
                data:
                  error:
                    code: INVALID_KRS
                    message: After normalization, KRS must contain exactly 10 digits.
                    details:
                      parameter: krs
            invalidCursor:
              summary: Invalid cursor format
              value:
                statusCode: 400
                statusMessage: Invalid cursor
                message: Invalid cursor
                data:
                  error:
                    code: INVALID_CURSOR
                    message: Cursor must be a valid UUID.
                    details:
                      parameter: cursor
            invalidRange:
              summary: Invalid numeric range
              value:
                statusCode: 400
                statusMessage: Invalid range
                message: Invalid range
                data:
                  error:
                    code: INVALID_RANGE
                    message: revenue_min cannot be greater than revenue_max.
                    details:
                      parameter: revenue_min,revenue_max
    Unauthorized:
      description: Missing, invalid, or revoked API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            statusCode: 401
            statusMessage: Unauthorized
            message: Unauthorized
            data:
              message: Missing or invalid API key.
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            statusCode: 404
            statusMessage: Not Found
            message: Not Found
            data:
              error:
                code: NOT_FOUND
                message: Company not found.
    TooManyRequests:
      description: Monthly quota exceeded for this API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            statusCode: 429
            statusMessage: Too Many Requests
            message: Too Many Requests
            data:
              error:
                code: QUOTA_EXCEEDED
                message: Monthly quota exceeded for this API key.
  schemas:
    ErrorResponse:
      type: object
      required:
      - statusCode
      - statusMessage
      - message
      properties:
        statusCode:
          type: integer
          description: HTTP status code.
        statusMessage:
          type: string
          description: HTTP status text.
        message:
          type: string
          description: Short error summary (same as statusMessage for most errors).
        data:
          type: object
          description: Domain error payload set by the handler.
          properties:
            message:
              type: string
              description: Human-readable message (used by 401 responses).
            error:
              type: object
              description: Structured domain error (used by 400/404/429 responses).
              properties:
                code:
                  type: string
                  description: Machine-readable domain error code.
                  enum:
                  - INVALID_KRS
                  - INVALID_NIP
                  - INVALID_CURSOR
                  - INVALID_RANGE
                  - INVALID_PARAMETER
                  - CONFLICTING_PARAMETERS
                  - INVALID_BOOLEAN
                  - INVALID_KEYWORDS
                  - INVALID_API_KEY
                  - UNAUTHORIZED
                  - NOT_FOUND
                  - QUOTA_EXCEEDED
                message:
                  type: string
                details:
                  type: object
                  additionalProperties: true
    CompanyListPagination:
      type: object
      required:
      - limit
      - offset
      - total
      - hasMore
      properties:
        limit:
          type: integer
          description: Page size (capped at 100 for `GET /companies`; up to 500 for `GET /companies/export`).
        offset:
          type: integer
        total:
          type: integer
          description: Total rows matching filters. May be `-1` when not computed (e.g. some cursor-based responses).
        hasMore:
          type: boolean
        cursor:
          type: string
          description: Last `entity_id` from this page; pass as `cursor` query param for the next page when supported.
    CompanyListItem:
      type: object
      description: 'Row from `company_search` (fields depend on query path) plus enrichment: `email`, `website`, `logo_url`,

        `currency`, `period_to`, and normalized `company_name_display`. `full_address` is derived from postal data when present.

        '
      properties:
        entity_id:
          type: string
          format: uuid
        company_name:
          type: string
        company_name_display:
          type: string
          nullable: true
        registry_number:
          type: string
          nullable: true
        nip:
          type: string
          nullable: true
        regon:
          type: string
          nullable: true
        legal_form:
          type: string
          nullable: true
        city:
          type: string
          nullable: true
        postal_code:
          type: string
          nullable: true
        county:
          type: string
          nullable: true
        municipality:
          type: string
          nullable: true
        region:
          type: string
          nullable: true
        full_address:
          type: string
          nullable: true
        primary_pkd:
          type: string
          nullable: true
        all_pkd_codes:
          type: array
          items:
            type: string
        is_active:
          type: boolean
          nullable: true
        latest_snapshot_id:
          type: string
          format: uuid
          nullable: true
        revenue_total:
          type: number
          nullable: true
        revenue_total_usd:
          type: number
          nullable: true
        revenue_operating:
          type: number
          nullable: true
        profit_operating:
          type: number
          nullable: true
        profit_net:
          type: number
          nullable: true
        profit_net_usd:
          type: number
          nullable: true
        operating_costs_total:
          type: number
          nullable: true
        income_tax:
          type: number
          nullable: true
        cost_wages:
          type: number
          nullable: true
        cost_amortization:
          type: number
          nullable: true
        total_assets:
          type: number
          nullable: true
        capital_total:
          type: number
          nullable: true
        has_email:
          type: boolean
          nullable: true
        has_website:
          type: boolean
          nullable: true
        extracted_at:
          type: string
          format: date-time
          nullable: true
        state_date:
          type: string
          format: date
          nullable: true
        email:
          type: string
          nullable: true
        website:
          type: string
          nullable: true
        logo_url:
          type: string
          nullable: true
        currency:
          type: string
          nullable: true
        period_to:
          type: string
          nullable: true
    CompanyListResponse:
      type: object
      description: Search/list response. `data` is an array of companies.
      required:
      - data
      - pagination
      properties:
        data:
          type: array
          description: One page of result rows (same filters as the request); length ≤ `pagination.limit`.
          items:
            $ref: '#/components/schemas/CompanyListItem'
        pagination:
          $ref: '#/components/schemas/CompanyListPagination'
    CompanyCountResponse:
      type: object
      required:
      - count
      properties:
        count:
          type: integer
          minimum: 0
          description: Number of companies matching the filters.
    WatchlistAddResponse:
      type: object
      required:
      - krs
      - entity_id
      - already_following
      properties:
        krs:
          type: string
          description: Normalized 10-digit KRS.
          example: "0000028860"
        entity_id:
          type: string
          format: uuid
          description: Internal company identifier.
        already_following:
          type: boolean
          description: "True if the company was already on the watchlist before this request."
        created_at:
          type: string
          format: date-time
          nullable: true
          description: "When the watchlist entry was created. Null when already_following is true and the original timestamp is unavailable."
    PersonSearchItem:
      type: object
      required:
      - person_id
      - display_name
      - birth_year
      - company_count
      properties:
        person_id:
          type: string
          format: uuid
          description: Stable UUID key for this person. Use with `GET /persons/{personId}/connections`.
        display_name:
          type: string
          nullable: true
          description: Full display name of the person.
        birth_year:
          type: integer
          nullable: true
          description: Year of birth when known (derived from registry data; full birth date is not exposed). `null` if unavailable.
          example: 1985
        company_count:
          type: integer
          description: Number of distinct companies this person is linked to.
    PersonConnectionsResponse:
      type: object
      required:
      - person_id
      - companies
      properties:
        person_id:
          type: string
          format: uuid
        companies:
          type: array
          items:
            type: object
            required:
            - entity_id
            - relationships
            properties:
              entity_id:
                type: string
                format: uuid
              registry_number:
                type: string
                nullable: true
                description: KRS number (10 digits), if available.
              company_name:
                type: string
                nullable: true
              relationships:
                type: array
                items:
                  type: object
                  required:
                  - kind
                  - role
                  properties:
                    kind:
                      type: string
                      description: Relationship type (e.g. `management`, `ownership`).
                    role:
                      type: string
                      nullable: true
                      description: Role label (e.g. `PREZES ZARZĄDU`), if available.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
    BearerAuth:
      type: http
      scheme: bearer
  parameters:
    CompanyFilterQ:
      name: q
      in: query
      description: 'Free-text search (company name and identifiers). **Digits** are treated as a possible KRS/NIP/REGON

        lookup. Whitespace is trimmed.

        Matching strategy:
        - identifier-like numeric input: identifier lookup path is used first.
          For 10-digit numeric values, exact match is attempted against KRS (`registry_number`) and NIP;
          an exact match is also attempted against REGON as stored in the index.
          For other numeric lengths, prefix matching is used for those identifier fields.
        - textual input: case-insensitive substring match on company name (`ILIKE %q%`).

        Numeric lookup has priority over text-name matching when `q` contains only digits.
        Example: `q=0000028860` is treated as identifier lookup (not generic relevance search).

        '
      schema:
        type: string
      example: orlen
    CompanyFilterKeywords:
      name: keywords
      in: query
      description: 'Keyword filter with **OR** semantics. Pass a comma-separated list, or repeat the parameter.

        Each keyword must be at least **2** characters after trimming.
        Empty tokens are ignored.
        Duplicate tokens may appear and are not guaranteed to be deduplicated server-side.
        Mixing repeated params and CSV is supported (e.g. `keywords=a,b&keywords=c`).

        '
      schema:
        type: string
      example: logistics,energy
    CompanyFilterLimit:
      name: limit
      in: query
      description: 'Page size. **Default:** `20`. `GET /companies` caps at **100** per request.

        For bulk pulls use `GET /companies/export` (or `limit` &gt; 100 on `/companies` via API key),
        capped at **500** per page. For deep traversal, page with `cursor`.

        **Quota accounting:** monthly quota counts each HTTP request. Row count in the page does not change the cost.
        Example: `limit=500` still consumes **1** request from monthly quota.

        '
      schema:
        type: integer
        default: 20
        minimum: 1
        maximum: 500
    CompanyFilterOffset:
      name: offset
      in: query
      description: 'Zero-based row offset for classic **offset** pagination.

        Client contract: use either `offset` or `cursor` (not both).
        If both are provided, the server returns `400 CONFLICTING_PARAMETERS`.

        '
      schema:
        type: integer
        default: 0
        minimum: 0
    CompanyFilterCursor:
      name: cursor
      in: query
      description: 'Optional **cursor** for pagination: `entity_id` of the last row from the previous page.

        Cursor paging is ordered by `entity_id` ascending. Use it for deep paging/export-style traversal.
        Do not combine with `offset`; if both are sent, request fails with `400 CONFLICTING_PARAMETERS`.

        When present, the server may return `pagination.total: -1` and include `pagination.cursor` for the next page.

        '
      schema:
        type: string
        format: uuid
    CompanyFilterPkd:
      name: pkd
      in: query
      description: 'PKD code (Polish business activity classification). **Case-insensitive**; trimmed and uppercased server-side.

        By default only the **primary** PKD (`primary_only=true`) is matched; set `primary_only=false` to match any code in
        `all_pkd_codes`.

        '
      schema:
        type: string
      example: '62.01'
    CompanyFilterPrimaryOnly:
      name: primary_only
      in: query
      description: 'Boolean filter syntax is accepted (`true`/`false`).

        Semantics: `true` (default) means `pkd` matches `primary_pkd` only.
        `false` means `pkd` can match any entry in `all_pkd_codes`.

        '
      schema:
        type: boolean
        default: true
    CompanyFilterCurrency:
      name: currency
      in: query
      description: 'Display/filter currency context for financial threshold filters.

        Allowed values: `PLN`, `USD`, `EUR` (case-insensitive).
        Default is `PLN`.

        For `revenue_min` / `revenue_max`, the API applies thresholds against:
        - `revenue_total` when `currency=PLN`
        - `revenue_total_usd` when `currency=USD`
        - `revenue_total_eur` when `currency=EUR`

        '
      schema:
        type: string
        enum: [PLN, USD, EUR]
        default: PLN
    CompanyFilterVoivodeship:
      name: voivodeship
      in: query
      description: 'Voivodeship / region filter. Compared to `company_search.region` **case-insensitively** (lowercased on
        the server).

        Use the same spelling as stored in data (e.g. Polish voivodeship names such as `mazowieckie`, `śląskie`).
        Common values include: `dolnośląskie`, `małopolskie`, `mazowieckie`, `pomorskie`, `śląskie`, `wielkopolskie`.

        '
      schema:
        type: string
      example: mazowieckie
    CompanyFilterPostalCode:
      name: postal_code
      in: query
      description: 'Exact postal code match (trimmed). Format depends on source data (e.g. `00-001`).

        '
      schema:
        type: string
    CompanyFilterCity:
      name: city
      in: query
      description: 'City substring filter (case-insensitive **ILIKE** `%city%`).

        '
      schema:
        type: string
      example: Warszawa
    CompanyFilterRevenueMin:
      name: revenue_min
      in: query
      description: 'Minimum **total revenue** (`revenue_total`).

        Currency depends on `currency` query param (`PLN`/`USD`/`EUR`).
        If `currency` is omitted, PLN is used.
        Values are in **full currency units** (not thousands), e.g. `1000000` = one million PLN.
        Evaluated against the latest indexed financial snapshot per company.
        Companies with `null` in the selected revenue column do not match this filter.

        '
      schema:
        type: number
    CompanyFilterRevenueMax:
      name: revenue_max
      in: query
      description: 'Maximum **total revenue** threshold in full units.

        Currency depends on `currency` query param (`PLN`/`USD`/`EUR`).
        If `currency` is omitted, PLN is used.

        If `revenue_min > revenue_max`, the API returns `400` with code `INVALID_RANGE`.
        Evaluated against the latest indexed financial snapshot per company.
        Companies with `null` in the selected revenue column do not match this filter.

        '
      schema:
        type: number
    CompanyFilterRevenueOperatingMin:
      name: revenue_operating_min
      in: query
      description: Minimum operating revenue (net sales / operating revenue field from financials).
      schema:
        type: number
    CompanyFilterRevenueOperatingMax:
      name: revenue_operating_max
      in: query
      schema:
        type: number
    CompanyFilterProfitOperatingMin:
      name: profit_operating_min
      in: query
      schema:
        type: number
    CompanyFilterProfitOperatingMax:
      name: profit_operating_max
      in: query
      schema:
        type: number
    CompanyFilterProfitNetMin:
      name: profit_net_min
      in: query
      schema:
        type: number
    CompanyFilterProfitNetMax:
      name: profit_net_max
      in: query
      schema:
        type: number
    CompanyFilterIncomeTaxMin:
      name: income_tax_min
      in: query
      schema:
        type: number
    CompanyFilterIncomeTaxMax:
      name: income_tax_max
      in: query
      schema:
        type: number
    CompanyFilterCostWagesMin:
      name: cost_wages_min
      in: query
      schema:
        type: number
    CompanyFilterCostWagesMax:
      name: cost_wages_max
      in: query
      schema:
        type: number
    CompanyFilterCostAmortizationMin:
      name: cost_amortization_min
      in: query
      schema:
        type: number
    CompanyFilterCostAmortizationMax:
      name: cost_amortization_max
      in: query
      schema:
        type: number
    CompanyFilterTotalAssetsMin:
      name: total_assets_min
      in: query
      schema:
        type: number
    CompanyFilterTotalAssetsMax:
      name: total_assets_max
      in: query
      schema:
        type: number
    CompanyFilterEbitdaMin:
      name: ebitda_min
      in: query
      schema:
        type: number
    CompanyFilterEbitdaMax:
      name: ebitda_max
      in: query
      schema:
        type: number
    CompanyFilterCapitalMin:
      name: capital_min
      in: query
      description: Minimum **share capital** (`capital_total`).
      schema:
        type: number
    CompanyFilterCapitalMax:
      name: capital_max
      in: query
      schema:
        type: number
    CompanyFilterEstimatedValueMin:
      name: estimated_value_min
      in: query
      description: Minimum **estimated company value** (`estimated_value`), when present in search index.
      schema:
        type: number
    CompanyFilterEstimatedValueMax:
      name: estimated_value_max
      in: query
      schema:
        type: number
    CompanyFilterRegistrationYearMin:
      name: registration_year_min
      in: query
      description: 'Earliest **registration** year (`registration_date` filter): companies registered on or after `YYYY-01-01`.

        '
      schema:
        type: integer
      example: 2015
    CompanyFilterRegistrationYearMax:
      name: registration_year_max
      in: query
      description: Latest registration year (`registration_date` on or before `YYYY-12-31`).
      schema:
        type: integer
    CompanyFilterRegistrationDateMin:
      name: registration_date_min
      in: query
      description: 'Earliest exact **registration date** (`registration_date` on or after this day, inclusive), format `YYYY-MM-DD`.
        Takes precedence over `registration_year_min`. For companies registered today, pass today''s date as both
        `registration_date_min` and `registration_date_max`. Applies to KRS companies and to JDG (CEIDG) rows merged into browse results.

        '
      schema:
        type: string
        format: date
      example: '2026-10-07'
    CompanyFilterRegistrationDateMax:
      name: registration_date_max
      in: query
      description: Latest exact registration date (`registration_date` on or before this day, inclusive), format `YYYY-MM-DD`. Takes precedence over `registration_year_max`.
      schema:
        type: string
        format: date
      example: '2026-10-07'
    CompanySort:
      name: sort
      in: query
      description: 'Sort the result set by one column (default: completeness / revenue ranking). Rows without a value in that
        column are excluded while sorting. Text columns (`name`, `nip`, `regon`, `city`, `region`, `postal_code`) sort alphabetically;
        `registration_date` with `dir=desc` lists the newest registrations first (future-dated register errors are skipped);
        `revenue_growth` with `dir=desc` lists the fastest-growing companies (y/y revenue growth in %, latest vs previous
        financial year from KRS statements; combine with `revenue_min` to skip small companies).
        Not applied to free-text (`q`) or `keywords` searches, which keep relevance order.

        '
      schema:
        type: string
        enum: [revenue, profit_net, estimated_value, total_assets, operating_costs_total, fixed_assets, current_assets, equity, liabilities_and_provisions, capital, registration_date, revenue_growth, name, nip, regon, city, region, postal_code]
      example: registration_date
    CompanySortDir:
      name: dir
      in: query
      description: Sort direction for `sort` (`asc` or `desc`, default `desc`).
      schema:
        type: string
        enum: [asc, desc]
        default: desc
    CompanyFilterLastReportYearMin:
      name: last_report_year_min
      in: query
      description: 'Earliest **financial report period** (`financial_period_to` on or after `YYYY-01-01`).

        When combined with financial metric filters, both are evaluated against the same indexed reporting snapshot.

        '
      schema:
        type: integer
    CompanyFilterLastReportYearMax:
      name: last_report_year_max
      in: query
      description: 'Latest financial report period (`financial_period_to` on or before `YYYY-12-31`).

        When combined with financial metric filters, both are evaluated against the same indexed reporting snapshot.

        '
      schema:
        type: integer
    CompanyFilterStatusActive:
      name: status_active
      in: query
      description: 'Boolean filter syntax is accepted (`true`/`false`).

        Semantics: `true` applies positive filter (only active companies).
        `false` is syntactically valid and treated as no filter.

        '
      schema:
        type: boolean
    CompanyFilterLegalForm:
      name: legal_form
      in: query
      description: 'Legal form substring filter (case-insensitive **ILIKE** `%legal_form%`).

        Common values/phrases:
        `spółka z ograniczoną odpowiedzialnością`, `spółka akcyjna`, `prosta spółka akcyjna`,
        `spółka komandytowa`, `jednoosobowa działalność gospodarcza`.

        '
      schema:
        type: string
      example: spółka akcyjna
    CompanyFilterHasEmail:
      name: has_email
      in: query
      description: 'Boolean filter syntax is accepted (`true`/`false`).

        Semantics: `true` applies positive filter (only companies with email).
        `false` is syntactically valid and treated as no filter.

        '
      schema:
        type: boolean
    CompanyFilterHasWebsite:
      name: has_website
      in: query
      description: 'Boolean filter syntax is accepted (`true`/`false`).

        Semantics: `true` applies positive filter (only companies with website).
        `false` is syntactically valid and treated as no filter.

        '
      schema:
        type: boolean
