← Back to blog

Insynshandel: Sweden's Insider Register as an API

Filip Stanczyk · · 3 min read

Sweden is the busiest insider register in Europe. Swedish listed companies file more PDMR transactions than any other country we collect: over 150,000 transaction lines since MAR took effect in July 2016, more than the UK. In Swedish the activity is called insynshandel, the people are insynspersoner (in MAR terms, persons discharging managerial responsibilities), and the register is run by the financial supervisor, Finansinspektionen (FI).

The register

FI publishes every notification in its search service at marknadssok.fi.se, under "Insyn". The service is open: no login and no API key. Every notification since MAR began is in it, newest first.

You can search by issuer, person, transaction date and publication date, then export the result as CSV. That export is the most practical way to get the data out, and it has its share of quirks.

The CSV export, field by field

One row per transaction, already flattened, with columns such as:

Publication date; Issuer; LEI-code; Person discharging managerial responsibilities; Position; Closely associated; Amendment; Nature of transaction; Intrument type; Instrument name; ISIN; Transaction date; Volume; Unit; Price; Currency; Trading venue; Status

Things to know before you parse it:

  • Encoding is UTF-16LE, semicolon-separated. A default UTF-8 read mangles every å, ä and ö in names.
  • "Intrument type" is misspelled in the header. Match it as written.
  • There is no filing id. Two lines belong to the same notification when issuer, person and publication timestamp agree.
  • The export stops at 1,000 rows. The web interface disables export above that, so a long history has to be fetched in date windows small enough to stay under the cap. We split any window that comes back near the limit in half and fetch both halves.
  • Status matters. Each row is Current, Revised or Cancelled. When a filer corrects a notification, the original turns Revised and the correction is a new row. Both stay in the export. Count both and you double-count.
  • Price equal to volume is no price. For bonds and commercial paper, filers often enter the nominal amount in both fields: 700,000,000 "at" 700,000,000. Treat it as unpriced.
  • Some issuers aren't Swedish-listed. FI supervises a few issuers listed abroad, so you will see trading venues like the TSX Venture Exchange and prices in CAD.

The service's HTML search pages also drop connections intermittently. The export endpoint has been reliable for us, the search pages less so.

The same data from the API

Duceus polls FI's register throughout the day, drops revised and cancelled rows from every read, nulls the price where it equals the volume, and serves the rest in the same schema as 19 other European registers.

Latest Swedish insider purchases:

curl "https://api.duceusapi.com/transactions?country=SE&transaction_type=BUY&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"

FI's own wording stays next to the normalized fields: person_position keeps "Verkställande direktör" or "Styrelseledamot", transaction_type_raw keeps "Acquisition" or "Allotment", and instrument_raw keeps "Aktier" or the option series. The role bucket (CEO, BOARD_MEMBER, …) and the type (BUY, SHARE_AWARD, …) are derived from them.

In Python, the five companies with the most insider buying this month:

import os
from collections import Counter
from datetime import datetime, timedelta, timezone

import requests

since = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat()
response = requests.get(
    "https://api.duceusapi.com/transactions",
    headers={"Authorization": f"Bearer {os.environ['DUCEUS_API_KEY']}"},
    # One page of up to 500 rows; add "offset" to page further.
    params={"country": "SE", "transaction_type": "BUY", "published_after": since, "limit": 500},
    timeout=30,
)
response.raise_for_status()

buyers = Counter()
for deal in response.json()["items"]:
    if not deal["is_closely_associated"]:
        buyers[deal["issuer_name"]] += 1

for issuer, count in buyers.most_common(5):
    print(f"{count:3}  {issuer}")

Free to try

Sweden is included in the free plan, so the examples above work as soon as you have a key. Create an account, generate a key on the API key page, and start with country=SE. The free plan's history starts in 2024; the full archive goes back to July 2016.

Summary

  • Insynshandel is reported to Finansinspektionen and published in its open search service.
  • The CSV export is UTF-16, capped at 1,000 rows, and keeps revised rows next to their corrections.
  • Sweden is the largest register in our archive by number of filings.
  • The API serves it as JSON, corrections applied, and it is on the free plan.