API schema reference
Field-by-field reference for the two datasets exposed via the API. See /developers for endpoints and authentication, and /methodology for per-country coverage and known caveats.
filings — managers' transactions (PDMR / MAR Article 19)
One row per regulator-published transaction. The deterministic id means re-scraping is idempotent: the same source filing always lands on the same row.
| Field | Type | Null? | Description |
|---|---|---|---|
| id | text (PK) | no | Deterministic id: scraper-prefix + regulator-reference + insider id + transaction date + price + value. Stable across re-scrapes. |
| country | text (ISO 3166-1 alpha-2) | no | Regulator's jurisdiction. Note Norwegian filings reach us via Euronext, so country='NO' but listing exchange may differ. |
| insider_id | text | no | Stable id of the manager: normalize(name) + '__' + normalize(company). See lib/types.ts:makeInsiderId. |
| insider_name | text | no | As published by the regulator. Diacritics preserved. |
| insider_role | text | yes | Position / status string from the regulator (e.g. 'Vorstand', 'CEO', 'Director'). Not normalized — language follows source. |
| company_name | text | no | Issuer name as published. Sometimes differs slightly from listing name (e.g. 'SAP SE' vs 'SAP'). |
| isin | text | yes | 12-char ISIN. Always present where the source provides it; missing on a few signal-only sources. |
| ticker | text | yes | Yahoo-Finance-compatible ticker (e.g. 'SAP.DE'). Populated asynchronously by ISIN→ticker enrichment, may be null for up to 24h after ingestion. |
| exchange | text | yes | Listing exchange string from the source (e.g. 'XETRA', 'SIX', 'Tradegate'). Free-form — no canonical taxonomy. |
| transaction_date | date | no | Date the trade was executed (regulator-reported). YYYY-MM-DD. |
| filing_date | date | no | Date the regulator received / published the filing. Always ≥ transaction_date. |
| transaction_type | enum: 'buy' | 'sell' | no | Normalized buy/sell. Regulator-specific labels (Kauf/Verkauf, achat/vente, acquisition/disposal) are mapped via per-source rules. Non-equity events (subscription, option exercise) are skipped at scrape time. |
| shares | bigint | yes | Number of shares. Where the regulator reports value+price but not shares, we derive as round(value/price). NULL on signal-only sources. |
| price | numeric | yes | Average price per share in the local trading currency. NULL on signal-only sources. |
| value_eur | numeric | yes | Aggregate transaction value in EUR. Native-currency values are converted using the ECB reference rate at transaction_date. NULL on signal-only sources. |
| currency | text (ISO 4217) | no | Original currency of the transaction. EUR for euro-area. Defaulted to 'EUR' when the source doesn't disambiguate. |
| source_url | text | yes | URL pointing back to the regulator filing or detail page. Used for provenance. |
| raw_data | jsonb | yes | Original regulator payload for the row, unmodified. |
substantial_holdings — Transparency Directive crossings
One row per regulator-published threshold crossing (5% / 10% / 25% / 50% / 75% of voting rights, per the EU Transparency Directive).
| Field | Type | Null? | Description |
|---|---|---|---|
| id | text (PK) | no | Deterministic id: scraper-prefix + regulator-reference + filer + issuer + filing date. |
| country | text (ISO 3166-1 alpha-2) | no | Regulator's jurisdiction. |
| filer_name | text | no | Entity that crossed the threshold (fund, person, holding company). As published. |
| filer_id | text | yes | Where regulator provides a stable id (LEI, BaFin id, etc.) — populated. Otherwise NULL. |
| issuer_name | text | no | Issuer whose shares the filer holds. |
| issuer_isin | text | yes | ISIN of the issuer's primary listing. Populated where the regulator provides it; many sources return NULL. |
| issuer_ticker | text | yes | Yahoo-Finance-compatible ticker. Populated asynchronously after ingestion. |
| filing_date | date | no | Date the regulator published the threshold-crossing filing. |
| event_date | date | yes | Date the threshold was actually crossed (always ≤ filing_date). May be missing in signal-only sources. |
| threshold_pct | numeric | yes | The threshold that was crossed (typically 5, 10, 15, 20, 25, 30, 50, 75). |
| direction | text | yes | 'up' (acquisition crossed up) or 'down' (disposal crossed down). |
| new_pct | numeric | yes | Post-trade ownership percentage. Currently sparse — most non-DE sources return NULL. |
| previous_pct | numeric | yes | Pre-trade ownership percentage. Sparse. |
| voting_rights_count | bigint | yes | Total voting rights held after the event. Sparse. |
| regulation | text | yes | Specific transparency regulation invoked (e.g. '§135 BörseG' for AT, 'WpHG §33' for DE, 'TR-FER 5.1' for GB). |
| source_url | text | yes | URL to the original regulator filing. |
| raw_data | jsonb | yes | Original regulator payload, unmodified. |
Notes on nullability
- For signal-only scrapers (most regulators outside DE / SE / CH — see /methodology),
shares,price, andvalue_eurareNULLon filings rows because the values live inside source PDFs we don't parse in v1. tickeris populated by an asynchronous ISIN-to-ticker enrichment job and may beNULLon freshly-ingested rows for up to 24 hours.raw_datacontains the full original regulator payload for that row, structured exactly as the source returned it. Useful for any field we don't surface in our typed schema.