Documentation
Duceus is a REST API over managers' transactions (PDMR dealings) and major shareholding notifications from European regulators and exchanges, normalised into one schema. All responses are JSON.
Getting started
The API is served from https://api.duceusapi.com. Create an account, then generate a key on the API key page. Every account starts on the free plan, so you can make your first call right away:
curl "https://api.duceusapi.com/transactions?country=NL&limit=5" \
-H "Authorization: Bearer YOUR_API_KEY"List endpoints return a page envelope (items, total, limit, offset); see pagination. Which countries and data types are available is listed on the coverage page.
Authentication
Every endpoint except /health requires your API key as a bearer token in the Authorization header:
Authorization: Bearer YOUR_API_KEYYou have one key per account; generating a new one revokes the previous one immediately. The key is shown once, at creation; store it in a secret manager, not in source control.
Rate limits
Requests are counted per key in fixed hourly windows that start on the hour (UTC). The limit depends on your plan. Every authenticated response carries the current state:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1758294000X-RateLimit-Reset is a Unix timestamp of the next window. Once the limit is exhausted, further requests return 429 with a Retry-After header (seconds). Rejected requests still count against the window, so back off rather than retrying in a tight loop.
Plan limits
Your plan sets how many requests you may make, and for each dataset which countries you may query and how far back the data goes.
| Plan | Requests / hour | Transactions | Shareholdings |
|---|---|---|---|
| Free | 10 | Sweden & Spain, from 2024-01-01 | Netherlands, full archive |
| Full | 100 | All countries, full archive | All countries, full archive |
How limits apply
- Asking for a country outside your plan returns
403, so you learn why rather than getting an empty list. - Omitting the country filter returns rows from all countries your plan covers.
- Rows dated before your plan's history start for that dataset are not returned.
/feedand/issuersspan both datasets: each row follows its own dataset's limits.
Upgrade on the plan page; the new limits apply on the next request.
Pagination
List endpoints return at most 500 rows per request and wrap them in an envelope:
{
"items": [ ... ],
"total": 1284,
"limit": 50,
"offset": 0
}| Field | Type | Description |
|---|---|---|
items | object[] | The rows on this page, newest publication first. |
total | integer | Rows matching the filters across all pages, within your plan's limits. |
limit | integer | Page size used. |
offset | integer | Offset used. |
Parameters
Every list endpoint accepts the same paging parameters alongside its own filters:
| Parameter | Type | Description |
|---|---|---|
limit | integer | Rows per page, 1–500. Defaults to 50. |
offset | integer | Rows to skip before the first returned one. Defaults to 0. |
published_after | datetime | Only rows the source published after this instant (ISO 8601). Use it for incremental syncs. |
Walking the archive
Increase offset by limit until offset + items.length reaches total:
GET /transactions?country=NL&limit=500&offset=0
GET /transactions?country=NL&limit=500&offset=500
GET /transactions?country=NL&limit=500&offset=1000Keeping up to date
Rows are ordered by published_at, newest first. To poll for new filings, remember the largest published_at you have seen and pass it as published_after on the next call. New rows never shift the ones you have already read, unlike a bare offset.
GET /holdings?published_after=2026-09-14T08:02:11Z&limit=500Errors
Errors use standard HTTP status codes and a JSON body with a human-readable detail:
{ "detail": "The Free plan covers NL, ES only; upgrade for DE" }| Status | Meaning |
|---|---|
| 401 | Missing, invalid or revoked API key, or the account is inactive. |
| 403 | The request is outside your plan (e.g. a country it does not cover). |
| 422 | A query parameter is invalid (e.g. limit above 500 or a malformed published_after). |
| 429 | Hourly rate limit exceeded. Wait for Retry-After seconds. |
GET/transactions
Managers' transactions: dealings by persons discharging managerial responsibilities (PDMRs) and their closely associated persons, as published under MAR Article 19 and equivalent national regimes. Newest publication first, paginated.
Query parameters
| Parameter | Type | Description |
|---|---|---|
country | string | Two-letter ISO code of the register the filing came from, e.g. NL. Case-insensitive. |
issuer_id | string | Only transactions at this issuer (iss_…). |
person_id | string | Only transactions by this person (per_…). |
isin | string | Only transactions at the issuer of this instrument. |
lei | string | Only transactions at the issuer with this LEI. |
issuer | string | Issuer name substring, case-insensitive. |
transaction_type | TransactionType | Only this category, e.g. SHARE_AWARD. |
direction | TransactionDirection | Only deals that raised (ACQUISITION) or lowered (DISPOSAL) the holding. |
role | PdmrRole | Only deals made in this role, e.g. CEO. |
limit | integer | Rows per page, 1–500. Defaults to 50. |
offset | integer | Rows to skip before the first returned one. Defaults to 0. |
published_after | datetime | Only rows the source published after this instant (ISO 8601). Use it for incremental syncs. |
Example
curl "https://api.duceusapi.com/transactions?country=SE&limit=1" \
-H "Authorization: Bearer YOUR_API_KEY"{
"items": [
{
"id": 48211,
"issuer_id": "iss_01K5APX7Q3S6R2M4V8JH0N3T9E",
"issuer_name": "Investor AB",
"issuer_lei": "549300VEBQPHRZBKUX38",
"country": "SE",
"person_id": "per_01K5APX8C4T7S3N5W9KJ1P4V0F",
"person_full_name": "Jane Doe",
"person_is_anonymized": false,
"person_role": "CEO",
"person_position": "Verkställande direktör",
"is_closely_associated": false,
"transaction_date": "2026-09-12",
"transaction_type": "BUY",
"direction": "ACQUISITION",
"transaction_type_raw": "Acquisition",
"instrument": { "isin": "SE0015811963", "kind": "SHARE", "name": null, "ticker": null, "mic": null },
"instrument_isin": "SE0015811963",
"instrument_raw": "Aktier",
"volume": 1500.0,
"unit_price": 312.4,
"currency": "SEK",
"source_name": "fi-insyn",
"source_reference": "1234567",
"source_url": "https://marknadssok.fi.se/...",
"published_at": "2026-09-14T08:02:11Z"
}
],
"total": 21730,
"limit": 1,
"offset": 0
}Response fields
Each entry of items:
| Field | Type | Description |
|---|---|---|
id | integer | Stable identifier of the transaction. |
issuer_id | string | Issuer public id (iss_…); see /issuers/{id}. |
issuer_name | string | Issuer the manager is associated with. |
issuer_lei | string | null | Issuer's Legal Entity Identifier. |
country | string | Two-letter ISO code of the register the filing came from. |
person_id | string | Person public id (per_…); see /persons/{id}. |
person_full_name | string | null | Name of the manager (PDMR) or closely associated person. |
person_is_anonymized | boolean | true when the source never discloses the person's identity, only their role. Check this rather than inferring anonymity from a null name. |
person_role | PdmrRole | Role held when dealing, derived from the filing's wording. |
person_position | string | null | Position exactly as the filing stated it. |
is_closely_associated | boolean | The dealer is a person or entity closely associated with a PDMR, not the PDMR. |
transaction_date | date | Day the transaction was executed (YYYY-MM-DD). |
transaction_type | TransactionType | What happened, as a closed category. BUY and SELL are market-price deals the person chose; the other values are the non-market ways a holding changes (see the enumeration below). |
direction | TransactionDirection | Whether the holding went up (ACQUISITION) or down (DISPOSAL). NEUTRAL for a pledge, UNKNOWN when the filing's wording didn't say. It is an explicit value, so "we couldn't tell" is never a missing key. |
transaction_type_raw | string | null | The filing's own wording for the nature of the transaction, verbatim ("Souscription", "Exercise & Sale"). |
instrument | Instrument | null | The traded instrument, when the filing named an ISIN (see below). |
instrument_isin | string | null | ISIN of the traded instrument. |
instrument_raw | string | null | Instrument as described in the filing, when no ISIN was given or alongside it. |
volume | number | Number of units traded. |
unit_price | number | null | Price per unit in `currency`. Null when the filing states no single price per unit, e.g. a tender paid partly in cash and partly in shares; `source_url` has the terms. |
currency | string | null | ISO 4217 currency code. Null only with unit_price, when the filing names no currency (an unpriced share award). |
source_name | string | Register the filing came from. |
source_reference | string | The filing's reference at that register. |
source_url | string | null | Original filing at the regulator or exchange. |
published_at | datetime | When the source published the filing (ISO 8601, UTC). |
Public ids are opaque strings with a type prefix (iss_ issuers, per_ persons, hld_ holders). They are stable: when two records turn out to be the same entity and are merged, the retired id keeps resolving to the surviving record, whose id the response then carries.
Instrument
| Field | Type | Description |
|---|---|---|
isin | string | ISIN. |
kind | InstrumentKind | SHARE, BOND, DERIVATIVE, OTHER or UNKNOWN (not yet classified). |
name | string | null | Instrument name from reference data, when available. |
ticker | string | null | Ticker, when available. |
mic | string | null | Primary market (ISO 10383 MIC), when available. |
GET/holdings
Major shareholding notifications: filings made when a holder's voting rights or capital cross a disclosure threshold (Transparency Directive and equivalents). Newest publication first, paginated.
Query parameters
| Parameter | Type | Description |
|---|---|---|
country | string | Two-letter ISO code of the register the filing came from. Case-insensitive. |
issuer_id | string | Only notifications for this issuer (iss_…). |
holder_id | string | Only notifications by this holder (hld_…). |
holder_lei | string | Only notifications by the holder with this LEI. |
ultimate_parent_lei | string | Every holder GLEIF places under this parent, e.g. all BlackRock entities. |
isin | string | Only notifications for the issuer of this instrument. |
lei | string | Only notifications for the issuer with this LEI. |
issuer | string | Issuer name substring, case-insensitive. |
limit | integer | Rows per page, 1–500. Defaults to 50. |
offset | integer | Rows to skip before the first returned one. Defaults to 0. |
published_after | datetime | Only rows the source published after this instant (ISO 8601). Use it for incremental syncs. |
Example
curl "https://api.duceusapi.com/holdings?country=GB&limit=1" \
-H "Authorization: Bearer YOUR_API_KEY"{
"items": [
{
"id": 9034,
"issuer_id": "iss_01K5APX7Q3S6R2M4V8JH0N3T9E",
"issuer_name": "Example plc",
"issuer_lei": "213800EXAMPLE0000001",
"country": "GB",
"holder_id": "hld_01K5APX9D5V8T4P6X0KM2Q5W1G",
"holder_name": "BlackRock Fund Advisors",
"holder_type": "LEGAL_ENTITY",
"holder_lei": "549300YOOGP0Y1M95C20",
"holder_ultimate_parent_lei": "529900VBK42Y5HHRMD23",
"holder_ultimate_parent_name": "BlackRock, Inc.",
"event_type": "ACQUISITION",
"reason_raw": null,
"event_date": "2026-09-10",
"notification_date": "2026-09-11",
"threshold_crossed_pct": 5.0,
"threshold_direction": "ABOVE",
"new_pct_voting": 5.12,
"new_pct_capital": null,
"new_pct_voting_short": null,
"new_voting_rights_count": 51200000,
"prev_pct_voting": 4.97,
"prev_pct_capital": null,
"issuer_total_voting_rights": 1000000000,
"issuer_total_shares": null,
"is_late": false,
"supersedes_reference": null,
"parties": [
{ "holder_id": "hld_01K5APX9D5V8T4P6X0KM2Q5W1G", "name": "BlackRock Fund Advisors", "holder_lei": "549300YOOGP0Y1M95C20", "role": "OBLIGED", "pct_voting": 5.12, "pct_capital": null }
],
"positions": [
{
"direction": "LONG",
"position_kind": "SHARES",
"holding_basis": "INDIRECT",
"instrument_isin": "GB00B03MLX29",
"instrument_type_raw": null,
"instrument_description": null,
"count": 51200000,
"voting_rights_count": 51200000,
"pct_voting": 5.12,
"pct_capital": null,
"expiration": null,
"exercise_price_raw": null,
"settlement": null
}
],
"source_url": "https://www.londonstockexchange.com/...",
"published_at": "2026-09-11T15:30:00Z"
}
],
"total": 5402,
"limit": 1,
"offset": 0
}Response fields
Each entry of items:
| Field | Type | Description |
|---|---|---|
id | integer | Stable identifier of the notification. |
issuer_id | string | Issuer public id (iss_…), usable as the `issuer_id` filter. |
issuer_name | string | Issuer whose shares are held. |
issuer_lei | string | null | Issuer's Legal Entity Identifier. |
country | string | Two-letter ISO code of the register the filing came from. |
holder_id | string | Holder public id (hld_…), usable as the `holder_id` filter. |
holder_name | string | The party obliged to notify. |
holder_type | HolderType | Legal entity, natural person, concert, issuer itself or unknown. |
holder_lei | string | null | Holder's Legal Entity Identifier. |
holder_ultimate_parent_lei | string | null | LEI of the entity that ultimately consolidates the holder (GLEIF Level 2); group entities share it. |
holder_ultimate_parent_name | string | null | Name of that parent. |
event_type | HoldingEventType | What triggered the notification. |
reason_raw | string | null | The source's own wording or trigger code for the event. |
event_date | date | null | Day the threshold was crossed. |
notification_date | date | null | Day the issuer or regulator was notified, when the source separates it. |
threshold_crossed_pct | number | null | Threshold that was crossed, in percent. |
threshold_direction | ThresholdDirection | Whether the holding went ABOVE or BELOW the threshold. |
new_pct_voting | number | Resulting share of voting rights, in percent. |
new_pct_capital | number | null | Resulting share of capital, in percent. |
new_pct_voting_short | number | null | Resulting short position in voting rights, in percent. |
new_voting_rights_count | integer | null | Absolute number of voting rights held after the event. |
prev_pct_voting | number | null | Share of voting rights before the event. |
prev_pct_capital | number | null | Share of capital before the event. |
issuer_total_voting_rights | integer | null | Denominator the filer used for voting rights. |
issuer_total_shares | integer | null | Denominator the filer used for shares. |
is_late | boolean | The source flagged the notification as filed late. |
supersedes_reference | string | null | Source reference of an earlier filing this one corrects. |
parties | Party[] | Chain of parties behind the holding (see below). |
positions | Position[] | Instrument-level breakdown (see below). |
source_name | string | Register the filing came from. |
source_reference | string | The filing's reference at that register. |
source_url | string | null | Original filing at the regulator or exchange. |
published_at | datetime | When the source published the filing (ISO 8601, UTC). |
Party
The first party is always the obliged holder. Further parties describe the ownership chain reported in the filing: controlled undertakings, beneficial owners, members acting in concert.
| Field | Type | Description |
|---|---|---|
holder_id | string | Holder public id (hld_…) of this party. |
name | string | Party name. |
holder_lei | string | null | Party's Legal Entity Identifier. |
role | PartyRole | The party's role in the holding chain. |
pct_voting | number | null | Voting rights attributed to this party, in percent. |
pct_capital | number | null | Capital attributed to this party, in percent. |
Position
One entry per instrument line in the filing: plain shares, derivatives, lent stock and so on.
| Field | Type | Description |
|---|---|---|
direction | PositionDirection | LONG or SHORT. |
position_kind | PositionKind | Shares, financial instrument, lending, tender, discretionary voting or other. |
holding_basis | HoldingBasis | DIRECT, INDIRECT or UNKNOWN. |
instrument_isin | string | null | ISIN of the instrument. |
instrument_type_raw | string | null | Instrument type as written in the filing. |
instrument_description | string | null | Free-text description from the filing. |
count | integer | null | Number of shares or instruments. |
voting_rights_count | integer | null | Voting rights attached to the position. |
pct_voting | number | null | Share of voting rights, in percent. |
pct_capital | number | null | Share of capital, in percent. |
expiration | string | null | Expiration of the instrument, as written in the filing. |
exercise_price_raw | string | null | Exercise price, as written in the filing. |
settlement | Settlement | null | PHYSICAL or CASH. |
GET/threshold-events
The subset of /holdings that records an actual crossing: filings whose direction the source states as above or below a disclosure line. Filings with an unknown direction are excluded, so every row is a holder entering, leaving or moving between thresholds. Same response shape as /holdings.
Query parameters
| Parameter | Type | Description |
|---|---|---|
country | string | Two-letter ISO code of the register the filing came from. Case-insensitive. |
direction | ThresholdDirection | ABOVE or BELOW. Default: both. |
min_pct | number | Only stakes whose resulting voting share is at least this percent. |
event_type | HoldingEventType | ACQUISITION, DISPOSAL, CHANGE_IN_BREAKDOWN or OTHER. |
holder_type | HolderType | Restrict to one kind of holder, e.g. NATURAL_PERSON. |
limit | integer | Rows per page, 1–500. Defaults to 50. |
offset | integer | Rows to skip before the first returned one. Defaults to 0. |
published_after | datetime | Only rows the source published after this instant (ISO 8601). Use it for incremental syncs. |
Example
New stakes of 5% or more, i.e. likely new significant shareholders:
curl "https://api.duceusapi.com/threshold-events?direction=ABOVE&min_pct=5&limit=50" \
-H "Authorization: Bearer YOUR_API_KEY"GET/issuers
Companies with at least one disclosure in either dataset, alphabetically. Use isin or lei to resolve a security to its issuer, or issuer to search by name. Only issuers in countries your plan covers are listed.
Query parameters
| Parameter | Type | Description |
|---|---|---|
country | string | Only issuers with filings on this register. Case-insensitive. |
isin | string | The issuer of this instrument (any of its share classes or bonds). |
lei | string | The issuer with this LEI. |
issuer | string | Name substring, matched against the source name, the GLEIF legal name and every spelling a register has used. |
limit | integer | Rows per page, 1–500. Defaults to 50. |
offset | integer | Rows to skip before the first returned one. Defaults to 0. |
Example
curl "https://api.duceusapi.com/issuers?issuer=fugro" \
-H "Authorization: Bearer YOUR_API_KEY"{
"items": [
{
"id": "iss_01K5APX7Q3S6R2M4V8JH0N3T9E",
"name": "Fugro N.V.",
"lei": "7245000R8GNBSDTSZ396",
"legal_name": "Fugro N.V.",
"jurisdiction": "NL",
"ultimate_parent_lei": null,
"ultimate_parent_name": null,
"countries": ["NL"],
"instruments": [{ "isin": "NL00150003E1", "kind": "SHARE", "name": null, "ticker": null, "mic": null }],
"transaction_count": 0,
"holding_count": 2,
"latest_published_at": "2026-09-10T00:00:00Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}Response fields
| Field | Type | Description |
|---|---|---|
id | string | Issuer public id (iss_…), usable as `issuer_id` on the list endpoints and in the issuer endpoints. |
name | string | Issuer name as first reported by a source. |
lei | string | null | Legal Entity Identifier. |
legal_name | string | null | Registered legal name (GLEIF), when resolved. |
jurisdiction | string | null | Two-letter ISO code of the country of incorporation (GLEIF). |
ultimate_parent_lei | string | null | LEI of the entity that ultimately consolidates the issuer, when GLEIF records one. |
ultimate_parent_name | string | null | Name of that parent. |
countries | string[] | Registers the issuer has filings on. A dual-listed company has more than one. |
instruments | Instrument[] | Instruments known for the issuer (shares, bonds, warrants); see the Instrument fields under /transactions. |
transaction_count | integer | Managers' transactions visible under your plan. |
holding_count | integer | Shareholding notifications visible under your plan. |
latest_published_at | datetime | null | Most recent publication of either kind. |
Public ids are opaque strings with a type prefix (iss_ issuers, per_ persons, hld_ holders). They are stable: when two records turn out to be the same entity and are merged, the retired id keeps resolving to the surviving record, whose id the response then carries.
GET/issuers/{id}
One issuer, same shape as an entry of /issuers. Returns 404 for an unknown id and 403 when none of the issuer's registers is inside your plan. An id retired by a merge resolves to the surviving issuer.
curl "https://api.duceusapi.com/issuers/iss_01K5APX7Q3S6R2M4V8JH0N3T9E" \
-H "Authorization: Bearer YOUR_API_KEY"GET/issuers/{id}/timeline
Every disclosure about one issuer (managers' transactions and shareholding notifications merged), newest publication first. Same item shape and paging as /feed.
curl "https://api.duceusapi.com/issuers/iss_01K5APX7Q3S6R2M4V8JH0N3T9E/timeline?limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"GET/persons
Named managers (PDMRs) and closely associated persons with at least one transaction you may see, as identities: one entry per person with every issuer they have dealt at. Anonymised persons are excluded.
Query parameters
| Parameter | Type | Description |
|---|---|---|
q | string | Name substring, case-insensitive. |
country | string | Only persons with filings on this register. |
issuer_id | string | Only insiders of this issuer (iss_…). |
role | PdmrRole | Only persons holding this role somewhere. |
limit | integer | Rows per page, 1–500. Defaults to 50. |
offset | integer | Rows to skip before the first returned one. Defaults to 0. |
Example
curl "https://api.duceusapi.com/persons?q=qviberg" \
-H "Authorization: Bearer YOUR_API_KEY"{
"items": [
{
"id": "per_01K5APX8C4T7S3N5W9KJ1P4V0F",
"full_name": "Johan Qviberg",
"is_anonymized": false,
"mandates": [
{
"issuer_id": "iss_01K5APX7Q3S6R2M4V8JH0N3T9E",
"issuer_name": "Investment AB Öresund",
"role": "BOARD_MEMBER",
"position": "Styrelseledamot",
"is_closely_associated": false,
"first_seen": "2024-03-12",
"last_seen": "2026-08-30",
"transaction_count": 14
}
],
"linked_holders": [],
"transaction_count": 14,
"latest_published_at": "2026-09-01T07:30:00Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}Response fields
| Field | Type | Description |
|---|---|---|
id | string | Person public id (per_…), usable as the `person_id` filter on /transactions. |
full_name | string | null | Name; null only for anonymised persons. |
is_anonymized | boolean | The source never disclosed this person's identity. |
mandates | Mandate[] | Every issuer the person has dealt at, with role and span (see below). |
linked_holders | LinkedHolder[] | Major-holdings records accepted as the same individual: holder_id, name, holder_type, lei, confidence, auto_accepted. |
transaction_count | integer | Transactions visible under your plan. |
latest_published_at | datetime | null | Most recent transaction publication. |
Mandate
| Field | Type | Description |
|---|---|---|
issuer_id | string | Issuer public id (iss_…). |
issuer_name | string | Issuer name. |
role | PdmrRole | Role at that issuer, derived from the filings' wording. |
position | string | null | Position exactly as the most recent filing stated it. |
is_closely_associated | boolean | The person is closely associated with a PDMR of the issuer, not a PDMR themselves. |
first_seen | date | Earliest transaction date filed under this mandate. |
last_seen | date | Latest transaction date filed under this mandate. |
transaction_count | integer | Transactions under this mandate visible under your plan. |
GET/persons/{id}
One person, same shape as an entry of /persons. /persons/{id}/transactions lists their deals across every mandate, newest publication first, with the same item shape and paging as /transactions.
curl "https://api.duceusapi.com/persons/per_01K5APX8C4T7S3N5W9KJ1P4V0F/transactions?limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"GET/holders
Parties that have notified a major holding you may see: funds, banks, holding companies and individuals. Where a holder has been confirmed to be the same individual as a PDMR, linked_persons carries that identity, joining the two datasets.
Query parameters
| Parameter | Type | Description |
|---|---|---|
q | string | Name substring (source or legal name), case-insensitive. |
country | string | Only holders with notifications on this register. |
holder_type | HolderType | Restrict to one kind of holder. |
ultimate_parent_lei | string | Every holder GLEIF places under this parent. |
linked_only | boolean | Only holders confirmed to be the same person as a PDMR. |
limit | integer | Rows per page, 1–500. Defaults to 50. |
offset | integer | Rows to skip before the first returned one. Defaults to 0. |
Example
curl "https://api.duceusapi.com/holders?ultimate_parent_lei=529900VBK42Y5HHRMD23" \
-H "Authorization: Bearer YOUR_API_KEY"Response fields
| Field | Type | Description |
|---|---|---|
id | string | Holder public id (hld_…), usable as the `holder_id` filter on /holdings. |
name | string | Holder name as first reported by a source. |
legal_name | string | null | Registered legal name (GLEIF), when resolved. |
holder_type | HolderType | Legal entity, natural person, concert, issuer itself or unknown. |
lei | string | null | Legal Entity Identifier. |
country | string | null | Country of the holder, as reported. |
ultimate_parent_lei | string | null | LEI of the ultimate parent (GLEIF), shared by every entity of one group. |
ultimate_parent_name | string | null | Name of that parent. |
linked_persons | LinkedPerson[] | PDMR identities accepted as the same individual: person_id, full_name, mandates, confidence, match_method, auto_accepted. |
holding_count | integer | Notifications by this holder visible under your plan. |
transaction_count | integer | Transactions of the linked persons visible under your plan. |
latest_published_at | datetime | null | Most recent notification publication. |
positions | Position[] | Detail endpoint only: the holder's latest notification per issuer, largest stake first (issuer_id, issuer_name, country, pct_voting, pct_capital, event_type, as_of, notification_id). |
GET/holders/{id}
One holder with positions: its latest notification in each issuer, largest stake first. /holders/{id}/timeline merges the holder's own notifications with every transaction of a linked PDMR identity, with the same item shape and paging as /feed.
curl "https://api.duceusapi.com/holders/hld_01K5APX9D5V8T4P6X0KM2Q5W1G" \
-H "Authorization: Bearer YOUR_API_KEY"GET/feed
Both datasets in one stream ordered by published_at, for clients that would otherwise poll /transactions and /holdings in lockstep. Each item carries a few common fields plus the full row under transaction or holding, depending on kind.
Query parameters
| Parameter | Type | Description |
|---|---|---|
country | string | Two-letter ISO code of the register the filing came from. Case-insensitive. |
limit | integer | Rows per page, 1–500. Defaults to 50. |
offset | integer | Rows to skip before the first returned one. Defaults to 0. |
published_after | datetime | Only rows the source published after this instant (ISO 8601). Use it for incremental syncs. |
Example
curl "https://api.duceusapi.com/feed?published_after=2026-09-15T09:00:00Z&limit=500" \
-H "Authorization: Bearer YOUR_API_KEY"{
"items": [
{
"kind": "holding",
"id": 39,
"issuer_id": "iss_01K5APX7Q3S6R2M4V8JH0N3T9E",
"issuer_name": "J SAINSBURY PLC",
"country": "GB",
"event_date": "2026-09-11",
"published_at": "2026-09-15T12:00:00Z",
"transaction": null,
"holding": { ... }
},
{
"kind": "transaction",
"id": 1087,
"issuer_id": "iss_01K5APXB1F7X0V6R8Z2MP4S7Y3J",
"issuer_name": "Investor AB",
"country": "SE",
"event_date": "2026-09-12",
"published_at": "2026-09-15T11:58:00Z",
"transaction": { ... },
"holding": null
}
],
"total": 1357,
"limit": 500,
"offset": 0
}Item fields
| Field | Type | Description |
|---|---|---|
kind | string | transaction or holding. |
id | integer | Identifier within its kind (transaction id or notification id). |
issuer_id | string | Issuer public id (iss_…). |
issuer_name | string | Issuer name. |
country | string | Register the filing came from. |
event_date | date | null | Transaction date, or the holding's event date when stated. |
published_at | datetime | Publication time; the stream is ordered by this, newest first. |
transaction | Transaction | null | Full /transactions row when `kind` is transaction. |
holding | Holding | null | Full /holdings row when `kind` is holding. |
Deep offset paging over the feed is slower than over a single dataset; prefer published_after for keeping up to date.
GET/coverage
What the archive holds per register and dataset, whole history. Plan limits are reported (in_plan), not applied, so you can see what an upgrade would add. Plain array.
curl "https://api.duceusapi.com/coverage" \
-H "Authorization: Bearer YOUR_API_KEY"Response fields
| Field | Type | Description |
|---|---|---|
country | string | Two-letter ISO code of the register. |
source_name | string | Register name. |
kind | string | PDMR (managers' transactions) or MAJOR_HOLDING. |
disclosures | integer | Filings archived, whole history. |
earliest_published_at | datetime | null | Oldest filing. |
latest_published_at | datetime | null | Newest filing. |
in_plan | boolean | Whether your plan may read this register. |
GET/health
Unauthenticated liveness check. Returns:
{ "status": "ok" }Enumerations
All enumerated fields are upper-case strings. New values may be added; treat unknown ones as OTHER.
TransactionType
BUYmarket-price purchase the person choseSELLmarket-price sale the person choseOPTION_EXERCISEexercise or conversion of options, warrants, convertiblesOPTION_GRANTgrant of options, RSUs, PSUs or other conditional rights, not yet sharesSUBSCRIPTIONnew shares taken up in an issue, rights issue or placementSHARE_AWARDshares received under a plan: vesting, matching shares, board fees, salary sharesGIFTgiven or received without paymentINHERITANCEreceived on deathDIVIDEND_IN_KINDshares received as a dividendINTERNAL_TRANSFERmoved between the person's own accounts or entitiesPLEDGEpledged, released, lent or returned; no change of beneficial ownerEXCHANGEswapped in a merger, tender or takeover offer, buyback, swap unwindOTHERthe filing's wording matched none of the above; see transaction_type_raw
TransactionDirection
ACQUISITIONthe holding went upDISPOSALthe holding went downNEUTRALunchanged (a pledge)UNKNOWNthe filing didn't say
PdmrRole
CEOCFOEXECUTIVECHAIRBOARD_MEMBERCLOSELY_ASSOCIATEDOTHER
InstrumentKind
SHAREBONDDERIVATIVEOTHERUNKNOWN
HolderType
LEGAL_ENTITYNATURAL_PERSONCONCERTISSUER_SELFUNKNOWN
HoldingEventType
ACQUISITIONDISPOSALCHANGE_IN_BREAKDOWNOTHER
ThresholdDirection
ABOVEBELOWUNKNOWN
PositionDirection
LONGSHORT
PositionKind
SHARESINSTRUMENTLENDINGTENDERDISCRETIONARY_VOTINGOTHER
HoldingBasis
DIRECTINDIRECTUNKNOWN
Settlement
PHYSICALCASH
PartyRole
OBLIGEDBENEFICIAL_OWNERDIRECT_HOLDERCONCERT_MEMBERCONTROLLED_UNDERTAKINGGROUP_REPRESENTATIVE