← back to /for-funds

Schema documentation

Two tables make up the data product: insidereu.filings (MAR Article 19 PDMR) and insidereu.substantial_holdings (Transparency Directive / DTR-5 / national major-holding notifications).

Schema stable since 2025-Q4; ingested_at added in 2026-Q1. No field has been renamed or dropped since. Other tables in the insidereu schema are application-internal and not part of the data product.

Table: insidereu.filings

PDMR (Person Discharging Managerial Responsibilities) and closely-related-person insider transactions filed under Article 19 of EU MAR (Regulation 596/2014) and UK/EEA equivalents.

ColumnTypeNullMeaning
idtext PKnoStable cross-scrape dedup key, format {country}-{source-system-ref}. Re-ingesting the same record is idempotent.
countrytextnoISO 3166-1 alpha-2 of the regulator's jurisdiction. Not the issuer's domicile.
insider_idtextnoStable insider key within a country. Cross-country join not guaranteed.
insider_nametextnoAs reported by regulator. Nordic and other transliteration preserved.
insider_roletextyesE.g. Chief Executive Officer, Member of the Board, Closely associated person, or the literal PDMR when no sub-role is provided.
company_nametextnoIssuer name.
isintextyesCoverage by country varies — see the coverage matrix. When null, join on (country, company_name).
tickertextyesYahoo Finance ticker. Mainly populated for Tier 1-3 countries.
exchangetextyesReporting venue.
transaction_datedatenoDate the trade was executed.
filing_datedatenoRegulator publication date. Currently unreliable for several countries (audit § 4d) where it's copied from transaction_date.
transaction_typetextnoCHECK IN ('buy','sell','unknown'). 'unknown' = source feed had no parseable direction. Filter transaction_type IN ('buy','sell') for direction-confirmed rows only.
sharesnumericyesVolume of the trade.
pricenumericyesPrice per share in the original currency.
value_eurnumericyesTrade size in EUR. Derived: shares * price * fx_rate_on_transaction_date using ECB reference rates.
currencytextyesISO 4217. EUR (default), CHF, SEK, GBP, NOK, DKK.
source_urltextyesDeep link to the regulator's original filing. Always populated for live-scraped rows.
raw_datajsonbyesSource-system payload preserved verbatim. Used to re-derive normalized fields when needed.
scraped_attimestamptzyesWhen the scraper first hit the source page.
current_price_eurnumericyesMost recent close, refreshed nightly for active tickers.
price_refreshed_attimestamptzyesWhen current_price_eur was last refreshed.
ingested_attimestamptznoWhen the row landed in our DB. Use this for ingestion-velocity / latency analysis.

Dedup recommendation

A server-side enrichment-aware view is shipped: read from insidereu.filings_dedup rather than the base table. It collapses two enriched rows from different sources with matching (country, issuer-key, txn_date, insider, direction, shares, price) and signal-only rows whose enriched sibling exists in another source. It does not collapse two signal-only rows with no enriched sibling. Same schema, no extra columns. The public REST API and sample CSV exports already read from the view.

Non-cash events

Option grants, RSU vests, and gifts are normalized: a grant or vest is recorded as buy with price = 0 and value_eur = 0; a gift-out is sell at price = 0. To exclude non-cash events, filter price > 0 AND value_eur > 0.

NULL ISIN handling

22 of 28 countries have <50% ISIN coverage. When isin IS NULL, join on (country, company_name) against a reference universe. Sample CSVs include an enriched_isin column derived by fuzzy-matching company name against the Yahoo Finance ticker universe at export time.

Sample row

{
  "id": "SE-FI-2026-05-19-A4B92",
  "country": "SE",
  "insider_id": "SE-FI-INSIDER-7741",
  "insider_name": "Erik Hägglund",
  "insider_role": "Chief Executive Officer",
  "company_name": "Saab AB",
  "isin": "SE0000112385",
  "ticker": "SAAB-B.ST",
  "exchange": "Nasdaq Stockholm",
  "transaction_date": "2026-05-16",
  "filing_date": "2026-05-19",
  "transaction_type": "buy",
  "shares": 2000,
  "price": 524.40,
  "value_eur": 90876.12,
  "currency": "SEK",
  "source_url": "https://marknadssok.fi.se/.../A4B92.html",
  "scraped_at": "2026-05-20T02:30:35.068+00:00",
  "ingested_at": "2026-05-20T02:30:35.068+00:00"
}

Table: insidereu.substantial_holdings

Transparency Directive (Directive 2004/109/EC) major-holding notifications and national equivalents — DTR 5 in the UK, BaFin §33-§38a WpHG in Germany, AFM in NL, CNMV in ES. Triggered when any holder crosses 3% / 5% / 10% / 15% / 20% / 25% / 30% / 50% / 75% of voting rights in a listed issuer.

ColumnTypeNullMeaning
idtext PKnoStable per-source key.
countrytextnoISO 3166-1 alpha-2 of the regulator that received the notification.
filer_nametextnoNotifying entity (institutional holder, individual, syndicate).
filer_idtextnoStable filer key within a country.
issuer_nametextnoThe company whose voting rights crossed a threshold.
issuer_isintextyesISIN. Coverage by country varies — see audit.
issuer_tickertextyesYahoo Finance ticker (derived).
filing_datedatenoDate the regulator published the notification.
event_datedateyesDate the threshold was crossed. Often 1-3 business days before filing_date. ES currently inverted (audit § 4a).
threshold_pctnumericyesThreshold crossed, in percent. Currently 0% populated except NO (17%). Full population on the roadmap.
directiontextnoCHECK IN ('crossed_above','crossed_below','disclosed'). ~99% of rows are disclosed today. Reliable directional values only for CH (98%) and NO (1.9%).
new_pctnumericyesFiler's voting rights percentage after the event.
previous_pctnumericyesFiler's voting rights percentage before the event.
voting_rights_countbigintyesAbsolute count of voting rights held after the event.
regulationtextyesThe exact regulatory citation — e.g. Section 33 WpHG, DTR 5.1.2 R, AFM 5:38 Wft.
source_urltextyesDeep link to the original notification.
raw_datajsonbyesVerbatim source payload.
created_attimestamptznoEquivalent to filings.scraped_at.
ingested_attimestamptznoWhen the row landed in our DB. Use for ingestion-velocity analysis.

Dedup

Natural key: (country, filer_id, issuer_isin OR issuer_name, event_date, threshold_pct, direction). The id PK dedupes within a single source. No multi-source overlap observed today (each regulator publishes exactly once).

direction = 'disclosed'

Three regulators (FSMA-BE, AMF-FR, AFM-NL) publish “current holdings disclosure” notices that don't state whether the filer crossed up or down — they only restate the new position. Those rows carry direction = 'disclosed'. For strict directional whale events, filter direction <> 'disclosed'.

Sample row

{
  "id": "DE-BAFIN-2026-05-19-77881",
  "country": "DE",
  "filer_name": "BlackRock, Inc.",
  "filer_id": "DE-BAFIN-FILER-BLACKROCK",
  "issuer_name": "Volkswagen AG",
  "issuer_isin": "DE0007664039",
  "issuer_ticker": "VOW3.DE",
  "filing_date": "2026-05-19",
  "event_date": "2026-05-15",
  "threshold_pct": null,
  "direction": "disclosed",
  "new_pct": 5.12,
  "previous_pct": 4.98,
  "voting_rights_count": 15125400,
  "regulation": "Section 33 WpHG",
  "source_url": "https://www.bafin.de/.../77881.pdf",
  "ingested_at": "2026-05-20T02:31:13.314+00:00"
}

Known caveats — read before integrating

  1. transaction_type is ternary. 'unknown' flags rows where the source did not carry parseable direction (signal-only). Filter transaction_type IN ('buy','sell') for direction-confirmed rows only. Fully reliable buy/sell mix today: CH, DE, SE; smaller reliable tails: DK, FI, GB, IT, NO, ES.
  2. filing_date semantics vary. For 18 countries it equals transaction_date. For GB it equals ingested_at::date. Use ingested_at for “when we knew about it” and transaction_datefor “when it happened.” Both are reliable.
  3. threshold_pct is sparsely populated in holdings. Until full backfill, the most reliable holdings signal is the change new_pct - previous_pct.
  4. ISIN coverage is non-uniform. Tier 1-2 countries are >99%; Tier 4 countries can be 0%. See per-country detail in the coverage matrix →
  5. Dedup is applied server-side via insidereu.filings_dedup. Read from the view, not the base table.
  6. current_price_eur is refreshed nightly for tickers with confirmed Yahoo Finance mapping. Coverage tracks ticker coverage.
  7. Non-cash events (grants, vests, gifts) are mapped to buy/sell with price = 0. Filter price > 0 AND value_eur > 0 to exclude.

API access

Filings and holdings are available via three delivery surfaces:

  • REST: https://insidereu.com/api/v1/filings?country=DE&since=2026-05-01
  • CSV / Parquet drop: nightly to S3-compatible bucket (pilot tier and above).
  • Postgres read replica: enterprise tier.

API keys are provisioned per-seat via insidereu.api_keys with hashed key storage.