---
openapi: 3.0.1
info:
  title: Nordic Financial News API
  version: v1.0
  description: |
    REST API for Nordic financial news. Each article is summarized in English (headline, short summary, and key points) with the original-language article linked via `article_url`. The full article body stays at the source.

    ## Authentication
    All endpoints require Bearer token authentication using an API key.
    Include the API key in the Authorization header:
    ```
    Authorization: Bearer YOUR_API_KEY
    ```

    ## Rate Limiting
    The API enforces per-hour rate limits based on your plan:
    - **Free**: 100 requests/hour
    - **Pro**: 5,000 requests/hour

    Rate limit information is included in response headers:
    - `X-RateLimit-Limit`: Maximum requests per hour
    - `X-RateLimit-Remaining`: Requests remaining
    - `X-RateLimit-Reset`: Seconds until limit resets
    - `X-RateLimit-Policy`: Human-readable rate limit policy (e.g. `5000 per hour; token bucket`)

    The `/health` endpoint is exempt from rate limiting and does not return rate limit headers.

    ## Monthly Usage Limits
    Each API key has a monthly request allowance based on your plan:
    - **Free**: 50 requests/month
    - **Plus**: 10,000 requests/month
    - **Pro**: 50,000 requests/month

    Monthly usage information is included in response headers:
    - `X-Monthly-Limit`: Maximum requests per month
    - `X-Monthly-Remaining`: Requests remaining this month
    - `X-Monthly-Reset`: ISO 8601 timestamp when the limit resets (end of month)

    When the monthly limit is exceeded, the API returns `429 Too Many Requests` with a `Retry-After` header.

    ## Caching
    The API supports HTTP caching with ETags. Include the `If-None-Match`
    header with the ETag from a previous response to receive a 304 Not Modified
    response if the data hasn't changed.

    ## Pagination
    List endpoints support cursor-based pagination. Use the `cursor` parameter
    with the value from `pagination.next_cursor` in the response to fetch the next page.
    Paginated responses also include a `Link` header with `rel="next"` pointing to the next page URL.

    ## Error Handling
    Errors follow the RFC 9457 Problem Details format with appropriate HTTP status codes.
  contact:
    name: API Support
    email: hello@nordicfinancialnews.com
paths:
  "/api/v1/articles/{id}":
    get:
      summary: Get Article Details
      tags:
      - Articles
      security:
      - bearer_auth: []
      description: 'Returns the details of a single article: an English headline,
        a short summary, key points, and associated companies, along with `article_url`
        pointing to the full original-language article at the source. Supports conditional
        requests via `If-None-Match` for efficient caching.'
      parameters:
      - name: id
        in: path
        required: true
        description: Article ID (e.g. `art_abc123def`).
        example: art_abc123def
        schema:
          type: string
      - name: If-None-Match
        in: header
        required: false
        description: ETag value from a previous response. Returns `304 Not Modified`
          if the article has not changed.
        schema:
          type: string
      responses:
        '200':
          description: Article found
          content:
            application/json:
              schema:
                type: object
                properties:
                  article:
                    "$ref": "#/components/schemas/ArticleDetail"
        '404':
          description: Article not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
        '401':
          description: Unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/meta":
    get:
      summary: Get API metadata
      tags:
      - Meta
      security:
      - bearer_auth: []
      description: Returns API metadata including version, capabilities, supported
        fields, content types, and rate limit policy.
      responses:
        '200':
          description: API metadata retrieved
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: string
                    example: v1
                  capabilities:
                    type: array
                    items:
                      type: string
                  supported_fields:
                    type: object
                    properties:
                      articles:
                        type: array
                        items:
                          type: string
                      stories:
                        type: array
                        items:
                          type: string
                      categories:
                        type: array
                        items:
                          type: string
                      companies:
                        type: array
                        items:
                          type: string
                      countries:
                        type: array
                        items:
                          type: string
                      exchanges:
                        type: array
                        items:
                          type: string
                  content_types:
                    type: array
                    items:
                      type: string
                  rate_limit_policy:
                    type: string
                  supported_countries:
                    type: array
                    items:
                      type: string
                  supported_scopes:
                    type: array
                    items:
                      type: string
                required:
                - version
                - capabilities
                - supported_fields
                - content_types
        '401':
          description: Unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/search":
    get:
      summary: Search
      tags:
      - Search
      security:
      - bearer_auth: []
      description: Search across articles, stories, companies, countries, exchanges,
        and indices. Results are grouped by resource type and ranked by relevance.
        Use `type` to limit which resource types are searched.
      parameters:
      - name: q
        in: query
        required: true
        description: Full-text search query (2-200 characters).
        example: Volvo
        schema:
          type: string
      - name: type
        in: query
        required: false
        description: 'Comma-separated list of resource types to search. Defaults to
          all types. Valid values: `articles`, `stories`, `companies`, `countries`,
          `exchanges`, `indices`.'
        example: articles,companies
        schema:
          type: string
      - name: limit
        in: query
        required: false
        description: Maximum number of results per resource type (default 5, max 25).
        example: 5
        schema:
          type: integer
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SearchResult"
        '400':
          description: Invalid search query
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/problem_details"
        '401':
          description: Unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/sources":
    get:
      summary: List Sources
      tags:
      - Sources
      security:
      - bearer_auth: []
      description: Returns a paginated list of enabled news sources, ordered alphabetically
        by name. Use source IDs to filter articles and stories via the `sources` parameter
        on the articles and stories list endpoints.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of sources to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response.
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: 'Comma-separated list of fields to include. Available fields:
          `id`, `name`, `domain`, `country`.'
        example: id,name,domain
        schema:
          type: string
      - name: If-None-Match
        in: header
        required: false
        description: ETag value from a previous response. Returns `304 Not Modified`
          if data has not changed.
        schema:
          type: string
      responses:
        '200':
          description: Sources retrieved
          content:
            application/json:
              examples:
                basic_response:
                  value:
                    sources:
                    - id: abc123def456
                      name: Dagens Industri
                      domain: di.se
                      country: SE
                    - id: ghi789jkl012
                      name: Dagens Nyheter
                      domain: dn.se
                      country: SE
                    pagination:
                      count: 2
                      next_cursor:
              schema:
                type: object
                properties:
                  sources:
                    type: array
                    items:
                      "$ref": "#/components/schemas/SourceSummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                        example: 25
                      next_cursor:
                        type: string
                        nullable: true
        '401':
          description: Unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/stories":
    get:
      summary: List Stories
      tags:
      - Stories
      security:
      - bearer_auth: []
      description: Returns a paginated list of published stories ordered by publication
        date (newest first). Each story synthesizes coverage of a single developing
        event from multiple source articles into one narrative. Supports filtering
        by country, category, source, and company ticker. Use `GET /api/v1/sources`
        to discover source IDs for the `sources` filter. Use `updated_after` for incremental
        syncing, or `q` for full-text search ranked by relevance.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of stories to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor returned as `pagination.next_cursor`
          from a previous response. Mutually exclusive with `q`.
        schema:
          type: string
      - name: updated_after
        in: query
        required: false
        description: 'ISO 8601 datetime. Returns only stories updated after this timestamp.
          Use this for polling-based real-time updates: store the timestamp of your
          last sync, then request only newer results on each poll (see the Real-Time
          Updates guide at https://docs.nordicfinancialnews.com). Disables cursor
          pagination — all matching results are returned in a single response.'
        example: '2024-01-01T00:00:00Z'
        schema:
          type: string
      - name: q
        in: query
        required: false
        description: Full-text search query (2-200 characters). Results are ranked
          by relevance instead of publication date. Mutually exclusive with `cursor`.
          Disables caching.
        example: Volvo earnings
        schema:
          type: string
      - name: country
        in: query
        required: false
        description: Filter by country using ISO 3166-1 alpha-2 code (e.g. `SE`, `NO`,
          `DK`). Comma-separated for multiple (e.g. `SE,NO`). Case-insensitive.
        example: SE
        schema:
          type: string
      - name: category
        in: query
        required: false
        description: Filter by category ID.
        example: cat_earnings1
        schema:
          type: string
      - name: sources
        in: query
        required: false
        description: Comma-separated list of source IDs (max 25). Use `GET /api/v1/sources`
          to discover source IDs. Also accepts array form (`sources[]=id1&sources[]=id2`).
          Returns stories with at least one article from a listed source.
        example: abc123def456,ghi789jkl012
        schema:
          type: string
      - name: ticker
        in: query
        required: false
        description: Filter by company stock ticker symbol (e.g. `VOLV-B`). Accepts
          full tickers with exchange suffix (e.g. `VOLV-B.ST`). Returns stories mentioning
          the company.
        example: VOLV-B
        schema:
          type: string
      - name: company
        in: query
        required: false
        description: Filter to a single company by its `id` (as returned by the companies
          endpoints). Complements `ticker`; use this to reach companies without a
          stock listing.
        example: cmpny1234567
        schema:
          type: string
      - name: exchange
        in: query
        required: false
        description: Filter by exchange using ISO 10383 Market Identifier Code (e.g.
          `XSTO` for Nasdaq Stockholm, `XCSE` for Nasdaq Copenhagen, `XHEL` for Nasdaq
          Helsinki, `XOSL` for Oslo Børs). Returns stories about companies actively
          listed there. Case-insensitive.
        example: XSTO
        schema:
          type: string
      - name: index
        in: query
        required: false
        description: Filter by stock index id or symbol (e.g. `OMXS30`, `OMXC25`,
          `OMXH25`, `OBX`). Returns stories about companies in that index. Case-insensitive
          for symbols.
        example: OMXS30
        schema:
          type: string
      - name: sector
        in: query
        required: false
        description: 'Filter by company sector. Case-sensitive; must exactly match
          one of: `Communication Services`, `Consumer Discretionary`, `Consumer Staples`,
          `Energy`, `Financials`, `Health Care`, `Industrials`, `Information Technology`,
          `Materials`, `Real Estate`, `Utilities`.'
        example: Industrials
        schema:
          type: string
      - name: watchlist
        in: query
        required: false
        description: Restricts to stories mentioning companies in a single watchlist,
          identified by its `id` (from the List Watchlists endpoint). An unknown id
          returns no stories. Requires `read:watchlist` scope.
        example: wl_8fk2a1b3c4d5
        schema:
          type: string
      - name: published_after
        in: query
        required: false
        description: ISO 8601 datetime. Returns stories published on or after this
          timestamp.
        example: '2024-06-01T00:00:00Z'
        schema:
          type: string
      - name: published_before
        in: query
        required: false
        description: ISO 8601 datetime. Returns stories published before this timestamp.
        example: '2024-12-31T23:59:59Z'
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: 'Comma-separated list of fields to include in the response. Available
          fields: `id`, `title`, `summary`, `published_at`, `article_count`, `article_ids`,
          `country`, `category`, `topics`, `companies`.'
        example: id,title,summary,published_at
        schema:
          type: string
      - name: If-None-Match
        in: header
        required: false
        description: ETag value from a previous response. Returns `304 Not Modified`
          if data has not changed.
        schema:
          type: string
      responses:
        '200':
          description: Stories retrieved
          content:
            application/json:
              schema:
                type: object
                properties:
                  stories:
                    type: array
                    items:
                      "$ref": "#/components/schemas/StorySummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                        example: 25
                      next_cursor:
                        type: string
                        nullable: true
        '401':
          description: Unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/stories/{id}":
    get:
      summary: Get Story Details
      tags:
      - Stories
      security:
      - bearer_auth: []
      description: Returns the full details of a single story, including content in
        Markdown format, associated articles, and companies. Supports conditional
        requests via `If-None-Match` for efficient caching.
      parameters:
      - name: id
        in: path
        required: true
        description: Story ID (e.g. `sty_abc123def`).
        example: sty_abc123def
        schema:
          type: string
      - name: If-None-Match
        in: header
        required: false
        description: ETag value from a previous response. Returns `304 Not Modified`
          if the story has not changed.
        schema:
          type: string
      responses:
        '200':
          description: Story found
          content:
            application/json:
              schema:
                type: object
                properties:
                  story:
                    "$ref": "#/components/schemas/StoryDetail"
        '404':
          description: Story not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/articles":
    get:
      summary: List Articles
      tags:
      - Articles
      security:
      - bearer_auth: []
      description: Returns a paginated list of live articles ordered by publication
        date (newest first). Each article includes an English headline, a short summary,
        and key points, with `article_url` linking to the full original-language article
        at the source. Supports filtering by country, category, source, company ticker,
        and content type. Use `GET /api/v1/sources` to discover source IDs for the
        `sources` filter. Use `updated_after` for incremental syncing, or `q` for
        full-text search ranked by relevance. Use `ids` for batch lookup of specific
        articles. Responses include cursor-based pagination and support field projection
        to minimize payload size.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of articles to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor returned as `pagination.next_cursor`
          from a previous response. Mutually exclusive with `q`.
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: 'Comma-separated list of fields to include in the response. Reduces
          payload size. Available fields: `id`, `title`, `article_url`, `published_at`,
          `content_type`, `source`, `company_ids`, `country`, `category`.'
        example: id,title,published_at
        schema:
          type: string
      - name: updated_after
        in: query
        required: false
        description: 'ISO 8601 datetime. Returns only articles updated after this
          timestamp. Use this for polling-based real-time updates: store the timestamp
          of your last sync, then request only newer results on each poll (see the
          Real-Time Updates guide at https://docs.nordicfinancialnews.com). Disables
          cursor pagination — all matching results are returned in a single response.'
        example: '2024-01-01T00:00:00Z'
        schema:
          type: string
      - name: q
        in: query
        required: false
        description: Full-text search query (2-200 characters). Results are ranked
          by relevance instead of publication date. Mutually exclusive with `cursor`.
          Disables caching.
        example: Volvo earnings
        schema:
          type: string
      - name: country
        in: query
        required: false
        description: Filter by country using ISO 3166-1 alpha-2 code (e.g. `SE`, `NO`,
          `DK`). Comma-separated for multiple (e.g. `SE,NO`). Case-insensitive.
        example: SE
        schema:
          type: string
      - name: sources
        in: query
        required: false
        description: Comma-separated list of source IDs (max 25). Use `GET /api/v1/sources`
          to discover source IDs. Also accepts array form (`sources[]=id1&sources[]=id2`).
          Disabled sources resolve to empty results.
        example: abc123def456,ghi789jkl012
        schema:
          type: string
      - name: ticker
        in: query
        required: false
        description: Filter by company stock ticker symbol (e.g. `VOLV-B`). Accepts
          full tickers with exchange suffix (e.g. `VOLV-B.ST`). Returns articles mentioning
          the company.
        example: VOLV-B
        schema:
          type: string
      - name: company
        in: query
        required: false
        description: Filter to a single company by its `id` (as returned by the companies
          endpoints). Complements `ticker`; use this to reach companies without a
          stock listing.
        example: cmpny1234567
        schema:
          type: string
      - name: exchange
        in: query
        required: false
        description: Filter by exchange using ISO 10383 Market Identifier Code (e.g.
          `XSTO` for Nasdaq Stockholm, `XCSE` for Nasdaq Copenhagen, `XHEL` for Nasdaq
          Helsinki, `XOSL` for Oslo Børs). Returns articles about companies actively
          listed there. Case-insensitive.
        example: XSTO
        schema:
          type: string
      - name: index
        in: query
        required: false
        description: Filter by stock index id or symbol (e.g. `OMXS30`, `OMXC25`,
          `OMXH25`, `OBX`). Returns articles about companies in that index. Case-insensitive
          for symbols.
        example: OMXS30
        schema:
          type: string
      - name: sector
        in: query
        required: false
        description: 'Filter by company sector. Case-sensitive; must exactly match
          one of: `Communication Services`, `Consumer Discretionary`, `Consumer Staples`,
          `Energy`, `Financials`, `Health Care`, `Industrials`, `Information Technology`,
          `Materials`, `Real Estate`, `Utilities`.'
        example: Industrials
        schema:
          type: string
      - name: content_type
        in: query
        required: false
        description: Filter by article content type.
        example: news
        schema:
          type: string
          enum:
          - news
          - analysis
          - press_release
          - market_commentary
          - market_news
          - trading_halt
          - trading_event
          - other
      - name: published_after
        in: query
        required: false
        description: ISO 8601 datetime. Returns articles published on or after this
          timestamp.
        example: '2024-06-01T00:00:00Z'
        schema:
          type: string
      - name: published_before
        in: query
        required: false
        description: ISO 8601 datetime. Returns articles published before this timestamp.
        example: '2024-12-31T23:59:59Z'
        schema:
          type: string
      - name: listed
        in: query
        required: false
        description: When `true`, only return articles mentioning at least one publicly
          listed company.
        example: 'true'
        schema:
          type: string
      - name: watchlist
        in: query
        required: false
        description: Restricts to articles mentioning companies in a single watchlist,
          identified by its `id` (from the List Watchlists endpoint). An unknown id
          returns no articles. Requires `read:watchlist` scope.
        example: wl_8fk2a1b3c4d5
        schema:
          type: string
      - name: ids
        in: query
        required: false
        description: Comma-separated list of article IDs for batch lookup (max 100).
          Cannot be combined with `cursor` or `updated_after`.
        example: art_abc123,art_def456
        schema:
          type: string
      responses:
        '200':
          description: Articles retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  articles:
                    type: array
                    items:
                      "$ref": "#/components/schemas/ArticleSummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                        example: 25
                      next_cursor:
                        type: string
                        nullable: true
                required:
                - articles
        '401':
          description: unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/calendar_events":
    get:
      summary: List Calendar Events
      tags:
      - Calendar Events
      security:
      - bearer_auth: []
      description: |-
        Returns a paginated list of scheduled Nordic financial calendar events — earnings reports (Q1 / H1 / FY etc.), earnings calls, trading updates, AGMs/EGMs, capital markets days, conference presentations, dividends, and M&A milestones (announcements, offer deadlines, completions).

        Each event is tied to a single company and is sorted by `scheduled_at` ascending (soonest first). `scheduled_at` is always in UTC; for local-time display use the `local_time` field (`scheduled_at` rendered in the issuer's `timezone`, ISO 8601 with offset). Prefer `local_time` for the calendar date — a day-precision event is stored at local midnight, so its UTC `scheduled_at` falls on the previous day for ahead-of-UTC (Nordic) zones. `title` and `description` are always English regardless of the source-article language.

        ## Dividends
        Each dividend row carries a nested `dividend` object with `ex_date`, `record_date`, `payment_date`, `declaration_date`, and `kind`. `scheduled_at` follows the priority **ex-date > record-date > payment-date** — whichever is explicitly stated, in that order. `dividend` is `null` for non-dividend events.

        ## Default window
        By default only upcoming events are returned, from today onward by the issuer's local date (so events scheduled for today stay listed throughout the day). Pass `scheduled_after` or `scheduled_before` to query a specific window — this also surfaces past events, which carry `status: published` (events flip from `scheduled` to `published` ~2 days after their date).

        ## Status
        List endpoints return `scheduled` + `published` events and **exclude `cancelled`** by default. Pass the `status` parameter to filter (`status=cancelled` to retrieve cancellations, or `scheduled`/`published` to narrow). Discarded events are never returned. Note: a `scheduled → cancelled` transition is *not* surfaced by default `updated_after` polling — to track cancellations, poll with `status=cancelled` or subscribe to cancellation alerts.

        ## Filtering
        Compose `ticker`, `country`, `exchange`, `index`, `sector`, and `event_type` to narrow the result set. All filters are intersected.

        ## Polling for updates
        Use `updated_after` with the timestamp of your last sync to fetch only events that have changed since. This disables cursor pagination and returns all matching events in one response.

        ## Pagination
        List responses include `pagination.next_cursor` when more pages are available. Pass it back as the `cursor` parameter on the next request. Cursors are opaque — do not parse them.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of events to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor returned as `pagination.next_cursor`
          from a previous response. Mutually exclusive with `updated_after`.
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: 'Comma-separated list of fields to include in the response. Reduces
          payload size. `id` is always included. Available fields: `title`, `description`,
          `event_type`, `status`, `date_precision`, `fiscal_period`, `scheduled_at`,
          `timezone`, `local_time`, `scheduled_at_changed_at`, `source_article_id`,
          `company_id`, `country`, `dividend`, `updated_at`.'
        example: title,event_type,scheduled_at,company_id,dividend
        schema:
          type: string
      - name: updated_after
        in: query
        required: false
        description: 'ISO 8601 datetime. Returns only events updated after this timestamp.
          Use for incremental polling: store the timestamp of your last sync and pass
          it on the next request. Disables cursor pagination.'
        example: '2026-03-01T00:00:00Z'
        schema:
          type: string
      - name: ticker
        in: query
        required: false
        description: Filter by company stock ticker (e.g. `VOLV-B`). Also accepts
          exchange-suffixed form (e.g. `VOLV-B.ST`).
        example: VOLV-B
        schema:
          type: string
      - name: company
        in: query
        required: false
        description: Filter to a single company by its `id` (as returned by the companies
          endpoints). Complements `ticker`; use this to reach companies without a
          stock listing.
        example: cmpny1234567
        schema:
          type: string
      - name: country
        in: query
        required: false
        description: Filter by country using ISO 3166-1 alpha-2 code (e.g. `SE`, `DK`,
          `NO`, `FI`, `IS`). Comma-separated for multiple (e.g. `SE,NO`). Case-insensitive.
        example: SE
        schema:
          type: string
      - name: exchange
        in: query
        required: false
        description: Filter by exchange using ISO 10383 Market Identifier Code (e.g.
          `XSTO` for Nasdaq Stockholm, `XCSE` for Nasdaq Copenhagen, `XHEL` for Nasdaq
          Helsinki, `XOSL` for Oslo Børs). Case-insensitive.
        example: XSTO
        schema:
          type: string
      - name: index
        in: query
        required: false
        description: Filter by stock index id or symbol (e.g. `OMXS30`, `OMXC25`,
          `OMXH25`, `OBX`). Returns events only for companies in that index. Case-insensitive
          for symbols.
        example: OMXS30
        schema:
          type: string
      - name: sector
        in: query
        required: false
        description: 'Filter by company sector. Must exactly match one of the standard
          sector names: `Communication Services`, `Consumer Discretionary`, `Consumer
          Staples`, `Energy`, `Financials`, `Health Care`, `Industrials`, `Information
          Technology`, `Materials`, `Real Estate`, `Utilities`.'
        example: Industrials
        schema:
          type: string
      - name: event_type
        in: query
        required: false
        description: Filter by event type. Accepts a single value or a comma-separated
          list for multiple types.
        example: earnings_report,earnings_call
        schema:
          type: string
          enum:
          - earnings_report
          - earnings_call
          - agm
          - egm
          - dividend
          - capital_markets_day
          - trading_update
          - conference_presentation
          - ma_announcement
          - ma_offer_deadline
          - ma_completion
          - listing
          - delisting
      - name: status
        in: query
        required: false
        description: 'Filter by lifecycle status. Omitted: returns `scheduled` + past
          `published` events and excludes `cancelled`. Pass `cancelled` to retrieve
          cancellations, or `scheduled`/`published` to narrow.'
        example: cancelled
        schema:
          type: string
          enum:
          - scheduled
          - published
          - cancelled
      - name: scheduled_after
        in: query
        required: false
        description: ISO 8601 datetime. Only events at or after this time. Passing
          this parameter disables the default upcoming-only floor, allowing past events
          to be returned.
        example: '2026-01-01T00:00:00Z'
        schema:
          type: string
      - name: scheduled_before
        in: query
        required: false
        description: ISO 8601 datetime. Only events before this time. Passing this
          parameter disables the default upcoming-only floor, allowing past events
          to be returned.
        example: '2026-12-31T23:59:59Z'
        schema:
          type: string
      - name: watchlist
        in: query
        required: false
        description: Restricts to events for companies in a single watchlist, identified
          by its `id` (from the List Watchlists endpoint). An unknown id returns no
          events. Requires `read:watchlist` scope.
        example: wl_8fk2a1b3c4d5
        schema:
          type: string
      responses:
        '200':
          description: Calendar events retrieved successfully
          content:
            application/json:
              examples:
                upcoming_events_with_dividend:
                  value:
                    calendar_events:
                    - id: cale_q3volvo1
                      title: Volvo AB Q3 2026 interim report
                      description: Publication of the interim report for Q3 2026 and
                        earnings call at 10:00 CET.
                      event_type: earnings_report
                      status: scheduled
                      date_precision: exact
                      fiscal_period: q3_2026
                      scheduled_at: '2026-10-22T05:00:00.000Z'
                      timezone: Europe/Stockholm
                      local_time: '2026-10-22T07:00:00.000+02:00'
                      scheduled_at_changed_at:
                      source_article_id: art_xyz45678
                      company_id: w55fcw3pbg3p
                      country: SE
                      dividend:
                      updated_at: '2026-03-01T09:00:00.000Z'
                    - id: cale_dividlim1
                      title: Lime Technologies dividend 2026
                      description: Cash dividend resolved at AGM on 2026-04-23.
                      event_type: dividend
                      status: scheduled
                      date_precision: day
                      fiscal_period:
                      scheduled_at: '2026-04-24T22:00:00.000Z'
                      timezone: Europe/Stockholm
                      local_time: '2026-04-25T00:00:00.000+02:00'
                      scheduled_at_changed_at:
                      source_article_id: art_limediv12
                      company_id: limeabxxx234
                      country: SE
                      dividend:
                        ex_date: '2026-04-25'
                        record_date: '2026-04-28'
                        payment_date: '2026-05-06'
                        declaration_date: '2026-04-23'
                        kind: ordinary
                      updated_at: '2026-04-23T08:30:00.000Z'
                    pagination:
                      count: 2
                      next_cursor: IlVtcHByZXRPQVZTWnJrVS4uLiI=
              schema:
                type: object
                required:
                - calendar_events
                - pagination
                properties:
                  calendar_events:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CalendarEventSummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                        example: 2
                      next_cursor:
                        type: string
                        nullable: true
                        description: Opaque cursor to fetch the next page. `null`
                          when there are no more results.
                        example: IlVtcHByZXRPQVZTWnJrVS4uLiI=
        '400':
          description: Invalid parameter
          content:
            application/json:
              examples:
                invalid_event_type:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/invalid-parameter
                    title: Invalid parameter
                    status: 400
                    detail: 'Unknown event_type(s): not_a_type. Valid values: earnings_report,
                      earnings_call, agm, egm, dividend, capital_markets_day, trading_update,
                      conference_presentation, ma_announcement, ma_offer_deadline,
                      ma_completion, listing, delisting'
                    instance: urn:request:a1b2c3
                    parameter: event_type
              schema:
                "$ref": "#/components/schemas/problem_details"
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Authentication required
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:a1b2c3
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/calendar_events/{public_id}":
    parameters:
    - name: public_id
      in: path
      required: true
      description: The calendar event ID, as returned in the `id` field of list responses.
      example: cale_q3volvo1
      schema:
        type: string
    get:
      summary: Get Calendar Event
      tags:
      - Calendar Events
      security:
      - bearer_auth: []
      description: |-
        Fetches a single scheduled calendar event by ID. Cancelled or discarded events return 404.

        Responses are cached with `ETag` and `Last-Modified` headers and a `private, max-age=300` `Cache-Control`; clients can send `If-None-Match` to receive a `304 Not Modified`.
      responses:
        '200':
          description: Calendar event retrieved
          content:
            application/json:
              examples:
                earnings_report:
                  value:
                    calendar_event:
                      id: cale_q3volvo1
                      title: Volvo AB Q3 2026 interim report
                      description: Publication of the interim report for Q3 2026 and
                        earnings call at 10:00 CET.
                      event_type: earnings_report
                      status: scheduled
                      date_precision: exact
                      fiscal_period: q3_2026
                      scheduled_at: '2026-10-22T05:00:00.000Z'
                      timezone: Europe/Stockholm
                      local_time: '2026-10-22T07:00:00.000+02:00'
                      scheduled_at_changed_at:
                      source_article_id: art_xyz45678
                      company_id: w55fcw3pbg3p
                      country: SE
                      dividend:
                      updated_at: '2026-03-01T09:00:00.000Z'
              schema:
                type: object
                required:
                - calendar_event
                properties:
                  calendar_event:
                    "$ref": "#/components/schemas/CalendarEventDetail"
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/problem_details"
        '404':
          description: Calendar event not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:a1b2c3
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/categories":
    get:
      summary: List Categories
      tags:
      - Categories
      security:
      - bearer_auth: []
      description: Returns all enabled article categories, ordered alphabetically
        by name. Categories are used to classify articles and stories (e.g. Earnings,
        M&A, IPO). Use category IDs to filter articles and stories by topic.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of categories to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response.
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: 'Comma-separated list of fields to include. Available fields:
          `id`, `name`.'
        example: id,name
        schema:
          type: string
      responses:
        '200':
          description: Categories retrieved successfully
          content:
            application/json:
              examples:
                basic_response:
                  value:
                    categories:
                    - id: cat_business12
                      name: Business
                    - id: cat_tech123456
                      name: Technology
                    pagination:
                      count: 2
                      next_cursor:
              schema:
                type: object
                properties:
                  categories:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CategorySummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
                required:
                - categories
        '401':
          description: unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
        '403':
          description: forbidden - missing scope
          content:
            application/json:
              examples:
                forbidden:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-insufficient
                    title: Forbidden
                    status: 403
                    detail: Insufficient scope for this resource
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/companies":
    get:
      summary: List Companies
      tags:
      - Companies
      security:
      - bearer_auth: []
      description: Returns a paginated list of companies covered in Nordic financial
        news, ordered alphabetically by name. Use `q` for full-text search across
        company names, ranked by relevance.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of companies to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor returned as `pagination.next_cursor`
          from a previous response. Mutually exclusive with `q`.
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: 'Comma-separated list of fields to include in the response. Available
          fields: `id`, `name`, `slug`, `ticker`.'
        example: id,name,ticker
        schema:
          type: string
      - name: q
        in: query
        required: false
        description: Full-text search query (2-200 characters). Searches company names
          and aliases. Results are ranked by relevance instead of alphabetical order.
          Mutually exclusive with `cursor`.
        example: Volvo
        schema:
          type: string
      - name: country
        in: query
        required: false
        description: Filter by country ISO 3166-1 alpha-2 code (e.g. `SE`, `NO`, `DK`).
          Comma-separated for multiple (e.g. `SE,NO`). Case-insensitive.
        example: SE
        schema:
          type: string
      - name: exchange
        in: query
        required: false
        description: Filter by exchange MIC code (e.g. `XSTO`, `XOSL`, `XCSE`).
        example: XSTO
        schema:
          type: string
      - name: sector
        in: query
        required: false
        description: Filter by industry sector (e.g. `Financials`, `Industrials`,
          `Information Technology`).
        example: Financials
        schema:
          type: string
      - name: listed
        in: query
        required: false
        description: When `true`, only return companies with at least one stock exchange
          listing.
        example: 'true'
        schema:
          type: string
      - name: watchlist
        in: query
        required: false
        description: When `true`, only return companies in the authenticated user's
          watchlist. Requires `read:watchlist` scope.
        example: 'true'
        schema:
          type: string
      - name: is_active
        in: query
        required: false
        description: Filter by active status. When `true`, only return active companies.
          When `false`, only return inactive companies.
        example: 'true'
        schema:
          type: string
      responses:
        '200':
          description: Companies retrieved successfully
          content:
            application/json:
              examples:
                companies_list:
                  value:
                    companies:
                    - id: comp_volvo123
                      name: Volvo AB
                      slug: volvo-ab
                      ticker: VOLV-B
                      is_active: true
                      exchange:
                        id: exch_xsto1234
                        name: Nasdaq Stockholm
                        mic_code: XSTO
                    pagination:
                      count: 1
                      next_cursor:
              schema:
                type: object
                properties:
                  companies:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CompanySummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
                required:
                - companies
  "/api/v1/companies/{identifier}":
    parameters:
    - name: identifier
      in: path
      required: true
      description: Company ID or ticker symbol
      schema:
        type: string
    get:
      summary: Get Company Details
      tags:
      - Companies
      security:
      - bearer_auth: []
      description: Returns detailed information about a single company, including
        description, sector, relationships, and stock listings. Look up by company
        ID (e.g. `comp_apple123`) or ticker symbol (e.g. `AAPL`, `VOLV-B`). Lookup
        also accepts the full ticker including exchange suffix (e.g. `VOLV-B.ST`).
      responses:
        '200':
          description: Company retrieved successfully
          content:
            application/json:
              examples:
                company_detail:
                  value:
                    company:
                      id: w55fcw3pbg3p
                      name: Volvo Car AB
                      slug: volvo-car-ab
                      ticker: VOLCAR-B
                      is_active: true
                      description: Volvo Car AB (publ.) designs, develops, manufactures,
                        markets, assembles, and sells passenger cars in Sweden and
                        internationally.
                      sector: Consumer Discretionary
                      website: https://www.volvocars.com/se
                      country:
                        id: o5kwz4bzzqkm
                        name: Sweden
                        iso2_code: SE
                      article_count: 176
                      story_count: 11
                      parent_company:
                        id: himhcsn81a3r
                        name: Geely Holding
                        ticker:
                      subsidiaries:
                      - id: 6lv8cihz3948
                        name: Polestar
                        ticker:
                      indices: []
                      stock_listings:
                      - ticker: VOLCAR-B
                        primary: true
                        exchange:
                          id: 64hn2d7ul6kr
                          name: Nasdaq Stockholm
                          mic_code: XSTO
                      registry:
                        source: Bolagsverket
                        registered_name: Volvo Personvagnar Aktiebolag
                        company_form: Aktiebolag
                        city: GÖTEBORG
                        founded: '1960-10-28'
                        employees:
                        industry: Tillverkning av personbilar och andra lätta motorfordon
                        bankruptcy:
                        under_liquidation:
                        share_capital:
                        institutional_sector:
                        deregistered_at:
                        deregistration_reason:
                      updated_at: '2026-03-28T11:22:22.697Z'
              schema:
                type: object
                properties:
                  company:
                    "$ref": "#/components/schemas/CompanyDetail"
                required:
                - company
        '404':
          description: Company not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/companies/{identifier}/articles":
    parameters:
    - name: identifier
      in: path
      required: true
      description: Company ID or ticker symbol
      schema:
        type: string
    get:
      summary: List Company Articles
      tags:
      - Companies
      security:
      - bearer_auth: []
      description: Returns articles that mention a specific company, ordered by publication
        date (newest first). Use `primary_only=true` to return only articles where
        this company is a primary subject of the article.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of articles to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response.
        schema:
          type: string
      - name: primary_only
        in: query
        required: false
        description: When `true`, only return articles where this company is the primary
          subject (not just mentioned).
        schema:
          type: boolean
      responses:
        '200':
          description: Company articles retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  articles:
                    type: array
                    items:
                      "$ref": "#/components/schemas/ArticleSummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
                required:
                - articles
  "/api/v1/companies/{identifier}/stories":
    parameters:
    - name: identifier
      in: path
      required: true
      description: Company ID or ticker symbol
      schema:
        type: string
    get:
      summary: List Company Stories
      tags:
      - Companies
      security:
      - bearer_auth: []
      description: Returns stories that mention a specific company, ordered by publication
        date (newest first).
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of stories to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response.
        schema:
          type: string
      responses:
        '200':
          description: Company stories retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  stories:
                    type: array
                    items:
                      "$ref": "#/components/schemas/StorySummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
                required:
                - stories
  "/api/v1/countries":
    get:
      summary: List Countries
      tags:
      - Countries
      security:
      - bearer_auth: []
      description: Returns the Nordic countries covered by the API, ordered alphabetically.
        Use the country `id` or `iso2_code` to filter articles, stories, and companies
        by country.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of countries to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response.
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: 'Comma-separated list of fields to include. Available fields:
          `id`, `name`, `iso2_code`.'
        example: id,name,iso2_code
        schema:
          type: string
      responses:
        '200':
          description: Countries retrieved successfully
          content:
            application/json:
              examples:
                countries_list:
                  value:
                    countries:
                    - id: ctry_sweden12
                      name: Sweden
                      iso2_code: SE
                    pagination:
                      count: 1
                      next_cursor:
              schema:
                type: object
                properties:
                  countries:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CountrySummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
                required:
                - countries
  "/api/v1/countries/{identifier}":
    parameters:
    - name: identifier
      in: path
      required: true
      description: Country ID, ISO 3166-1 alpha-2 code (e.g. `SE`), or slug (e.g.
        `sweden`)
      schema:
        type: string
    get:
      summary: Get Country Details
      tags:
      - Countries
      security:
      - bearer_auth: []
      description: Returns details for a single country, including its stock exchanges.
        Look up by country ID or ISO 3166-1 alpha-2 code (e.g. `SE`).
      responses:
        '200':
          description: Country retrieved successfully
          content:
            application/json:
              examples:
                country_detail:
                  value:
                    country:
                      id: ctry_sweden12
                      name: Sweden
                      iso2_code: SE
                      exchanges:
                      - id: exch_xsto1234
                        name: Nasdaq Stockholm
                        mic_code: XSTO
                        acronym: STO
              schema:
                type: object
                properties:
                  country:
                    "$ref": "#/components/schemas/CountrySummary"
                required:
                - country
        '404':
          description: Country not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/exchanges":
    get:
      summary: List Exchanges
      tags:
      - Exchanges
      security:
      - bearer_auth: []
      description: Returns active Nordic stock exchanges, ordered alphabetically.
        Supports filtering by country.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of exchanges to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response.
        schema:
          type: string
      - name: country
        in: query
        required: false
        description: Filter by country using ISO 3166-1 alpha-2 code. Comma-separated
          for multiple (e.g. `SE,NO`). Case-insensitive.
        example: SE
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: 'Comma-separated list of fields to include in the response. Available
          fields: `id`, `name`, `mic_code`, `acronym`, `country`.'
        example: id,name,mic_code
        schema:
          type: string
      responses:
        '200':
          description: Exchanges retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  exchanges:
                    type: array
                    items:
                      "$ref": "#/components/schemas/ExchangeSummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
        '401':
          description: unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
        '403':
          description: forbidden - insufficient scope
          content:
            application/json:
              examples:
                forbidden:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-insufficient
                    title: Forbidden
                    status: 403
                    detail: Insufficient scope for this resource
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/exchanges/{identifier}":
    parameters:
    - name: identifier
      in: path
      required: true
      description: Exchange ID or MIC code (e.g. `XSTO`)
      schema:
        type: string
    get:
      summary: Get Exchange Details
      tags:
      - Exchanges
      security:
      - bearer_auth: []
      description: Returns detailed information about a stock exchange, including
        currency, timezone, and location. Look up by exchange ID (e.g. `exch_nasdaq1`)
        or MIC code (e.g. `XSTO`).
      responses:
        '200':
          description: Exchange retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  exchange:
                    "$ref": "#/components/schemas/ExchangeDetail"
                required:
                - exchange
        '404':
          description: Exchange not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/exchanges/{identifier}/companies":
    parameters:
    - name: identifier
      in: path
      required: true
      description: Exchange ID or MIC code (e.g. `XSTO`)
      schema:
        type: string
    get:
      summary: List Exchange Companies
      tags:
      - Exchanges
      security:
      - bearer_auth: []
      description: Returns companies listed on a specific exchange, ordered alphabetically.
        The `ticker` and `exchange` shown for each company reflect its primary listing,
        which may be on a different exchange if the company is listed on multiple
        exchanges.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of companies to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response.
        schema:
          type: string
      responses:
        '200':
          description: Exchange companies retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  companies:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CompanySummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
                required:
                - companies
        '404':
          description: Exchange not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/health":
    get:
      summary: Health check endpoint
      tags:
      - Health
      description: Returns the health status of the API
      responses:
        '200':
          description: successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                    - ok
                required:
                - status
  "/api/v1/indices":
    get:
      summary: List Indices
      tags:
      - Indices
      security:
      - bearer_auth: []
      description: Returns stock indices, ordered alphabetically by name. Use `exchange`
        to filter by a specific exchange.
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of indices to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response.
        schema:
          type: string
      - name: exchange
        in: query
        required: false
        description: Filter by exchange MIC code.
        example: XSTO
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: 'Comma-separated list of fields to include in the response. Available
          fields: `id`, `name`, `symbol`, `pan_nordic`, `exchange`.'
        example: id,name,symbol
        schema:
          type: string
      responses:
        '200':
          description: Indices retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  indices:
                    type: array
                    items:
                      "$ref": "#/components/schemas/IndexSummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
        '401':
          description: unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
        '403':
          description: forbidden - insufficient scope
          content:
            application/json:
              examples:
                forbidden:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-insufficient
                    title: Forbidden
                    status: 403
                    detail: Insufficient scope for this resource
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/indices/{identifier}":
    parameters:
    - name: identifier
      in: path
      required: true
      description: Index ID or symbol (e.g. `OMXS30`)
      schema:
        type: string
    get:
      summary: Get Index Details
      tags:
      - Indices
      security:
      - bearer_auth: []
      description: 'Returns details for a single index, including description and
        company count. Look up by index ID or symbol (case-insensitive, e.g. `OMXS30`).
        Note: `companies_count` reflects the number of distinct companies in the index,
        which may be less than the number of index constituents when a company has
        multiple share classes listed.'
      responses:
        '200':
          description: Index retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  index:
                    "$ref": "#/components/schemas/IndexDetail"
                required:
                - index
        '404':
          description: Stock index not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/indices/{identifier}/companies":
    parameters:
    - name: identifier
      in: path
      required: true
      description: Index ID or symbol (e.g. `OMXS30`)
      schema:
        type: string
    get:
      summary: List Index Companies
      tags:
      - Indices
      security:
      - bearer_auth: []
      description: Returns companies that are members of a specific index, ordered
        alphabetically. The `ticker` and `exchange` shown for each company reflect
        its primary listing, which may differ from the listing used in this index
        (e.g. a company listed on multiple exchanges).
      parameters:
      - name: limit
        in: query
        required: false
        description: Number of companies to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response.
        schema:
          type: string
      responses:
        '200':
          description: Index companies retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  companies:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CompanySummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
                required:
                - companies
        '404':
          description: Stock index not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/watchlists":
    get:
      summary: List Watchlists
      tags:
      - Watchlists
      security:
      - bearer_auth: []
      description: Returns all watchlists belonging to the authenticated user, ordered
        by position (ascending) then creation date. Each watchlist includes a company
        count but not the full company list — use the Get Watchlist Details endpoint
        to retrieve companies. Requires the `read:watchlist` scope.
      responses:
        '200':
          description: Watchlists retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  watchlists:
                    type: array
                    items:
                      "$ref": "#/components/schemas/WatchlistSummary"
                required:
                - watchlists
        '401':
          description: Unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
  "/api/v1/watchlists/{id}":
    get:
      summary: Get Watchlist Details
      tags:
      - Watchlists
      security:
      - bearer_auth: []
      description: Returns watchlist details along with a paginated list of its companies,
        ordered alphabetically by company name. Hidden and unverified companies are
        excluded. Requires the `read:watchlist` scope.
      parameters:
      - name: id
        in: path
        required: true
        description: Watchlist ID (e.g. `a1b2c3d4e5f6`).
        example: a1b2c3d4e5f6
        schema:
          type: string
      - name: limit
        in: query
        required: false
        description: Number of companies to return per page (default 25, max 100).
        example: 25
        schema:
          type: integer
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor returned as `pagination.next_cursor`
          from a previous response.
        schema:
          type: string
      responses:
        '200':
          description: Watchlist retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  watchlist:
                    "$ref": "#/components/schemas/WatchlistSummary"
                  companies:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CompanySummary"
                  pagination:
                    type: object
                    properties:
                      count:
                        type: integer
                      next_cursor:
                        type: string
                        nullable: true
                required:
                - watchlist
                - companies
        '404':
          description: Watchlist not found
          content:
            application/json:
              examples:
                not_found:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/not-found
                    title: Not Found
                    status: 404
                    detail: The requested resource could not be found
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
        '401':
          description: Unauthorized
          content:
            application/json:
              examples:
                unauthorized:
                  value:
                    type: https://docs.nordicfinancialnews.com/problems/auth-invalid
                    title: Unauthorized
                    status: 401
                    detail: Missing or invalid API key
                    instance: urn:request:abc123
              schema:
                "$ref": "#/components/schemas/problem_details"
servers:
- url: https://nordicfinancialnews.com
  description: Production server
components:
  securitySchemes:
    bearer_auth:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: API Key authentication using Bearer token
  schemas:
    problem_details:
      type: object
      required:
      - type
      - title
      - status
      - detail
      - instance
      properties:
        type:
          type: string
          description: URI that identifies the problem type
        title:
          type: string
          description: Short human-readable summary
        status:
          type: integer
          description: HTTP status code
        detail:
          type: string
          description: Human-readable explanation
        instance:
          type: string
          description: URI that identifies the specific occurrence
    ArticleSummary:
      type: object
      required:
      - id
      - title
      - published_at
      - article_url
      - content_type
      - category
      - source
      - company_ids
      - country
      properties:
        id:
          type: string
          description: Unique article identifier
          example: art_abc123def
        title:
          type: string
          description: Article title in English
          example: Volvo Reports Record Q3 Earnings
        article_url:
          type: string
          description: Link to the original article
          example: https://di.se/articles/volvo-q3-2026
        content_type:
          type: string
          description: Type of article content
          example: news
          enum:
          - news
          - analysis
          - press_release
          - market_commentary
          - market_news
          - trading_halt
          - trading_event
          - other
        published_at:
          type: string
          format: date-time
          description: When the article was published (ISO 8601)
          example: '2026-03-15T09:30:00.000Z'
        category:
          type: object
          description: Article category
          properties:
            id:
              type: string
              example: cat_earnings1
            name:
              type: string
              example: Earnings & Financial Results
        source:
          type: object
          description: The news source that published this article
          properties:
            id:
              type: string
              description: Unique source identifier
              example: abc123def456
            name:
              type: string
              example: Dagens Industri
            domain:
              type: string
              example: di.se
        company_ids:
          type: array
          items:
            type: string
          description: IDs of companies mentioned in the article
          example:
          - uejazctgchj4
        country:
          type: string
          nullable: true
          description: Country code (ISO 3166-1 alpha-2)
          example: SE
    ArticleDetail:
      allOf:
      - "$ref": "#/components/schemas/ArticleSummary"
      - type: object
        properties:
          source_title:
            type: string
            description: Original article title in source language
            example: Volvo rapporterar rekordresultat för Q3
          summary:
            type: string
            description: Article summary
            example: Volvo reported record Q3 earnings driven by strong truck demand...
          updated_at:
            type: string
            format: date-time
            description: When the article was last updated (ISO 8601)
            example: '2026-03-15T10:00:00.000Z'
          story_ids:
            type: array
            items:
              type: string
            description: IDs of related stories
            example:
            - story_volvo_q3
          companies:
            type: array
            description: Companies mentioned in this article, primary company first
            items:
              type: object
              properties:
                id:
                  type: string
                  description: Unique company identifier
                  example: uejazctgchj4
                name:
                  type: string
                  description: Company name
                  example: AB Volvo (Volvo Group)
                ticker:
                  type: string
                  nullable: true
                  description: Primary stock ticker
                  example: VOLV-B
                is_primary:
                  type: boolean
                  description: Whether this company is the primary subject of the
                    article
                  example: true
            example:
            - id: uejazctgchj4
              name: AB Volvo (Volvo Group)
              ticker: VOLV-B
              is_primary: true
          key_points:
            type: array
            items:
              type: string
            description: Key points from the article
            example:
            - Revenue up 15% year-over-year
            - Strong truck demand in Europe
    StorySummary:
      type: object
      required:
      - id
      - title
      - summary
      - article_count
      - published_at
      properties:
        id:
          type: string
          description: Unique story identifier
          example: story_volvo_q3
        title:
          type: string
          description: Story title
          example: Volvo Reports Record Q3 Results
        summary:
          type: string
          description: Story summary
          example: Volvo reported record third-quarter earnings, beating analyst expectations...
        article_count:
          type: integer
          description: Number of articles in this story (matches the length of article_ids)
          example: 4
        published_at:
          type: string
          format: date-time
          description: When the story was first published (ISO 8601)
          example: '2026-03-15T09:30:00.000Z'
        article_ids:
          type: array
          items:
            type: string
          description: IDs of articles in this story, most relevant first
          example:
          - art_abc123def
          - art_def456ghi
        country:
          type: string
          nullable: true
          description: Country code (ISO 3166-1 alpha-2)
          example: SE
        company_ids:
          type: array
          items:
            type: string
          description: IDs of companies mentioned in this story
          example:
          - uejazctgchj4
        category:
          type: object
          description: Story category
          properties:
            id:
              type: string
            name:
              type: string
    StoryDetail:
      allOf:
      - "$ref": "#/components/schemas/StorySummary"
      - type: object
        properties:
          content:
            type: string
            format: markdown
            description: Full story content in Markdown format
            example: "## Volvo Q3 Results\\n\\nVolvo reported record earnings..."
          updated_at:
            type: string
            format: date-time
            description: When the story was last updated (ISO 8601)
            example: '2026-03-15T14:00:00.000Z'
          companies:
            type: array
            description: Companies mentioned in this story
            items:
              type: object
              properties:
                id:
                  type: string
                  description: Unique company identifier
                  example: uejazctgchj4
                name:
                  type: string
                  description: Company name
                  example: AB Volvo (Volvo Group)
                ticker:
                  type: string
                  nullable: true
                  description: Primary stock ticker
                  example: VOLV-B
            example:
            - id: uejazctgchj4
              name: AB Volvo (Volvo Group)
              ticker: VOLV-B
    CategorySummary:
      type: object
      required:
      - id
      - name
      properties:
        id:
          type: string
          description: Unique category identifier
          example: cat_business12
        name:
          type: string
          description: Category display name
          example: Earnings & Financial Results
    SourceSummary:
      type: object
      required:
      - id
      - name
      - domain
      properties:
        id:
          type: string
          description: Unique source identifier
          example: abc123def456
        name:
          type: string
          description: Source display name
          example: Dagens Industri
        domain:
          type: string
          description: Primary domain of the source
          example: di.se
        country:
          type: string
          nullable: true
          description: Country code (ISO 3166-1 alpha-2)
          example: SE
    CompanySummary:
      type: object
      required:
      - id
      - name
      - slug
      properties:
        id:
          type: string
          description: Unique company identifier
          example: w55fcw3pbg3p
        name:
          type: string
          description: Company name
          example: Volvo Car AB
        slug:
          type: string
          description: URL-friendly company name
          example: VOLCAR-B
        ticker:
          type: string
          nullable: true
          description: Primary stock ticker (null if unlisted)
          example: VOLCAR-B
        is_active:
          type: boolean
          description: Whether the company is currently active/operating
          example: true
        exchange:
          type: object
          nullable: true
          description: Primary exchange where the company is listed
          properties:
            id:
              type: string
              description: Exchange identifier
            name:
              type: string
              description: Exchange display name
            mic_code:
              type: string
              description: ISO 10383 Market Identifier Code
    CompanyDetail:
      allOf:
      - "$ref": "#/components/schemas/CompanySummary"
      - type: object
        properties:
          description:
            type: string
            nullable: true
            description: Company description
            example: Volvo Car AB (publ.) designs, develops, manufactures, markets,
              assembles, and sells passenger cars in Sweden and internationally.
          sector:
            type: string
            nullable: true
            description: Industry sector
            example: Consumer Discretionary
          website:
            type: string
            nullable: true
            description: Company website
            example: https://www.volvocars.com/se
          article_count:
            type: integer
            description: Number of articles mentioning this company
            example: 176
          story_count:
            type: integer
            description: Number of stories mentioning this company
            example: 11
          parent_company:
            type: object
            nullable: true
            description: Parent company, if this is a subsidiary
            properties:
              id:
                type: string
              name:
                type: string
              ticker:
                type: string
                nullable: true
          subsidiaries:
            type: array
            description: Subsidiary companies
            items:
              type: object
              properties:
                id:
                  type: string
                name:
                  type: string
                ticker:
                  type: string
                  nullable: true
          indices:
            type: array
            description: Stock index memberships (e.g. OMXS30)
            items:
              type: object
              properties:
                id:
                  type: string
                name:
                  type: string
                symbol:
                  type: string
          stock_listings:
            type: array
            description: All stock listings across exchanges
            items:
              type: object
              properties:
                ticker:
                  type: string
                  description: Full ticker including exchange suffix
                primary:
                  type: boolean
                  description: Whether this is the primary listing
                exchange:
                  type: object
                  nullable: true
                  properties:
                    id:
                      type: string
                    name:
                      type: string
                    mic_code:
                      type: string
          country:
            type: object
            nullable: true
            description: Country where the company is domiciled
            properties:
              id:
                type: string
                description: Country identifier
              name:
                type: string
                description: Country name
                example: Sweden
              iso2_code:
                type: string
                description: ISO 3166-1 alpha-2 code
                example: SE
          registry:
            type: object
            nullable: true
            description: Official company registry data from Nordic registries (e.g.
              Bolagsverket, PRH). Null if no registry data exists.
            properties:
              source:
                type: string
                nullable: true
                description: Name of the official registry
                example: Bolagsverket
              registered_name:
                type: string
                nullable: true
                description: Official registered company name
                example: Volvo Personvagnar Aktiebolag
              company_form:
                type: string
                nullable: true
                description: Legal form of the company
                example: Aktiebolag
              city:
                type: string
                nullable: true
                description: Registered city
                example: GÖTEBORG
              founded:
                type: string
                nullable: true
                description: Founding or registration date
                example: '1960-10-28'
              employees:
                type: integer
                nullable: true
                description: Number of employees
                example: 43000
              industry:
                type: string
                nullable: true
                description: Industry classification from registry
                example: Tillverkning av personbilar och andra lätta motorfordon
              bankruptcy:
                type: boolean
                nullable: true
                description: Whether the company is in bankruptcy proceedings
              under_liquidation:
                type: boolean
                nullable: true
                description: Whether the company is under liquidation
              share_capital:
                type: object
                nullable: true
                description: Registered share capital
                properties:
                  amount:
                    type: number
                    nullable: true
                    description: Share capital amount
                    example: 50000000
                  currency:
                    type: string
                    nullable: true
                    description: Currency code (ISO 4217)
                    example: SEK
              institutional_sector:
                type: string
                nullable: true
                description: Institutional sector classification
                example: Non-financial corporations
              deregistered_at:
                type: string
                nullable: true
                description: Date the company was deregistered, if applicable
                example: '2023-01-15'
              deregistration_reason:
                type: string
                nullable: true
                description: Reason for deregistration
                example: Merger
          updated_at:
            type: string
            format: date-time
            description: When the company was last updated (ISO 8601)
    CountrySummary:
      type: object
      required:
      - id
      - name
      - iso2_code
      properties:
        id:
          type: string
          description: Unique country identifier
          example: ctry_sweden12
        name:
          type: string
          description: Country display name
          example: Sweden
        iso2_code:
          type: string
          description: ISO 3166-1 alpha-2 country code
          example: SE
    ExchangeSummary:
      type: object
      required:
      - id
      - name
      - mic_code
      properties:
        id:
          type: string
          description: Unique exchange identifier
          example: exch_nasdaq1
        name:
          type: string
          description: Exchange display name
          example: Nasdaq Stockholm
        mic_code:
          type: string
          description: ISO 10383 Market Identifier Code
          example: XSTO
        acronym:
          type: string
          nullable: true
          description: Exchange acronym
          example: NASDAQ
        country:
          type: object
          description: Country where the exchange is located
          properties:
            id:
              type: string
            name:
              type: string
            iso2_code:
              type: string
    ExchangeDetail:
      allOf:
      - "$ref": "#/components/schemas/ExchangeSummary"
      - type: object
        properties:
          operating_mic_code:
            type: string
            nullable: true
            description: Parent operating MIC code (for segment exchanges)
            example: XSTO
          currency:
            type: string
            nullable: true
            description: Primary trading currency (ISO 4217)
            example: SEK
          ticker_suffix:
            type: string
            nullable: true
            description: Ticker suffix used for this exchange
            example: ST
          timezone:
            type: string
            nullable: true
            description: Exchange timezone
            example: Europe/Stockholm
          city:
            type: string
            nullable: true
            description: City where the exchange is located
          indices:
            type: array
            description: Stock indices on this exchange
            items:
              type: object
              properties:
                id:
                  type: string
                name:
                  type: string
                symbol:
                  type: string
    IndexSummary:
      type: object
      required:
      - id
      - name
      - symbol
      properties:
        id:
          type: string
          description: Unique stock index identifier
          example: idx_omxs30abc
        name:
          type: string
          description: Index display name
          example: OMX Stockholm 30
        symbol:
          type: string
          description: Index symbol
          example: OMXS30
        pan_nordic:
          type: boolean
          description: True if the index spans multiple Nordic exchanges (e.g. OMXN40),
            false if tied to a single exchange (e.g. OMXS30)
          example: false
        exchange:
          type: object
          nullable: true
          description: Exchange associated with this index
          properties:
            id:
              type: string
            name:
              type: string
            mic_code:
              type: string
    IndexDetail:
      allOf:
      - "$ref": "#/components/schemas/IndexSummary"
      - type: object
        properties:
          description:
            type: string
            nullable: true
            description: Index description
            example: The 30 most-traded stocks on Nasdaq Stockholm
          companies_count:
            type: integer
            description: Number of distinct companies in this index (may be less than
              constituents when a company has multiple share classes)
            example: 30
    CalendarEventSummary:
      type: object
      required:
      - id
      - title
      - event_type
      - status
      - scheduled_at
      - company_id
      properties:
        id:
          type: string
          description: Unique calendar event identifier
          example: cale_q3volvo1
        title:
          type: string
          description: Event title as reported by the issuer or exchange
          example: Q3 2026 Interim Report
        description:
          type: string
          nullable: true
          description: Optional long-form description
          example: Publication of the interim report for Q3 2026 and earnings call
            at 10:00 CET.
        event_type:
          type: string
          description: The kind of calendar event
          enum:
          - earnings_report
          - earnings_call
          - agm
          - egm
          - dividend
          - capital_markets_day
          - trading_update
          - conference_presentation
          - ma_announcement
          - ma_offer_deadline
          - ma_completion
          - listing
          - delisting
          example: earnings_report
        status:
          type: string
          description: Event lifecycle status. `scheduled` is upcoming; `published`
            means the event has occurred (events flip ~2 days after their date); `cancelled`
            means the issuer withdrew it. List endpoints return `scheduled` + `published`
            and exclude `cancelled` by default — pass the `status` query parameter
            to filter (e.g. `status=cancelled`).
          enum:
          - scheduled
          - published
          - cancelled
          example: scheduled
        date_precision:
          type: string
          description: Granularity of `scheduled_at`. `exact` = timestamp known; `day`
            = date only; `month` = only the month is known. Consumers that need a
            date should still use `scheduled_at` (set to a representative point inside
            the window).
          enum:
          - exact
          - day
          - month
          example: day
        fiscal_period:
          type: string
          nullable: true
          description: Fiscal period slug (required for report-type events, e.g. `q3_2026`,
            `fy_2025`)
          example: q3_2026
        scheduled_at:
          type: string
          format: date-time
          description: Scheduled date/time in UTC (ISO 8601). For the local calendar
            date use `local_time` instead — a day-precision event is stored at local
            midnight, so its UTC instant falls on the previous day for ahead-of-UTC
            (Nordic) zones.
          example: '2026-10-22T05:00:00.000Z'
        timezone:
          type: string
          description: The issuer's IANA timezone, used to interpret `scheduled_at`.
            Resolved from the source article, falling back to the company's country
            zone.
          example: Europe/Stockholm
        local_time:
          type: string
          format: date-time
          description: "`scheduled_at` rendered in the issuer's `timezone` (ISO 8601
            with offset). Use this for the correct local date/time — its date is right
            even for day-precision events."
          example: '2026-10-22T07:00:00.000+02:00'
        scheduled_at_changed_at:
          type: string
          format: date-time
          nullable: true
          description: When `scheduled_at` last changed. Useful for change-feed consumers
            tracking date moves.
          example: '2026-03-15T10:00:00.000Z'
        source_article_id:
          type: string
          nullable: true
          description: 'ID of the article the event was extracted from. `null` when
            the source article is not retrievable (not live, or removed). Best-effort:
            on the free plan it may reference an article your plan can''t fetch (premium
            source or outside the age window).'
          example: art_xyz45678
        company_id:
          type: string
          description: ID of the company this event relates to
          example: w55fcw3pbg3p
        country:
          type: string
          nullable: true
          description: Country ISO 3166-1 alpha-2 code
          example: SE
        dividend:
          type: object
          nullable: true
          description: Dividend lifecycle dates. `null` for non-dividend events. `scheduled_at`
            follows the priority ex-date > record-date > payment-date — whichever
            is explicitly stated.
          properties:
            ex_date:
              type: string
              nullable: true
              description: Ex-dividend date (YYYY-MM-DD). The market-event anchor
                — share price drops on this day.
              example: '2026-04-25'
            record_date:
              type: string
              nullable: true
              description: Record date (YYYY-MM-DD). Holders of record on this date
                receive the dividend.
              example: '2026-04-28'
            payment_date:
              type: string
              nullable: true
              description: Payment date (YYYY-MM-DD). When the cash is distributed
                to shareholders.
              example: '2026-05-06'
            declaration_date:
              type: string
              nullable: true
              description: Declaration / announcement date (YYYY-MM-DD). Metadata
                only — never used as the calendar anchor.
              example: '2026-03-20'
            kind:
              type: string
              enum:
              - ordinary
              - special
              description: Tranche type. `ordinary` for the recurring/regular dividend;
                `special` for an extra/bonus/one-off dividend. When an issuer proposes
                both for the same date, they are returned as two separate events.
              example: ordinary
        updated_at:
          type: string
          format: date-time
          description: When the event was last updated (ISO 8601) — use with `updated_after`
            for polling
          example: '2026-03-01T09:00:00.000Z'
    CalendarEventDetail:
      allOf:
      - "$ref": "#/components/schemas/CalendarEventSummary"
    WatchlistSummary:
      type: object
      required:
      - id
      - name
      - position
      - company_count
      properties:
        id:
          type: string
          description: Unique watchlist identifier
          example: a1b2c3d4e5f6
        name:
          type: string
          description: User-defined watchlist name
          example: Tech Stocks
        position:
          type: integer
          description: Display order (lower values appear first)
          example: 0
        company_count:
          type: integer
          description: Number of companies in the watchlist
          example: 12
        created_at:
          type: string
          format: date-time
          description: When the watchlist was created
          example: '2025-01-15T10:30:00Z'
        updated_at:
          type: string
          format: date-time
          description: When the watchlist was last modified
          example: '2025-03-01T14:00:00Z'
    SearchResult:
      type: object
      required:
      - results
      - pagination
      properties:
        results:
          type: object
          properties:
            articles:
              type: array
              items:
                "$ref": "#/components/schemas/ArticleSummary"
            stories:
              type: array
              items:
                "$ref": "#/components/schemas/StorySummary"
            companies:
              type: array
              items:
                "$ref": "#/components/schemas/CompanySummary"
            countries:
              type: array
              items:
                "$ref": "#/components/schemas/CountrySummary"
            exchanges:
              type: array
              items:
                "$ref": "#/components/schemas/ExchangeSummary"
            indices:
              type: array
              items:
                "$ref": "#/components/schemas/IndexSummary"
        pagination:
          type: object
          properties:
            articles:
              type: object
              properties:
                count:
                  type: integer
                limit:
                  type: integer
            stories:
              type: object
              properties:
                count:
                  type: integer
                limit:
                  type: integer
            companies:
              type: object
              properties:
                count:
                  type: integer
                limit:
                  type: integer
            countries:
              type: object
              properties:
                count:
                  type: integer
                limit:
                  type: integer
            exchanges:
              type: object
              properties:
                count:
                  type: integer
                limit:
                  type: integer
            indices:
              type: object
              properties:
                count:
                  type: integer
                limit:
                  type: integer
