openapi: 3.2.0 info: title: Nordic Financial News Articles API version: v1.0 contact: name: API Support email: hello@nordicfinancialnews.com description: 'Operations tagged Articles across 2 of this provider''s published API definitions: nordic-financial-news-openapi-original.yml, nordic-financial-news-openapi.yml. Each path carries the servers of the definition it was published in.' servers: - url: https://nordicfinancialnews.com description: Production server tags: - name: Articles 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: include_peripheral in: query required: false description: When `true`, widen `companies` and `company_ids` back to peripheral associations (a company named only as the article's source, an advisor, or a passing mention), which are otherwise excluded. Default `false` (principal-only). Each `companies[]` entry always carries `association_tier` regardless of this parameter. example: 'true' schema: type: boolean - 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 - name: If-Modified-Since in: header required: false description: HTTP date from a previous `Last-Modified` header. Returns `304 Not Modified` if nothing has changed since. schema: type: string responses: '200': description: Article found content: application/json: schema: type: object properties: article: $ref: '#/components/schemas/ArticleDetail' '304': description: Not Modified '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' operationId: getApiV1ArticlesById x-operation-id-source: derived servers: - url: https://nordicfinancialnews.com description: Production server /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. ## Filtering 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 `ids` for batch lookup of specific articles. ## Search Use `q` for full-text search: relevance-ranked by default, newest-first with `sort=latest`, or matched by meaning with `mode=semantic`. ## Polling for updates Use `updated_after` for incremental syncing. ## Pagination Responses include cursor-based pagination and support field projection to minimize payload size. ## Caching Requests with `updated_after` are not cached: they return `Cache-Control: no-cache, no-store` and no `ETag`. The `ETag` reflects the response as served, not only record timestamps: it changes when plan limits alter the visible set, so a changed `ETag` does not necessarily mean a record was updated.' 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` unless `sort=latest` is also given. 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`, `updated_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 incremental sync: store the timestamp of your last completed sync, then request only newer results on each poll. Results are ordered by `updated_at` ascending and are cursor-paginated. Follow `pagination.next_cursor`, resending the same `updated_after` value, until it is null. Only advance your stored timestamp once you have drained every page (see the Real-Time Updates guide at https://docs.nordicfinancialnews.com). Mutually exclusive with `sort=latest` (with or without `q`). An `updated_after` walk is always ordered by `updated_at`.' example: '2024-01-01T00:00:00Z' schema: type: string - name: q in: query required: false description: Full-text search query (2-200 characters). By default, results are ranked by relevance instead of publication date, as a single page with no cursor. Mutually exclusive with `cursor` unless `sort=latest` is also given. Disables caching. example: Volvo earnings schema: type: string - name: sort in: query required: false description: Result order for `q`. `relevance` (default) is a single relevance-ranked page with no cursor; it requires `q`, and returns 400 without one. `latest` turns `q` into a match filter under the endpoint's normal newest-first order (`published_at` descending) and the response is cursor-paginated like any other request; without `q` it is an accepted no-op (that's already the default order). Mutually exclusive with `updated_after`. example: latest schema: type: string enum: - relevance - latest - name: mode in: query required: false description: 'Set to `semantic` to match `q` by meaning rather than by keyword. The match runs over each article''s translated title and summary, so it answers thematic questions a keyword match cannot ("defense contract wins", "battery cell capacity expansion"). Requires `q`, is mutually exclusive with `sort`, `cursor`, `updated_after` and `ids`, and returns a single ranked page capped by `limit`; every other filter (`country`, `category`, `sources`, `source_type`, `ticker`, `company`, `market`, `domicile`, `exchange`, `index`, `sector`, `watchlist`, `content_type`, `published_after`/`published_before`) remains a hard filter applied before the match, not after. Each article gains a `distance` (raw cosine, lower is closer, rounded to 4dp). Compare it only within one response, never across responses: the scale is model-dependent. Ranking also blends in a small recency bonus that `distance` does not reflect. A server-side relevance floor drops weak matches, so a page is frequently shorter than `limit` and can be empty. Raising `limit` does not recover floored rows. On plans with restricted article access the response carries `plan_limited: true`; the plan article count cap does not apply in this mode, only the source and age restrictions. The response `pagination` object carries `count` only. There is no `next_cursor`, since a distance ranking cannot be cursor-paginated, and the response is uncacheable (`Cache-Control: no-cache, no-store`, no `ETag`). If the query cannot be embedded the request returns `503` rather than silently falling back to keyword matching.' example: semantic schema: type: string enum: - semantic - 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` (from `GET /api/v1/categories`). Comma-separated for multiple (e.g. `cat_earnings1,cat_mna000001`); unknown ids are ignored. 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`). Disabled sources resolve to empty results. example: abc123def456,ghi789jkl012 schema: type: string - name: source_type in: query required: false description: 'Filter by source type, comma-separated. Valid values: `news_publication`, `wire_service`, `press_release`, `government`, `trade_publication`, `blog`, `stock_exchange`, `research`. Articles from disabled sources are excluded. An unrecognized value returns 400 naming the bad value(s).' example: stock_exchange schema: type: string - name: ticker in: query required: false description: 'Filter by company stock ticker symbol, comma-separated for several (e.g. `VOLV-B` or `VOLV-B,ERIC-B`; matches any of them). Accepts full tickers with exchange suffix (e.g. `VOLV-B.ST`). Returns articles where the company is a principal association (subject, partner, investor, or competitor) — a company named only as the article''s source, an advisor, or a passing mention does not match. Former tickers resolve too: a company that renamed or moved venues is still reachable by an old ticker; the returned company `former_tickers` lists any such historical tickers. Matching is case-insensitive. Every value must resolve to a company, or the request is rejected with `400` naming the unknown values. Combined with `company`, the two filters intersect.' example: VOLV-B schema: type: string - name: company in: query required: false description: Filter by company `id` (as returned by the companies endpoints), comma-separated for several (matches any of them). Complements `ticker`; use this to reach companies without a stock listing. Matches only the company's principal associations (subject, partner, investor, or competitor); a passing mention does not match. Every value must resolve, or the request is rejected with `400` naming the unknown ids. Combined with `ticker`, the two filters intersect. example: cmpny1234567 schema: type: string - name: exchange in: query required: false description: Filter by exchange using ISO 10383 Market Identifier Code, comma-separated for multiple (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. A company counts only through a principal association (subject, partner, investor, or competitor); a passing mention does not qualify. Case-insensitive. example: XSTO,XCSE schema: type: string - name: market in: query required: false description: Filter to articles about companies with an active equity listing in these countries' exchanges (ISO 3166-1 alpha-2, comma-separated, e.g. `SE,NO`). `SE` covers Nasdaq Stockholm, First North, Spotlight and NGM. Distinct from `country` (the source that reported it) and `domicile` (legal domicile). Includes foreign-domiciled but locally-listed issuers. A company counts only through a principal association (subject, partner, investor, or competitor); a passing mention does not qualify. example: SE schema: type: string - name: domicile in: query required: false description: Filter to articles about companies legally domiciled in these countries (ISO 3166-1 alpha-2, comma-separated). Reaches unlisted companies; excludes foreign-domiciled but locally-listed issuers. Use `market` for market-coverage questions. A company counts only through a principal association (subject, partner, investor, or competitor); a passing mention does not qualify. example: SE 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. A company counts only through a principal association (subject, partner, investor, or competitor); a passing mention does not qualify. Case-insensitive for symbols. example: OMXS30 schema: type: string - name: sector in: query required: false description: 'Filter by company sector. A company counts only through a principal association (subject, partner, investor, or competitor); a passing mention does not qualify. 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, comma-separated. Valid values: `news`, `analysis`, `press_release`, `market_commentary`, `market_news`, `trading_halt`, `trading_event`, `other`. `GET /api/v1/meta` lists them as `content_types`. An unrecognized value returns 400 naming the bad value(s).' example: news,analysis schema: type: string - 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 about at least one company with an active listing on any exchange, through a principal association (subject, partner, investor, or competitor). A passing mention does not qualify, and `include_peripheral` does not change this. A company whose listings are all delisted does not count. Hidden and unverified companies do not count. example: 'true' schema: type: string - name: watchlist in: query required: false description: Restricts to articles about companies in a single watchlist, identified by its `id` (from the List Watchlists endpoint), through a principal association (subject, partner, investor, or competitor) — a passing mention does not qualify. An unknown id returns no articles. Requires `read:watchlist` scope. example: wl_8fk2a1b3c4d5 schema: type: string - name: primary_only in: query required: false description: When `true`, narrows further within the already-principal match, to only articles where the matched company (`ticker`, `company`, or `watchlist`) is the primary subject. Requires `ticker`, `company`, or `watchlist`, and returns 400 without one. example: 'true' schema: type: boolean - name: include_peripheral in: query required: false description: 'When `true`, widen the `ticker`, `company` and `watchlist` filters — and each article''s `company_ids` — back to peripheral associations (a company named only as the article''s source, an advisor, or a passing mention), which are otherwise excluded. Default `false` (principal-only: subject, partner, investor, competitor). `primary_only` still wins where it applies: the primary-subject restriction is kept even when peripheral is requested. Not honored on `GET /api/v1/search`.' example: 'true' schema: type: boolean - name: ids in: query required: false description: 'Comma-separated list of article IDs for batch lookup (max 100). Cannot be combined with `cursor`, `updated_after`, or `sort`. Returns a single page: `pagination` carries `count` only, with no `next_cursor`. IDs that don''t resolve, or that your plan can''t access, are omitted rather than rejected, so `count` can be less than the number of IDs sent. Key off each row''s `id`.' example: k3n9x2m7p4q1,b8v5c2z6w1r3 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 - name: If-Modified-Since in: header required: false description: HTTP date from a previous `Last-Modified` header. Returns `304 Not Modified` if nothing has changed since. 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 - 'null' required: - articles '304': description: Not Modified '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' operationId: getApiV1Articles x-operation-id-source: derived servers: - url: https://nordicfinancialnews.com description: Production server components: schemas: ArticleSummary: type: object required: - id - title - published_at - updated_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. Machine-translated when the source article is not 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 by its source (ISO 8601). This is editorial time, not the time the article reached this API. Use it to filter by when news happened, never to track what is new to you. example: '2026-03-15T09:30:00.000Z' updated_at: type: string format: date-time description: 'When this record last changed (ISO 8601). This is the clock incremental sync runs on. It only ever moves forward, unlike published_at. To sync: pass your last completed sync time as `updated_after`, drain every page via `pagination.next_cursor`, and only then advance your stored timestamp.' example: '2026-03-15T09:35: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 source_type: type: string example: news_publication enum: - news_publication - wire_service - press_release - government - trade_publication - blog - stock_exchange - research company_ids: type: array items: type: string description: IDs of companies mentioned in the article example: - uejazctgchj4 country: type: - string - 'null' description: Country code (ISO 3166-1 alpha-2) example: SE event_id: type: - string - 'null' description: ID of the event this article belongs to. Null when the article has not been assigned to an event yet. An event that has since been merged or removed still answers `GET /events/{id}` with a tombstone rather than a 404. example: 8fzq2rmtnk5c 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 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: 'AI-generated English summary of the article: a machine translation and paraphrase of the source text, not verbatim.' 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 published stories covering this article, if any example: - story_volvo_q3 companies: type: array description: Companies mentioned in this article, primary company first items: type: object required: - id - name - is_primary - association_tier properties: id: type: string description: Unique company identifier example: uejazctgchj4 name: type: string description: Company name example: AB Volvo (Volvo Group) ticker: type: - string - 'null' description: Primary stock ticker example: VOLV-B is_primary: type: boolean description: Whether this company is the primary subject of the article example: true association_tier: type: string enum: - principal - peripheral description: How the company relates to the article. `principal` (subject, partner, investor, competitor) is the default set; `peripheral` (source, advisor, passing mention) appears only when `include_peripheral=true`. Always present. example: principal example: - id: uejazctgchj4 name: AB Volvo (Volvo Group) ticker: VOLV-B is_primary: true association_tier: principal key_points: type: array items: type: string description: Key points from the article. Machine-generated English paraphrases of statements in the source article, not verbatim quotations. example: - Revenue up 15% year-over-year - Strong truck demand in Europe securitySchemes: bearer_auth: type: http scheme: bearer bearerFormat: API Key description: API Key authentication using Bearer token x-refined-from: - nordic-financial-news-openapi-original.yml - nordic-financial-news-openapi.yml