> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nordicfinancialnews.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How to get Nordic earnings dates and dividend calendars

> Fetch scheduled Nordic earnings reports, dividends, AGMs, and M&A deadlines, then filter the calendar by company, index, and date window.

Nordic financial event data sits in five languages, on seven exchange calendars, and on every issuer's own investor relations page. This endpoint pulls all of it into one calendar: reporting dates, dividend timetables, shareholder meetings, and deal deadlines, each one extracted from a source article and tied to a single company.

## Before you start

Generate an API key in [API key settings](https://nordicfinancialnews.com/settings/api_keys) under **Settings > API Keys**, and keep it in an `API_KEY` environment variable. Every request below sends it as `Authorization: Bearer $API_KEY` against `https://nordicfinancialnews.com/api/v1`. See [Authentication](/guides/authentication) for scopes and rate limits.

## Step 1: Get the upcoming calendar

[`GET /calendar_events`](/api-reference/calendar-events/list-calendar-events) returns scheduled events soonest first, starting from today.

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -H "Authorization: Bearer $API_KEY" \
    "https://nordicfinancialnews.com/api/v1/calendar_events?limit=5"
  ```

  ```python Python theme={"dark"}
  import os
  import requests

  BASE = "https://nordicfinancialnews.com/api/v1"
  HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}"}

  resp = requests.get(
      f"{BASE}/calendar_events",
      headers=HEADERS,
      params={"limit": 5},
  )
  for event in resp.json()["calendar_events"]:
      print(event["local_time"][:10], event["event_type"], event["title"])
  ```

  ```javascript JavaScript theme={"dark"}
  const BASE = "https://nordicfinancialnews.com/api/v1";
  const HEADERS = { Authorization: `Bearer ${process.env.API_KEY}` };

  const resp = await fetch(
    `${BASE}/calendar_events?limit=5`,
    { headers: HEADERS }
  );
  const { calendar_events } = await resp.json();
  calendar_events.forEach((e) =>
    console.log(e.local_time.slice(0, 10), e.event_type, e.title)
  );
  ```
</CodeGroup>

```json Response theme={"dark"}
{
  "calendar_events": [
    {
      "id": "91gyim3bem8n",
      "title": "H&M Q3 2026 Interim Report",
      "description": "H&M is scheduled to publish its Q3 2026 results (third-quarter 2026 report).",
      "event_type": "earnings_report",
      "status": "scheduled",
      "date_precision": "day",
      "fiscal_period": "q3_2026",
      "scheduled_at": "2026-09-23T22:00:00.000Z",
      "timezone": "Europe/Stockholm",
      "local_time": "2026-09-24T00:00:00.000+02:00",
      "scheduled_at_changed_at": null,
      "source_article_id": "w3h40x09hkg8",
      "company_id": "8omipsw0q995",
      "company": {
        "id": "8omipsw0q995",
        "name": "H & M Hennes & Mauritz AB",
        "ticker": "HM-B"
      },
      "country": "SE",
      "dividend": null,
      "updated_at": "2026-08-18T05:45:42.420Z"
    }
  ],
  "pagination": { "count": 5, "next_cursor": "eyJ2ZXJzaW9uIjoi..." }
}
```

Each row carries its issuer inline as `company`, with the name and the primary `ticker`. Many titles carry no company name at all, so read the issuer from `company` rather than parsing `title`. `ticker` is `null` for an issuer with no active listing.

The feed floors at today by the issuer's local date, so an event stays listed for the whole of its own day. Results are cursor-paginated, described in [Pagination](/guides/pagination). There is no `q` parameter here.

Titles are templated, so searching them only rediscovers what the fields already hold: six issuers in one week share the exact title "Extraordinary General Meeting 2026". Filtering is the search, and [Step 2](#step-2-narrow-to-what-you-track) lists the filters.

To read the announcement an event came from, fetch `source_article_id` from the [articles endpoint](/api-examples/company-news). It is `null` when that article is no longer live.

List rows are complete records: [`GET /calendar_events/{id}`](/api-reference/calendar-events/get-calendar-event) returns the same fields for one event, so you never need a second call.

<Note>
  `plan_limited: true` means your plan restricted the calendar to a short upcoming window. Filters narrow within that window rather than searching the full calendar, so a company or index filter can come back empty even when the events exist. See the [pricing page](https://nordicfinancialnews.com/pricing) for current limits.
</Note>

## Which date to trust

Each event carries the same instant twice. `scheduled_at` is UTC, and `local_time` is that instant in the issuer's `timezone`. Read your calendar date off `local_time`.

<Warning>
  A `date_precision: day` event is stored at local midnight, so its UTC `scheduled_at` falls on the **previous** day wherever the issuer's zone is ahead of UTC. The response above shows it: local `2026-09-24` is UTC `2026-09-23T22:00:00Z`. Group by `scheduled_at` and a Swedish earnings calendar comes out a day early.
</Warning>

Slice the date off `local_time` and group on that. The first ten characters are already the issuer's local date, so no timezone conversion is involved:

```python Python theme={"dark"}
from collections import defaultdict

by_date = defaultdict(list)
for event in resp.json()["calendar_events"]:
    by_date[event["local_time"][:10]].append(event)

for day in sorted(by_date):
    print(day)
    for event in by_date[day]:
        print(" ", event["event_type"], event["title"])
```

Precision follows the event type. Dividends, listings, and delistings are almost always `day`; meetings, earnings calls, and investor events are usually `exact`; earnings reports go either way. Every event carries one or the other, so you never have to render a partial date. An event the issuer dated only to a month or a quarter is left out of the calendar rather than placed on a guessed day, which is one reason a report you expect to see may not be listed yet.

The offset comes from the event's own `timezone`, not from the market the issuer trades on. The calendar covers Nordic-listed issuers domiciled anywhere, so Icelandic and North American zones turn up too.

## Step 2: Narrow to what you track

`event_type` selects the kinds of event you care about and takes a comma-separated list.

| You want             | `event_type`                                            |
| -------------------- | ------------------------------------------------------- |
| Reporting dates      | `earnings_report`, `earnings_call`, `trading_update`    |
| Shareholder meetings | `agm`, `egm`                                            |
| Cash returns         | `dividend`                                              |
| Deal milestones      | `ma_announcement`, `ma_offer_deadline`, `ma_completion` |
| Listing changes      | `listing`, `delisting`                                  |
| Investor events      | `capital_markets_day`, `conference_presentation`        |

Pick companies with `ticker` or `company`, each taking up to 25 comma-separated values, or pick a group with `index`, `market`, `exchange`, `country`, `domicile`, `sector`, or `watchlist`. Send `ticker` and `company` together and they intersect, like any other pair of filters.

The two groups fail differently. Every value in `ticker`, `company`, and `sector` has to resolve, so one typo fails the request with a `400`. `index`, `exchange`, `market`, `country`, and `domicile` never error: an unrecognized code drops out or returns an empty list, so a mistyped index looks like a quiet week.

```bash cURL theme={"dark"}
curl -H "Authorization: Bearer $API_KEY" \
  "https://nordicfinancialnews.com/api/v1/calendar_events?index=OMXS30&event_type=earnings_report"
```

`scheduled_after` and `scheduled_before` bound the window, and either one lifts the upcoming-only floor so past events come back too. Use them for a fixed reporting season rather than a rolling feed.

Both compare against the UTC `scheduled_at`, which puts the day-precision trap back in play. A dividend dated 1 October in Stockholm is stored at `2026-09-30T22:00:00Z`, so a window opening at `2026-10-01T00:00:00Z` misses it. Pad each bound by a day and trim on `local_time`. `scheduled_after` is inclusive, `scheduled_before` exclusive.

```bash cURL theme={"dark"}
curl -H "Authorization: Bearer $API_KEY" \
  "https://nordicfinancialnews.com/api/v1/calendar_events?event_type=dividend&scheduled_after=2026-09-30T00:00:00Z&scheduled_before=2027-01-02T00:00:00Z"
```

## Step 3: Follow a single company

<Warning>
  Plans with a capped calendar cannot use this route. It returns `403` whatever you pass. The flat feed is the fallback, subject to the same cap: filters narrow within the capped window, so a company with no event inside it comes back empty.
</Warning>

[`GET /companies/{identifier}/calendar_events`](/api-reference/companies/list-company-calendar-events) fixes the company in the path and resolves identifiers the same way the company news routes do: a ticker, an exchange-suffixed ticker, a former ticker, or a company ID.

```bash cURL theme={"dark"}
curl -H "Authorization: Bearer $API_KEY" \
  "https://nordicfinancialnews.com/api/v1/companies/VOLV-B/calendar_events"
```

It accepts `event_type`, `status`, `scheduled_after`, `scheduled_before`, `updated_after`, `fields`, and the pagination parameters. The company and group filters are absent, since the path already fixes the company.

## Dividends

A `dividend` event carries a nested `dividend` object with `ex_date`, `record_date`, `payment_date`, `declaration_date`, and `kind`. Its `scheduled_at` is the first of those the issuer stated, in the order ex-date, then record-date, then payment-date. `dividend` is `null` on every other event type.

```json Response theme={"dark"}
{
  "id": "a3213j3oe5ik",
  "title": "Elekta AB Cash Dividend (SEK 1.20 per share)",
  "event_type": "dividend",
  "local_time": "2026-09-07T00:00:00.000+02:00",
  "dividend": {
    "ex_date": null,
    "record_date": "2026-09-07",
    "payment_date": "2026-09-10",
    "declaration_date": null,
    "kind": "ordinary"
  }
}
```

<Warning>
  Do not build an ex-dividend calendar by reading `ex_date`. It is `null` on most dividend events, including the one above. The event date is whichever lifecycle date the issuer published, and the `dividend` object tells you which one that was. Check the field before you label the date.
</Warning>

The dividend dates are strings copied from the issuer's announcement, not validated dates. Values such as `2026-09-31` do occur, so parse them defensively.

`kind` separates the recurring `ordinary` dividend from a one-off `special` one. An issuer proposing both on the same date produces two events sharing a date and a company, so key your dividend records on the event `id` rather than on the pair. There is no structured amount field. Many titles carry the amount as free text in whatever form the issuer wrote it, so treat it as a label rather than a value to parse.

## When a date moves or an event is cancelled

`scheduled_at_changed_at` records when an event was last rescheduled and is `null` for a date that has never moved. Compare it against your stored copy to catch reschedules.

`status` runs `scheduled`, then `published`, then `cancelled` if the issuer withdraws the event. `published` arrives on a daily sweep once `scheduled_at` is more than two days past, so an event flips two to three days after its date rather than the morning after.

List responses return `scheduled` and `published` and exclude `cancelled`, so a cancellation looks like an event that quietly disappeared. Ask for them directly, and pair `status` with a past window. Two things make that pairing necessary: `status` on its own does not lift the upcoming-only floor, and issuers usually withdraw an event at or after its own date, which puts most cancellations behind you.

```bash cURL theme={"dark"}
curl -H "Authorization: Bearer $API_KEY" \
  "https://nordicfinancialnews.com/api/v1/calendar_events?status=cancelled&scheduled_after=2026-01-01T00:00:00Z"
```

Keeping a mirror in sync needs a second pass for the same reason. [Real-time updates](/guides/real-time-updates) covers the `updated_after` walk and the separate cancellation poll it requires. On a capped plan `updated_after` returns `403` rather than a partial walk, since a window that moves with the clock cannot produce a coherent change feed.

## Next steps

<Columns cols={2}>
  <Card title="Company news" icon="newspaper" color="#01B2FF" href="/api-examples/company-news" horizontal>
    Pair a reporting date with the coverage that follows it.
  </Card>

  <Card title="Real-time updates" icon="rotate" color="#01B2FF" href="/guides/real-time-updates" horizontal>
    Walk `updated_after` to catch reschedules and cancellations.
  </Card>

  <Card title="CLI" icon="terminal" color="#01B2FF" href="/cli/commands" horizontal>
    Run `nfn calendar list --ticker VOLV-B` without writing code.
  </Card>

  <Card title="MCP server" icon="robot" color="#01B2FF" href="/model-context-protocol/tools" horizontal>
    Ask an AI assistant what reports are due this week.
  </Card>
</Columns>
