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_KEY

You 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: 1758294000

X-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.

PlanRequests / hourTransactionsShareholdings
Free10Sweden & Spain, from 2024-01-01Netherlands, full archive
Full100All countries, full archiveAll 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.
  • /feed and /issuers span 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
}
FieldTypeDescription
itemsobject[]The rows on this page, newest publication first.
totalintegerRows matching the filters across all pages, within your plan's limits.
limitintegerPage size used.
offsetintegerOffset used.

Parameters

Every list endpoint accepts the same paging parameters alongside its own filters:

ParameterTypeDescription
limitintegerRows per page, 1–500. Defaults to 50.
offsetintegerRows to skip before the first returned one. Defaults to 0.
published_afterdatetimeOnly 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=1000

Keeping 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=500

Errors

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" }
StatusMeaning
401Missing, invalid or revoked API key, or the account is inactive.
403The request is outside your plan (e.g. a country it does not cover).
422A query parameter is invalid (e.g. limit above 500 or a malformed published_after).
429Hourly 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

ParameterTypeDescription
countrystringTwo-letter ISO code of the register the filing came from, e.g. NL. Case-insensitive.
issuer_idstringOnly transactions at this issuer (iss_…).
person_idstringOnly transactions by this person (per_…).
isinstringOnly transactions at the issuer of this instrument.
leistringOnly transactions at the issuer with this LEI.
issuerstringIssuer name substring, case-insensitive.
transaction_typeTransactionTypeOnly this category, e.g. SHARE_AWARD.
directionTransactionDirectionOnly deals that raised (ACQUISITION) or lowered (DISPOSAL) the holding.
rolePdmrRoleOnly deals made in this role, e.g. CEO.
limitintegerRows per page, 1–500. Defaults to 50.
offsetintegerRows to skip before the first returned one. Defaults to 0.
published_afterdatetimeOnly 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:

FieldTypeDescription
idintegerStable identifier of the transaction.
issuer_idstringIssuer public id (iss_…); see /issuers/{id}.
issuer_namestringIssuer the manager is associated with.
issuer_leistring | nullIssuer's Legal Entity Identifier.
countrystringTwo-letter ISO code of the register the filing came from.
person_idstringPerson public id (per_…); see /persons/{id}.
person_full_namestring | nullName of the manager (PDMR) or closely associated person.
person_is_anonymizedbooleantrue when the source never discloses the person's identity, only their role. Check this rather than inferring anonymity from a null name.
person_rolePdmrRoleRole held when dealing, derived from the filing's wording.
person_positionstring | nullPosition exactly as the filing stated it.
is_closely_associatedbooleanThe dealer is a person or entity closely associated with a PDMR, not the PDMR.
transaction_datedateDay the transaction was executed (YYYY-MM-DD).
transaction_typeTransactionTypeWhat 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).
directionTransactionDirectionWhether 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_rawstring | nullThe filing's own wording for the nature of the transaction, verbatim ("Souscription", "Exercise & Sale").
instrumentInstrument | nullThe traded instrument, when the filing named an ISIN (see below).
instrument_isinstring | nullISIN of the traded instrument.
instrument_rawstring | nullInstrument as described in the filing, when no ISIN was given or alongside it.
volumenumberNumber of units traded.
unit_pricenumber | nullPrice 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.
currencystring | nullISO 4217 currency code. Null only with unit_price, when the filing names no currency (an unpriced share award).
source_namestringRegister the filing came from.
source_referencestringThe filing's reference at that register.
source_urlstring | nullOriginal filing at the regulator or exchange.
published_atdatetimeWhen 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

FieldTypeDescription
isinstringISIN.
kindInstrumentKindSHARE, BOND, DERIVATIVE, OTHER or UNKNOWN (not yet classified).
namestring | nullInstrument name from reference data, when available.
tickerstring | nullTicker, when available.
micstring | nullPrimary 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

ParameterTypeDescription
countrystringTwo-letter ISO code of the register the filing came from. Case-insensitive.
issuer_idstringOnly notifications for this issuer (iss_…).
holder_idstringOnly notifications by this holder (hld_…).
holder_leistringOnly notifications by the holder with this LEI.
ultimate_parent_leistringEvery holder GLEIF places under this parent, e.g. all BlackRock entities.
isinstringOnly notifications for the issuer of this instrument.
leistringOnly notifications for the issuer with this LEI.
issuerstringIssuer name substring, case-insensitive.
limitintegerRows per page, 1–500. Defaults to 50.
offsetintegerRows to skip before the first returned one. Defaults to 0.
published_afterdatetimeOnly 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:

FieldTypeDescription
idintegerStable identifier of the notification.
issuer_idstringIssuer public id (iss_…), usable as the `issuer_id` filter.
issuer_namestringIssuer whose shares are held.
issuer_leistring | nullIssuer's Legal Entity Identifier.
countrystringTwo-letter ISO code of the register the filing came from.
holder_idstringHolder public id (hld_…), usable as the `holder_id` filter.
holder_namestringThe party obliged to notify.
holder_typeHolderTypeLegal entity, natural person, concert, issuer itself or unknown.
holder_leistring | nullHolder's Legal Entity Identifier.
holder_ultimate_parent_leistring | nullLEI of the entity that ultimately consolidates the holder (GLEIF Level 2); group entities share it.
holder_ultimate_parent_namestring | nullName of that parent.
event_typeHoldingEventTypeWhat triggered the notification.
reason_rawstring | nullThe source's own wording or trigger code for the event.
event_datedate | nullDay the threshold was crossed.
notification_datedate | nullDay the issuer or regulator was notified, when the source separates it.
threshold_crossed_pctnumber | nullThreshold that was crossed, in percent.
threshold_directionThresholdDirectionWhether the holding went ABOVE or BELOW the threshold.
new_pct_votingnumberResulting share of voting rights, in percent.
new_pct_capitalnumber | nullResulting share of capital, in percent.
new_pct_voting_shortnumber | nullResulting short position in voting rights, in percent.
new_voting_rights_countinteger | nullAbsolute number of voting rights held after the event.
prev_pct_votingnumber | nullShare of voting rights before the event.
prev_pct_capitalnumber | nullShare of capital before the event.
issuer_total_voting_rightsinteger | nullDenominator the filer used for voting rights.
issuer_total_sharesinteger | nullDenominator the filer used for shares.
is_latebooleanThe source flagged the notification as filed late.
supersedes_referencestring | nullSource reference of an earlier filing this one corrects.
partiesParty[]Chain of parties behind the holding (see below).
positionsPosition[]Instrument-level breakdown (see below).
source_namestringRegister the filing came from.
source_referencestringThe filing's reference at that register.
source_urlstring | nullOriginal filing at the regulator or exchange.
published_atdatetimeWhen 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.

FieldTypeDescription
holder_idstringHolder public id (hld_…) of this party.
namestringParty name.
holder_leistring | nullParty's Legal Entity Identifier.
rolePartyRoleThe party's role in the holding chain.
pct_votingnumber | nullVoting rights attributed to this party, in percent.
pct_capitalnumber | nullCapital attributed to this party, in percent.

Position

One entry per instrument line in the filing: plain shares, derivatives, lent stock and so on.

FieldTypeDescription
directionPositionDirectionLONG or SHORT.
position_kindPositionKindShares, financial instrument, lending, tender, discretionary voting or other.
holding_basisHoldingBasisDIRECT, INDIRECT or UNKNOWN.
instrument_isinstring | nullISIN of the instrument.
instrument_type_rawstring | nullInstrument type as written in the filing.
instrument_descriptionstring | nullFree-text description from the filing.
countinteger | nullNumber of shares or instruments.
voting_rights_countinteger | nullVoting rights attached to the position.
pct_votingnumber | nullShare of voting rights, in percent.
pct_capitalnumber | nullShare of capital, in percent.
expirationstring | nullExpiration of the instrument, as written in the filing.
exercise_price_rawstring | nullExercise price, as written in the filing.
settlementSettlement | nullPHYSICAL 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

ParameterTypeDescription
countrystringTwo-letter ISO code of the register the filing came from. Case-insensitive.
directionThresholdDirectionABOVE or BELOW. Default: both.
min_pctnumberOnly stakes whose resulting voting share is at least this percent.
event_typeHoldingEventTypeACQUISITION, DISPOSAL, CHANGE_IN_BREAKDOWN or OTHER.
holder_typeHolderTypeRestrict to one kind of holder, e.g. NATURAL_PERSON.
limitintegerRows per page, 1–500. Defaults to 50.
offsetintegerRows to skip before the first returned one. Defaults to 0.
published_afterdatetimeOnly 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

ParameterTypeDescription
countrystringOnly issuers with filings on this register. Case-insensitive.
isinstringThe issuer of this instrument (any of its share classes or bonds).
leistringThe issuer with this LEI.
issuerstringName substring, matched against the source name, the GLEIF legal name and every spelling a register has used.
limitintegerRows per page, 1–500. Defaults to 50.
offsetintegerRows 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

FieldTypeDescription
idstringIssuer public id (iss_…), usable as `issuer_id` on the list endpoints and in the issuer endpoints.
namestringIssuer name as first reported by a source.
leistring | nullLegal Entity Identifier.
legal_namestring | nullRegistered legal name (GLEIF), when resolved.
jurisdictionstring | nullTwo-letter ISO code of the country of incorporation (GLEIF).
ultimate_parent_leistring | nullLEI of the entity that ultimately consolidates the issuer, when GLEIF records one.
ultimate_parent_namestring | nullName of that parent.
countriesstring[]Registers the issuer has filings on. A dual-listed company has more than one.
instrumentsInstrument[]Instruments known for the issuer (shares, bonds, warrants); see the Instrument fields under /transactions.
transaction_countintegerManagers' transactions visible under your plan.
holding_countintegerShareholding notifications visible under your plan.
latest_published_atdatetime | nullMost 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}/shareholders

A derived cap table: for every holder that has ever notified a stake in the issuer, that holder's most recent filing reduced to the fields describing the stake, largest first. Holders whose last filing put them at 0% are dropped.

This is computed from threshold notifications, not reported as such by any register. A holder's stake is only known as of its as_of date; movements that stay between thresholds never trigger a filing and are invisible here. The response is a plain array, not a paginated envelope.

Example

curl "https://api.duceusapi.com/issuers/iss_01K5APX7Q3S6R2M4V8JH0N3T9E/shareholders" \
  -H "Authorization: Bearer YOUR_API_KEY"
[
  {
    "holder_id": "hld_01K5APX9D5V8T4P6X0KM2Q5W1G",
    "holder_name": "DWS Investment GmbH",
    "holder_type": "UNKNOWN",
    "holder_lei": "549300MJ6B0X7BZ7VZ26",
    "holder_ultimate_parent_lei": "529900IBM3XAY4TH9G96",
    "holder_ultimate_parent_name": "DWS Group GmbH & Co. KGaA",
    "pct_voting": 3.04,
    "pct_capital": 3.04,
    "pct_voting_short": null,
    "voting_rights_count": null,
    "event_type": "OTHER",
    "threshold_direction": "UNKNOWN",
    "as_of": "2026-09-10",
    "published_at": "2026-09-10T00:00:00Z",
    "notification_id": 247,
    "source_url": "https://www.afm.nl/..."
  },
  {
    "holder_id": "hld_01K5APXA0E6W9U5Q7Y1LN3R6X2H",
    "holder_name": "Goldman Sachs Group Inc., The",
    "holder_type": "UNKNOWN",
    "holder_lei": "784F5XWPLTWKTBV3E584",
    "holder_ultimate_parent_lei": null,
    "holder_ultimate_parent_name": null,
    "pct_voting": 3.03,
    "pct_capital": 3.03,
    "pct_voting_short": 2.52,
    "voting_rights_count": null,
    "event_type": "OTHER",
    "threshold_direction": "UNKNOWN",
    "as_of": "2026-09-10",
    "published_at": "2026-09-10T00:00:00Z",
    "notification_id": 245,
    "source_url": "https://www.afm.nl/..."
  }
]

Response fields

FieldTypeDescription
holder_idstringHolder public id (hld_…), usable as the `holder_id` filter on /holdings.
holder_namestringThe holder as named in its latest filing.
holder_typeHolderTypeLegal entity, natural person, concert, issuer itself or unknown.
holder_leistring | nullHolder's Legal Entity Identifier.
holder_ultimate_parent_leistring | nullLEI of the holder's ultimate parent (GLEIF), shared by every entity of one group.
holder_ultimate_parent_namestring | nullName of that parent.
pct_votingnumberShare of voting rights after the latest filing, in percent.
pct_capitalnumber | nullShare of capital, in percent.
pct_voting_shortnumber | nullShort position in voting rights, in percent.
voting_rights_countinteger | nullAbsolute number of voting rights held.
event_typeHoldingEventTypeWhat the latest filing reported.
threshold_directionThresholdDirectionDirection of the latest crossing.
as_ofdate | nullEvent date of the latest filing. The stake is only known as of this day.
published_atdatetimeWhen that filing was published.
notification_idinteger`id` of the underlying /holdings row.
source_urlstring | nullOriginal filing.

GET/issuers/{id}/shareholder-groups

The same cap table rolled up by ultimate parent: every holder that GLEIF says is consolidated by the same group becomes one line with its members listed, so the dozens of “BlackRock …” entities read as one shareholder. Holders without a known parent are their own group. Plain array, largest first.

curl "https://api.duceusapi.com/issuers/iss_01K5APX7Q3S6R2M4V8JH0N3T9E/shareholder-groups" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response fields

FieldTypeDescription
group_leistring | nullLEI of the ultimate parent (or of the holder itself when it has no parent); null for holders without a LEI.
group_namestringName of the parent, or of the holder.
pct_votingnumberSum of the members' voting shares: an upper bound, since group entities can report overlapping stakes.
member_countintegerHolders in the group.
membersShareholder[]The group's rows from /issuers/{id}/shareholders.

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

ParameterTypeDescription
qstringName substring, case-insensitive.
countrystringOnly persons with filings on this register.
issuer_idstringOnly insiders of this issuer (iss_…).
rolePdmrRoleOnly persons holding this role somewhere.
limitintegerRows per page, 1–500. Defaults to 50.
offsetintegerRows 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

FieldTypeDescription
idstringPerson public id (per_…), usable as the `person_id` filter on /transactions.
full_namestring | nullName; null only for anonymised persons.
is_anonymizedbooleanThe source never disclosed this person's identity.
mandatesMandate[]Every issuer the person has dealt at, with role and span (see below).
linked_holdersLinkedHolder[]Major-holdings records accepted as the same individual: holder_id, name, holder_type, lei, confidence, auto_accepted.
transaction_countintegerTransactions visible under your plan.
latest_published_atdatetime | nullMost recent transaction publication.

Mandate

FieldTypeDescription
issuer_idstringIssuer public id (iss_…).
issuer_namestringIssuer name.
rolePdmrRoleRole at that issuer, derived from the filings' wording.
positionstring | nullPosition exactly as the most recent filing stated it.
is_closely_associatedbooleanThe person is closely associated with a PDMR of the issuer, not a PDMR themselves.
first_seendateEarliest transaction date filed under this mandate.
last_seendateLatest transaction date filed under this mandate.
transaction_countintegerTransactions 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

ParameterTypeDescription
qstringName substring (source or legal name), case-insensitive.
countrystringOnly holders with notifications on this register.
holder_typeHolderTypeRestrict to one kind of holder.
ultimate_parent_leistringEvery holder GLEIF places under this parent.
linked_onlybooleanOnly holders confirmed to be the same person as a PDMR.
limitintegerRows per page, 1–500. Defaults to 50.
offsetintegerRows 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

FieldTypeDescription
idstringHolder public id (hld_…), usable as the `holder_id` filter on /holdings.
namestringHolder name as first reported by a source.
legal_namestring | nullRegistered legal name (GLEIF), when resolved.
holder_typeHolderTypeLegal entity, natural person, concert, issuer itself or unknown.
leistring | nullLegal Entity Identifier.
countrystring | nullCountry of the holder, as reported.
ultimate_parent_leistring | nullLEI of the ultimate parent (GLEIF), shared by every entity of one group.
ultimate_parent_namestring | nullName of that parent.
linked_personsLinkedPerson[]PDMR identities accepted as the same individual: person_id, full_name, mandates, confidence, match_method, auto_accepted.
holding_countintegerNotifications by this holder visible under your plan.
transaction_countintegerTransactions of the linked persons visible under your plan.
latest_published_atdatetime | nullMost recent notification publication.
positionsPosition[]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

ParameterTypeDescription
countrystringTwo-letter ISO code of the register the filing came from. Case-insensitive.
limitintegerRows per page, 1–500. Defaults to 50.
offsetintegerRows to skip before the first returned one. Defaults to 0.
published_afterdatetimeOnly 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

FieldTypeDescription
kindstringtransaction or holding.
idintegerIdentifier within its kind (transaction id or notification id).
issuer_idstringIssuer public id (iss_…).
issuer_namestringIssuer name.
countrystringRegister the filing came from.
event_datedate | nullTransaction date, or the holding's event date when stated.
published_atdatetimePublication time; the stream is ordered by this, newest first.
transactionTransaction | nullFull /transactions row when `kind` is transaction.
holdingHolding | nullFull /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

FieldTypeDescription
countrystringTwo-letter ISO code of the register.
source_namestringRegister name.
kindstringPDMR (managers' transactions) or MAJOR_HOLDING.
disclosuresintegerFilings archived, whole history.
earliest_published_atdatetime | nullOldest filing.
latest_published_atdatetime | nullNewest filing.
in_planbooleanWhether 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 chose
  • SELLmarket-price sale the person chose
  • OPTION_EXERCISEexercise or conversion of options, warrants, convertibles
  • OPTION_GRANTgrant of options, RSUs, PSUs or other conditional rights, not yet shares
  • SUBSCRIPTIONnew shares taken up in an issue, rights issue or placement
  • SHARE_AWARDshares received under a plan: vesting, matching shares, board fees, salary shares
  • GIFTgiven or received without payment
  • INHERITANCEreceived on death
  • DIVIDEND_IN_KINDshares received as a dividend
  • INTERNAL_TRANSFERmoved between the person's own accounts or entities
  • PLEDGEpledged, released, lent or returned; no change of beneficial owner
  • EXCHANGEswapped in a merger, tender or takeover offer, buyback, swap unwind
  • OTHERthe filing's wording matched none of the above; see transaction_type_raw

TransactionDirection

  • ACQUISITIONthe holding went up
  • DISPOSALthe holding went down
  • NEUTRALunchanged (a pledge)
  • UNKNOWNthe filing didn't say

PdmrRole

  • CEO
  • CFO
  • EXECUTIVE
  • CHAIR
  • BOARD_MEMBER
  • CLOSELY_ASSOCIATED
  • OTHER

InstrumentKind

  • SHARE
  • BOND
  • DERIVATIVE
  • OTHER
  • UNKNOWN

HolderType

  • LEGAL_ENTITY
  • NATURAL_PERSON
  • CONCERT
  • ISSUER_SELF
  • UNKNOWN

HoldingEventType

  • ACQUISITION
  • DISPOSAL
  • CHANGE_IN_BREAKDOWN
  • OTHER

ThresholdDirection

  • ABOVE
  • BELOW
  • UNKNOWN

PositionDirection

  • LONG
  • SHORT

PositionKind

  • SHARES
  • INSTRUMENT
  • LENDING
  • TENDER
  • DISCRETIONARY_VOTING
  • OTHER

HoldingBasis

  • DIRECT
  • INDIRECT
  • UNKNOWN

Settlement

  • PHYSICAL
  • CASH

PartyRole

  • OBLIGED
  • BENEFICIAL_OWNER
  • DIRECT_HOLDER
  • CONCERT_MEMBER
  • CONTROLLED_UNDERTAKING
  • GROUP_REPRESENTATIVE