NAV
Alternatives
Partner API

Partner API

Last updated: 28 September 2026

This documentation covers the Alternatives Partner API (v3) — the customer-facing interface for programmatic access to Alternatives’ private-market data across Southeast Asia. It is a read-only REST API that returns JSON and is intended for external API subscribers. Access is subscription-gated — each endpoint is protected by entitlements that are enabled according to your subscription.

v3 replaces v2. If you are migrating from the legacy API at api.alternatives.pe/api/v2, see the Migration Guide for a full breakdown of what changed.

What the API Covers

Entity TypeWhat It RepresentsExample
Capital ReceiversCompanies and startups that have raised fundingShopBack
Capital AllocatorsInvestors (VC firms, family offices, PE funds) that deploy capitalWavemaker Group
FundsIndividual fund vehicles managed by a capital allocatorWavemaker Pacific 1, Bain Capital Asia III
PeopleFounders, directors, and other individuals linked to companiesShanru Lai (ShopBack co-founder)
InvestorsInvestment totals for any entity that has been a buyer in a transaction, by base entity typeAny company, person, or shareholder group that has invested
Service ProvidersAuditors and professional-services firmsErnst & Young LLP
Legal EntitiesUnderlying registered companies — returned as a nested object on profile detail responsesAny registered business
Reference DataTaxonomies and lookup lists for filter fieldsCountries, stages, themes, industries

New to the API? The Data Model page explains how Profiles relate to the underlying Legal Entity and what each UUID identifies.

Base URL

https://api.altdmp.io/v3/partners/

All endpoints are relative to this base. For example, to list capital receivers:

curl https://api.altdmp.io/v3/partners/capital-receivers/ \
  -H "Authorization: Bearer YOUR_TOKEN"
import requests

BASE = "https://api.altdmp.io/v3/partners"
headers = {"Authorization": "Bearer YOUR_TOKEN"}

resp = requests.get(f"{BASE}/capital-receivers/", headers=headers)
print(resp.json())

Request Format

Most list endpoints support two calling styles:

On POST, the request body accepts only the filters object. Pagination and sorting (search, ordering, limit, offset) are always query parameters — sending them in the body returns 400 Bad Request.

Filtering has its own page. Filtering is the reference — every operator with its exact matching behavior, the accepted body shapes, the supported filter field list for each endpoint, and the cases where a filter is accepted but not applied.

filters goes in the body, never on the query string. The two directions are not interchangeable: search, ordering, limit, and offset are query parameters only, and filters is a body parameter only. A request that puts a value in ?filters= returns 400 Bad Request — it is not applied, and it is not ignored. A bare ?filters= with no value expresses no filter and is accepted. The sole exception is /investors/, which ignores a query-string filters and returns unfiltered results; do not rely on that.

Pagination

All list responses are paginated. Use limit and offset query parameters:

# Page 2 of results (20 per page)
curl "https://api.altdmp.io/v3/partners/capital-receivers/?limit=20&offset=20" \
  -H "Authorization: Bearer YOUR_TOKEN"
resp = requests.get(
    f"{BASE}/capital-receivers/",
    params={"limit": 20, "offset": 20},
    headers=headers,
)

Responses include count, next, and previous fields alongside results.

The page-size contract is the same on every list endpoint:

ParameterDefaultAccepted valuesBehavior outside that range
limit20Whole number from 1 to 1000Above 1000, the page is reduced to 1,000 rows and the request succeeds. Anything else — 0, a negative number, a decimal, or a non-numeric value — returns 400 Bad Request
offset0Whole number from 0 upwardA negative, decimal, or non-numeric value returns 400 Bad Request. There is no upper bound: an offset past the end returns an empty page with count still reporting the true total

A refused value names the parameter at fault rather than using detail:

{
  "limit": ["Must be a positive integer. Omit `limit` to use the default of 20; values above 1000 are reduced to 1000."],
  "status_code": 400
}

Two points worth building around:

Only plain digits are accepted. limit=1_0, limit=+10, and non-ASCII digits are refused rather than reinterpreted.

A few non-list responses omit the pagination keys entirely. The cap table endpoints, for example, drop count / next / previous when the company has no cap table on file. Read results rather than branching on count.

Unpublished and Deleted Records

The Partner API never shows, counts, or matches a record that is unpublished or deleted, or anything that reaches the API only through one:

Two consequences worth designing for:

One exception: a company’s own funding figures — the funding object, funding_status, total_funding_usd — and the is_startup classification still count a transaction whose investor is hidden. The company is visible, and the hidden investor is never named.

These rules were applied consistently across every route in September 2026. Before then, some routes still counted or matched hidden records — for example, count on /investors/ could include deleted shareholder groups, and a hidden company’s UUID still returned rows from the batch endpoints.

Incremental Sync

Every capital receiver, capital allocator, fund, person, and service provider carries aggregate_updated_at: the last time the record, or anything shown on it, changed. It includes the record’s own edits, so it is the only field you need to poll to keep a copy of the data current.

To pull what changed since your last sync, filter on it and sort by it together with uuid:

curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/?ordering=aggregate_updated_at,uuid&limit=1000" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"all": [
        {"op": "gte", "field": "aggregate_updated_at", "value": "2026-09-24T00:00:00Z"}
      ]}}'

The same works on /capital-allocators/, /funds/, /people/, and /service-providers/.

Page by keyset, not by offset

The value changes while you page. A record edited mid-pull jumps to the end of the list, every record after it moves up one place, and the next offset page silently skips one. So build each next page from the last row of the previous one: take its aggregate_updated_at (V) and uuid (U), and ask for everything after that pair.

{"filters": {"all": [
  {"op": "gte", "field": "aggregate_updated_at", "value": "V"},
  {"any": [
    {"op": "gt", "field": "aggregate_updated_at", "value": "V"},
    {"op": "gt", "field": "uuid", "value": "U"}
  ]}
]}}

Keep ordering=aggregate_updated_at,uuid and offset at 0, and stop when a page returns fewer rows than limit. The uuid half matters: one change can stamp thousands of records with the same instant, so gt on the timestamp alone would skip the rest of them, and gte alone would return the same page forever. A record edited mid-pull reappears near the end with its new value, so de-duplicate by uuid and keep the newest copy.

Start each new pull a few minutes before the highest value you saw last time. Two changes saved at nearly the same moment can become visible in the opposite order to their timestamps, and a small overlap means you never miss the earlier one.

Missed changes are repaired overnight and reach you on your next pull. A nightly check recomputes every value. A record it repairs is stamped with the time of the repair, not the time of the original change, so it lands inside your next window like any other change. A record whose value has not been computed yet has aggregate_updated_at: null, matches no filter on the field, and gets a value from the same check.

What moves it

A change to any of the following, in addition to an edit of the record itself:

RecordAlso moves when this changes
Capital receiver (company)Its registered entity (name, description, status, locations, founding date, and the other company details), financials, valuations, paid-up capital, employee counts, licences, registration numbers, alternate names, industry codes, contact details, attached files, cap table snapshots and their shareholdings, shareholder groups, secondary share availability, its service providers, transactions in the company, news about the company, and its themes, horizontals, technologies, business models, industries, and keywords
Capital allocatorThe company or person behind the firm, and everything that moves them; its preferred_* preferences; AUM; contact details and files on the firm’s profile; transactions in which the firm bought or sold
FundThe fund’s registered entity and everything that moves it; its preferred_* preferences; performance records; AUM; contact details and files on the fund’s profile
PersonTheir roles at organizations, registration numbers, contact details, news about them, and transactions in which they bought or sold
Service providerIts registered entity and everything that moves it; the companies it provides services to

It can move without a visible change. Some of these changes are to data the API does not show you, such as a cap-table holding or a transaction whose shareholder or investor is hidden (see Unpublished and Deleted Records). The value still moves, so a pull can return a record whose payload is identical to the copy you already hold. Treat a new value as “something may have changed”, not “the payload is different”.

What does not move it. Renaming shared reference data (a country, city, currency, or classification label) does not bump every record that points at it; pointing a record at a different one does, because that is an edit of the record. Parent and subsidiary relationships, shareholder group membership, a fund’s list of managers, a capital allocator’s types, a service provider’s service types, and a funding round’s own details (as opposed to the transactions in it) do not move it either.

Removals are not reported

aggregate_updated_at covers additions and edits only. A record that is deleted or unpublished simply stops appearing in the lists; it is not re-stamped, so a filter on the field cannot tell you it went. There is no feed of removed records yet. To detect removals, periodically compare the uuid values you hold against a full pull.

last_updated_at is deprecated

last_updated_at is unchanged. It is still the later of when the profile and its registered entity (or, for a capital allocator, the company or person behind it) were last edited, and on people it is the person’s own last edit. It does not move when related data such as financials, transactions, or the cap table changes, cannot be filtered, and can be sorted only on capital receivers, where that sort is slow. Use aggregate_updated_at instead. last_updated_at will be removed in a future release, announced in advance on the Changelog.

Field Types and Nulls

Three conventions run through every response and are worth setting up for once rather than discovering per endpoint.

Every numeric value is a JSON number. Amounts, percentages, multiples, and rates are returned unquoted — 925652927.23, not "925652927.23". This applies to every endpoint and every field: there is no longer any field that returns a number as a quoted string.

This changed on 2026-08-14. Amounts, percentages, and multiples were previously returned as quoted strings. If your integration parses them as strings (v.replace(...), parseFloat(v) on a value you assume is text, a schema that types them as string), it will break. See the changelog entry for the full list of affected fields.

Values carry the precision they are stored with, so the number of decimal places varies by field and sometimes between two fields with the same name — 0.1800, 40.0000, and 374.0000000000 are all valid. Read the value, not the digits. Trailing zeros are a fidelity guarantee, not a display instruction; most JSON parsers discard them anyway (JSON.parse("0.1800") gives 0.18), and nothing about how a value should be formatted can be inferred from them.

If you need exact decimal arithmetic — summing money, comparing valuations — parse into a decimal type rather than a float, the same as you would for any other JSON API. Python’s json module accepts parse_float=decimal.Decimal; in JavaScript, parse with a big-decimal library rather than the native number.

The field name tells you the scale of a percentage. For any value expressing a proportion, you never have to guess whether 0.1875 means 18.75% or 0.1875%:

Name shapeScale18.75% is writtenExample fields
Bare name — irrFraction0.1875irr
_pct / _percentage / percentage in the nameFraction × 10018.75irr_pct, percentage_held_absolute, management_fee_percentage, operating_revenue_growth_yoy_pct

Where both forms exist they are siblings covering the same figure: irr_pct is always exactly irr × 100. Filters and ordering keys use the scale of the field they name — irr_min=0.15 means 15%.

The rule covers proportions only. Two other kinds of value have rate-like or ratio-like names and are neither a fraction nor a percentage, so neither has a _pct sibling:

null means unknown, not zero or false. Nullable booleans (is_raising_now, is_open_to_co_investment, the fund classification flags) return null when nothing has been declared — distinct from false. Nullable amounts return null both when the value is unknown and when it is known to net to zero; they are never 0.00. A null never satisfies a numeric comparison, so rows with a null value are excluded from range filters on that field and sort to the end of an ordering on it, in both directions.

Keys and Display Values

Many concepts are returned twice: once as a stable key and once as a human-readable label. Always join, look up, filter, group, and store on the key. Treat the label as presentation only.

Use the keyAlongside the label
type_keytype
profile_type_keyprofile_type
allocation_type_keyallocation_type_name
allocation_subtype_keyallocation_subtype_name
allocation_deal_type_keyallocation_deal_type_name
latest_investment_stage_keylatest_investment_stage_name
deal_type_keydeal_type_name
key (inside themes[], techs[], status, trading_status, audit_opinion, and every other {key, name} object)name

Keys are contractual: "capital_allocator" stays "capital_allocator". Labels are not — their exact spelling can change while the thing they describe stays the same. A relabeling would silently re-partition anything keyed on the display string, with no error and no version signal to tell you it happened.

A few fields are the key half of a pair that has no separate _key suffix, because the field itself already holds the key: investor_type ("legal_entity", "person", …), iso_alpha3, iso_code, code on industries and SIC codes, and date_founded_precision. Join on those directly.

type_key is scoped to the object it appears on — there is no single global vocabulary. On a buyer, seller, shareholder, or LP it names a kind of entity ("capital_allocator", "person", …). On legal_entity.alternate_names[] it names a kind of name ("alt_name_legal_name", "alt_name_fka"). The two never mix, but do not build one shared lookup table across both — read type_key in the context of the object holding it.

type and profile_type will change what they display. Today they present values like "CapitalAllocatorProfile" and "capitalallocatorprofile" — the same concept, spelled two different ways, and neither reads as a label. A future release will replace both with proper labels such as "Capital Allocator".

Neither field is going away, and nor is the display half of any other pair — only the presented string changes. type_key and profile_type_key are unaffected. If you join on the keys, this change is invisible to you. If you join on type or profile_type, it will break you.

Cap Table Types

Every capital receiver has a captable_source.type field that tells you how its shareholding data was collected. Always check this field before interpreting cap table results — the response shape and available data differ between the two types.

TypeHow it worksWhat’s available
managedCap table is computed from individual transaction records on the platform. Each share issuance, transfer, and sale is tracked.Full share counts, investment amounts, deal-stage breakdowns. /investors/ endpoint available.
snapshotCap table is sourced from a periodic shareholder-register filing (e.g. an ACRA annual return). Data reflects a point-in-time view.Share counts and percentage held as of the snapshot date. Investment amounts not available. /investors/ returns HTTP 400.

Use the captable_is_managed filter on /capital-receivers/ to segment your queries:

# Companies with full transaction-level cap table data
curl -X POST https://api.altdmp.io/v3/partners/capital-receivers/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"all": [{"op": "eq", "field": "captable_is_managed", "value": true}]}}'

The same captable_source.type field and response structure applies to /capital-allocators/{uuid}/captable/.

Common Use Cases

GoalHow
Company profile, funding history, cap tableGET /capital-receivers/{uuid}/ → /deals/ → /captable/
Current shareholders for a companyGET /capital-receivers/{uuid}/captable/
Total invested per investor with deal-stage breakdownGET /capital-receivers/{uuid}/investors/ (managed cap table only)
Find VC firms active in SEA at Seed stagePOST /capital-allocators/ filtering preferred_allocation_deal_types_keys, then compare against each firm’s actual_allocation_deal_types
Startups, their investors, and their peopleis_startup on POST /capital-receivers/, has_startup_investments on POST /capital-allocators/, capital_receiver_is_startup on /investors/, and organization_is_startup on /people/ — see Startup filters
Fund performance, LP list, size historyGET /funds/{uuid}/performance/ + /aum/ + /commitments/
Look up a founder or director by nameGET /people/?role_type_key=person_association_founder&search=Henry+Chan
Registration number lookupGET /capital-receivers/?search=<name> — registration numbers are in the legal_entity object on the detail response
Build filter dropdowns in a UIGET /reference-data/?type=enums

Data Model

Last updated: 19 August 2026

Before working with any endpoint, it helps to understand two concepts that run through the entire API: the Legal Entity and the Profile. Most of the UUIDs you encounter are one or the other, and knowing which is which tells you what a given ID identifies and which endpoint it drives.

A Legal Entity is the underlying registered organization — a company as it exists in a corporate registry, identified by its registration number (e.g. a Singapore UEN). It holds the identity and registration facts: legal name, domicile, headquarters, trading status, year founded, and registration numbers.

A Profile is a role-specific view onto an entity. The API exposes several profile types, each with its own endpoint and its own fields:

Profile TypeEndpointRepresents
Capital Receiver/capital-receivers/{uuid}/The entity in its role as a company that has raised funding
Capital Allocator/capital-allocators/{uuid}/The entity in its role as an investor deploying capital
Fund/funds/{uuid}/An individual fund vehicle managed by a capital allocator
Service Provider/service-providers/{uuid}/The entity in its role as an auditor or professional-services firm
Person/people/{uuid}/An individual — a founder, director, or other associated person

The key relationship: a single Legal Entity can back more than one profile. The same registered company may appear as a Capital Receiver (it raised money) and as a Capital Allocator (it also invests) — two distinct profiles, two distinct profile UUIDs, one shared underlying Legal Entity. This is why registration and identity data lives on the nested legal_entity object rather than being duplicated on each profile.

Not every profile is backed by a Legal Entity. A Capital Allocator can instead be backed by a Person (an angel investor, for example). Profiles carry a profile_type field of "legalentity" or "person" to indicate which. Fields that resolve through the legal entity — geography, cap table, financials — return null for person-backed profiles. See Capital Allocators.

How Many Profiles Per Root Record

A profile always hangs off a root record — a Legal Entity, or for capital allocators a Person. The root record is the identity; the profile is a role layered on top of it.

The relationship runs in two directions, and only one of them is one-to-one:

Profile typeRoot recordHow many per root
Capital ReceiverLegal EntityExactly one
FundLegal EntityExactly one
Capital AllocatorLegal Entity or PersonOne per preferred allocation type — more than one is normal
Service ProviderLegal EntityNot limited to one

So a single company can be a Capital Receiver, a Fund, a Service Provider, and several Capital Allocators simultaneously. Each of those profiles has its own UUID, and none of them is the company’s identity — the root UUID is.

Do not assume one capital allocator profile per firm. A firm that allocates both equity and debt has a separate allocator profile, with a separate UUID, for each. Any figure you total per firm — capital deployed, number of investments, dry powder — must be collapsed on the root UUID first, or a multi-strategy firm is counted once per profile. The same applies to service providers, where there is no limit at all.

Profile detail responses embed the shared identity data under a legal_entity object:

{
  "uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
  "legal_entity": {
    "uuid": "fee788ac-cffe-46f0-9bb5-fedb62992bd8",
    "display_name": "Company Name",
    "domicile_country": {"iso_alpha3": "SGP", "name": "Singapore"},
    "trading_status": {"key": "operating", "name": "Operating"},
    "year_founded": 2020,
    "registration_numbers": [
      {
        "reg_number": "202012345A",
        "authority_type": "UEN",
        "authority_name": "Accounting and Corporate Regulatory Authority"
      }
    ]
  }
}

Here the top-level uuid is the profile UUID (a capital receiver, in this case) and legal_entity.uuid is the Legal Entity UUID. They are different IDs pointing at different things: the first drives the profile’s own endpoints, the second identifies the registered company shared across any other profiles built on it.

Understanding the UUIDs

The same underlying entity surfaces different UUIDs depending on where you are in the API. This table maps each one to what it identifies and how to use it:

UUIDWhere it appearsIdentifiesUse it with
Profile uuidTop level of every list and detail responseThe profile (capital receiver, allocator, fund, service provider, or person)The matching profile endpoint, e.g. /capital-receivers/{uuid}/
legal_entity.uuidNested inside the legal_entity object on detail responsesThe underlying registered companyReference key — links profiles that share the same legal entity
legal_entity_uuidFlat field on investment rows — the /investments/ sub-resource on capital allocators, funds, and peopleThe underlying registered company (same value as legal_entity.uuid)Reference key to correlate rows belonging to the same company
capital_receiver_uuidFlat field on investment rows and on every batch response row (deals, financials, news, deal share types)The capital receiver profile/capital-receivers/{uuid}/
uuid beside a type_keyBuyer / seller / shareholder / organization objectsWhatever type_key says — see aboveThe endpoint for that key

Telling Which Namespace a UUID Belongs To

Rows that reference some entity without knowing in advance what kind — a deal’s buyer and seller, a cap-table shareholder, a role’s organization, an LP buyer on a commitment — carry a type_key (or profile_type_key) alongside the uuid. That key names the namespace the uuid lives in, and so which endpoint to call:

Key valueuuid identifiesUse it with
capital_allocatorA capital allocator profile/capital-allocators/{uuid}/
capital_receiverA capital receiver profile/capital-receivers/{uuid}/
fundA fund profile/funds/{uuid}/
service_providerA service provider profile/service-providers/{uuid}/
personA person/people/{uuid}/
legal_entityThe underlying registered companyCorrelation key only — there is no legal-entity endpoint
shareholder_groupA grouping of shareholdersCorrelation key only
other, alt, nullNothing — an aggregate or name-only rowNothing to fetch

The uuid is the most specific record available, not always the legal entity. The same firm appears as capital_allocator where it has an allocator profile and as legal_entity where it doesn’t. Branch on the key before joining; a join that assumes one namespace silently drops every row in the others.

Each of these fields is paired with a display-only twin (type, profile_type) whose presented string is not stable. Join on the key. See Keys and Display Values.

Which UUID do I pass to an endpoint? Always the profile UUID. Endpoints like /capital-receivers/{uuid}/, /capital-allocators/{uuid}/, and /funds/{uuid}/ expect the profile UUID for their type — for example capital_receiver_uuid, not legal_entity_uuid. The legal_entity_uuid is a correlation key: use it to recognize when two rows or two profiles refer to the same registered company. There is no /legal-entities/{uuid}/ endpoint.

Counterparty UUIDs Are Not Stable Identifiers

The uuid on a buyer, seller, shareholder, organization, or investor row is resolved per row, at the time you fetch it, to the most specific profile that entity currently has. It is a handle for fetching detail, not an identity. Two behaviors follow from that, and either one will fork an entity-resolution table keyed on it.

The same company resolves differently depending on the role it plays in the row. Where a company is the outside investor, it resolves to its capital allocator profile. Where the same company is the one being invested in, it resolves to its capital receiver profile. One company, two rows, two different UUIDs and two different type_key values — with nothing tying them together in the response.

The UUID changes when a profile is added later. A company with no allocator profile is reported as {"uuid": "<legal entity uuid>", "type_key": "legal_entity"}. Once an allocator profile exists for that company, the same historical transaction starts returning the allocator profile’s UUID with type_key: "capital_allocator". Nothing about the transaction changed, the record was not corrected, and no field in the response indicates the identifier moved.

So do not use these UUIDs as an entity-resolution key. Key your own company or party table on the root UUID — legal_entity.uuid for an organization, the person UUID for an individual — and treat the counterparty uuid as a per-row pointer you re-read on every sync.

Where To Get A Root UUID

ResponseRoot UUID
Profile detail — /capital-receivers/{uuid}/, /capital-allocators/{uuid}/, /funds/{uuid}/, /service-providers/{uuid}/Nested legal_entity.uuid
/investments/ on capital allocators, funds, and peopleFlat legal_entity_uuid
/investors/ — the top-level listThe row uuid already is the root: investor_type is legal_entity, person, or shareholder_group, never a profile
Snapshot cap tableshareholder.uuid is the legal entity or person directly — type_key confirms which
/capital-receivers/{uuid}/investors/, managed cap table, deal buyer / seller, commitment buyer, role organizationNone — a profile-resolved uuid only

For that last row, resolve the root by fetching the profile’s own detail response and reading legal_entity.uuid. That is one request per profile: legal_entity_uuid is a filter field on /service-providers/ alone, so a set of capital receiver, allocator, or fund UUIDs cannot be crosswalked to their root records in bulk. Plan the resolution step into your ingestion rather than assuming a batch lookup exists.

Some rows have no root record at all. Pooled and unnamed holders — an ESOP pool, an “Other Shareholders” aggregate, a shareholder_group — are not backed by a legal entity or a person, so there is no root UUID to resolve. type_key is other, alt, shareholder_group, or null on these; treat them as unresolvable rather than minting a company for them.

Why Two IDs on the Same Row

Investment rows — the /investments/ sub-resource on capital allocators, funds, and people — carry both capital_receiver_uuid and legal_entity_uuid:

{
  "capital_receiver_uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
  "legal_entity_uuid": "fee788ac-cffe-46f0-9bb5-fedb62992bd8",
  "capital_receiver_name": "Shopback"
}

Use capital_receiver_uuid to navigate to the company’s profile. Use legal_entity_uuid to group or reconcile rows at the registered-company level — for example, to see that two holdings belong to the same legal entity even when they surfaced through different profiles.

This pairing is specific to investment rows. Cap-table rows carry neither: a managed or snapshot cap-table row identifies its holder through the nested shareholder object and nothing else. See Counterparty UUIDs Are Not Stable Identifiers.

Authentication

Last updated: 5 October 2026

Every request to the Partner API must include a Bearer token in the Authorization header.

Obtaining a Token

Exchange your API key for a short-lived access token:

curl -X POST https://api.altdmp.io/v3/token/issue/ \
  -H "Content-Type: application/json" \
  -d '{"api_key": "YOUR_API_KEY"}'
import requests

resp = requests.post(
    "https://api.altdmp.io/v3/token/issue/",
    json={"api_key": "YOUR_API_KEY"},
)
token = resp.json()["access_token"]

Response:

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ij...",
  "token_duration": 1440,
  "expires_at": "2024-11-21T06:29:30.396717Z"
}

The token is valid for 24 hours (token_duration is in minutes).

Contact support@alternatives.pe to obtain your API key.

Using the Token

Include the token in the Authorization header on every request:

curl https://api.altdmp.io/v3/partners/capital-receivers/ \
  -H "Authorization: Bearer YOUR_TOKEN"
headers = {"Authorization": f"Bearer {token}"}
resp = requests.get(
    "https://api.altdmp.io/v3/partners/capital-receivers/",
    headers=headers,
)

Subscription and Access Control

Authentication alone is not sufficient. Each endpoint is also protected by entitlements that are enabled according to your subscription. Your organization must hold the appropriate entitlements for the resources you need to access.

Entitlements are provisioned automatically when your subscription is activated. Different subscription tiers unlock access to different endpoints and data types. See Plans & Access for the full per-endpoint breakdown by plan. Contact support@alternatives.pe to review or upgrade your subscription.

A 403 Forbidden response means your organization’s subscription does not include access to that endpoint. Contact support to adjust your subscription.

Rate Limits

The Partner API is rate limited to protect service quality for all clients. Requests are limited to 320 requests per rolling 1-minute window. The window is rolling rather than fixed: at any moment, the count is the number of requests made in the preceding 60 seconds, so capacity frees up continuously rather than resetting on a clock boundary.

What counts as a request

Every HTTP request that reaches the API counts, whatever it returns. A 400, 401, 403, or 404 costs the same as a 200, so a loop retrying a malformed request burns quota as fast as one doing useful work.

What does not affect the count:

The three limits, and which one you hit

Three separate ceilings apply to the same traffic, and the dimension field in the 429 body names the one you reached:

dimensionCounted againstWhen it binds
userThe credentials the request authenticated withThe normal case — this is the published 320/minute rate
customerYour organization, across every set of credentials it holdsSeveral users, services, or workers running in parallel under one organization
ipThe network address the request came fromTraffic leaving through one shared egress address, such as a NAT gateway or a proxy shared with other systems

The organization and network ceilings are set well above the per-credential rate, so a single well-behaved integration will not meet them. They exist to bound aggregate load, which has two practical consequences: issuing more credentials does not raise your organization’s total throughput, and parallel workers draw on one shared allowance rather than each getting their own.

The checks run in order — network, then credentials, then organization — and stop at the first refusal. A request refused on one limit may already have counted against a limit it passed on the way, so the figures in the 429 headers describe only the limit that refused it.

The response never reveals which address, user, or organization was counted. dimension tells you the kind of limit, and that is by design.

When you exceed a limit

The API responds 429 Too Many Requests with a JSON body naming the limit that was hit:

{
  "status_code": 429,
  "detail": "Too many requests. Please slow down and retry.",
  "scope": "partner.standard",
  "dimension": "user",
  "retry_after_seconds": 60
}
FieldMeaning
scopeNames the class of request the limit applies to — partner.standard for Partner API traffic
dimensionWhat the limit was counted against — user for your credentials, customer for your organization, or ip for your network address
retry_after_secondsSeconds until the window frees capacity. Carries the same value as the Retry-After header. May be null if it cannot be determined

Four headers accompany it:

HeaderMeaning
X-RateLimit-LimitThe limit that was hit
X-RateLimit-RemainingRequests left in the window that refused the request
Retry-AfterSeconds to wait before retrying
X-RateLimit-ResetAlso seconds to wait, not a timestamp — it carries the same value as Retry-After

Retry-After and X-RateLimit-Reset appear only when a retry time can be determined; the first two are always present on a 429.

X-RateLimit-Reset is a duration, not a Unix timestamp. The name usually means the latter, and treating 60 as an epoch second produces a retry time in 1970 — which typically shows up as a client that retries immediately and stays limited.

Retry-After is a real wait, not a constant. It is the time until the oldest request in the rolling window ages out, so it shrinks as the window drains and is usually shorter than the full 60 seconds. It is never 0 and never longer than the window. Waiting the value you are given is both sufficient and the fastest correct retry.

These four headers appear on 429 responses only. A successful response carries no quota information, so there is no way to read how much capacity is left before you run out. Pace your own sending rather than planning to steer by remaining-quota headers.

Handling a 429

Treat 429 as an expected response on a busy integration, not an error condition. The minimum correct handling is to wait for Retry-After and retry the same request:

import random, time
import requests

def get(url, headers, params=None, max_attempts=5):
    for attempt in range(max_attempts):
        resp = requests.get(url, headers=headers, params=params)
        if resp.status_code != 429:
            resp.raise_for_status()
            return resp.json()
        wait = float(resp.headers.get("Retry-After", 60))
        # Jitter keeps parallel workers from retrying in lockstep.
        time.sleep(wait + random.uniform(0, 1))
    raise RuntimeError(f"Still rate limited after {max_attempts} attempts: {url}")

Three failure modes are worth designing out:

If retries keep failing after honoring Retry-After, increase the delay between attempts (exponential backoff with a ceiling) rather than retrying at a fixed interval.

A run with no 429s is not proof you are under the limit. Enforcement can be more permissive than the published rate during infrastructure incidents — limits fail open rather than taking the API down with them. Pace your integration to the published rate; do not calibrate it by pushing until you see a 429.

Staying within the limits

In rough order of how much they buy you:

Error Responses

Errors are always JSON. Most carry two keys: status_code, repeating the HTTP status of the response, and detail, a human-readable message.

{
  "status_code": 404,
  "detail": "The requested resource was not found."
}
StatusdetailMeaning
400 Bad Requestvaries — see 400 response bodiesMalformed request — e.g. a filter field name the endpoint does not support, an unsupported ordering field, any ordering on an endpoint whose order is fixed, a POST body containing keys other than filters (pass limit, offset, and ordering as query parameters), a filters value passed on the query string instead of in the body, an unsupported limit or offset value, or a missing, oversized, or malformed UUID in filter on a batch endpoint
401 UnauthorizedAuthentication credentials are invalid or missing.Missing, malformed, or expired Bearer token. The message is fixed, so the body does not distinguish a missing token from an expired one — re-issue the token and retry
403 ForbiddenYou do not have permission to perform this action.Token is valid but your organization’s subscription does not include access to that endpoint. See Subscription and Access Control
404 Not FoundThe requested resource was not found.No record matches the UUID in the path, or the record has since been deleted — deleted records are filtered out, so a UUID that resolved earlier can start returning 404
404 Not FoundThe requested resource was not found on this server.The URL matched no route at all. The wording is deliberately different from the message above, so a mistyped path is distinguishable from a missing record
429 Too Many RequestsToo many requests. Please slow down and retry.Rate limit exceeded. The body carries three extra keys — scope, dimension, and retry_after_seconds — naming which limit was hit. Wait for the period given in the Retry-After header, then retry. See Rate Limits
500 Internal Server ErrorAn unexpected error occurred. Please contact support.Unhandled server error. The cause is logged on our side and never returned in the response

Every endpoint can return 401, 403, and 500; endpoints that take a UUID in the path can also return 404; endpoints that accept a body or filter parameters can also return 400. These are declared per operation in the OpenAPI schema and the Postman collection.

400 response bodies

Validation errors raised by the API put the message in a list:

{
  "status_code": 400,
  "detail": [
    "Invalid ordering field 'founded'. Valid ordering fields are: created_at, display_name, term_years, updated_at, vintage_year."
  ]
}

A few endpoint-specific checks return the message as a plain string, without status_code:

{
  "detail": "Investor summary is only available for capital receivers with a managed cap table. Use /partners/capital-receivers/{uuid}/captable/ for snapshot-based cap tables."
}

Some checks key the message by the parameter at fault instead of using detail. Passing filters on the query string rather than in the request body:

{
  "filters": "`filters` must be sent in the JSON request body on this endpoint, not as a query-string parameter.",
  "status_code": 400
}

And an unsupported limit or offset — see Pagination for the accepted range:

{
  "offset": ["Must be zero or a positive integer. Omit `offset` to start from the beginning of the collection."],
  "status_code": 400
}

When handling errors, take the status from the HTTP response rather than from the body — status_code is present on most error bodies but not all of them. Read detail and accept either a string or a list of strings, and treat its absence as a valid body: a 400 may instead carry the message under the name of the offending parameter.

Very long URLs return a non-JSON 400

The request line — the method, the full path, and the entire query string — is limited to 8190 bytes. A request over that is rejected before the API runs, so it returns 400 Bad Request as HTML, with no detail and no status_code. If you parse every error body as JSON, guard for this.

Long ?search= values and long comma-separated country or UUID lists are what push a URL over the limit. Move bulk conditions into a POST filters body, or split them across pages.

Plans and Access

Last updated: 25 September 2026

Which Partner API endpoints you can call depends on your subscription plan. This page lists every endpoint and shows whether it is included with Atlas or Allocate.

Custom plans are not covered here. The tables below describe our two standard plans. If your organization is on a plan we’ve tailored to your requirements, your endpoint access is defined by your agreement and may differ from what’s shown here. Contact support@alternatives.pe if you’re unsure which endpoints your subscription includes.

How Access Works

Two things must be true for a request to succeed:

  1. Partners API access. Programmatic access is a separate add-on to your subscription. When it’s enabled, your organization can authenticate and call the API.
  2. Endpoint entitlement. Each endpoint is individually gated by your plan. If your plan doesn’t include an endpoint, calling it returns 403 Forbidden even with a valid token — see Authentication → Subscription and Access Control.

What Differs Between Atlas and Allocate

Most endpoints are available on both plans. The differences are:

AreaAtlasAllocate
Funds (all fund endpoints)✓✗
Capital Allocator → LP commitments✓✗
Capital Receivers → list / search✗✓

On Atlas, you can retrieve a capital receiver directly by UUID (and all of its sub-resources), but you cannot list or search the capital-receivers collection. On Allocate, the entire Funds resource and the capital-allocator LP commitments endpoint are not included.

Full Endpoint Matrix

✓ = included  ·  ✗ = not included (returns 403 Forbidden)

Capital Receivers

MethodEndpointAtlasAllocate
GET / POST/v3/partners/capital-receivers/✗✓
GET/v3/partners/capital-receivers/{uuid}/✓✓
GET/v3/partners/capital-receivers/{uuid}/financials/✓✓
POST/v3/partners/capital-receivers/financials/✓✓
GET/v3/partners/capital-receivers/{uuid}/captable/✓✓
GET/v3/partners/capital-receivers/{uuid}/investors/✓✓
GET / POST/v3/partners/capital-receivers/{uuid}/deals/✓✓
POST/v3/partners/capital-receivers/deals/✓✓
POST/v3/partners/capital-receivers/deal-share-types/✓✓
GET/v3/partners/capital-receivers/{uuid}/news/✓✓

Capital Allocators

MethodEndpointAtlasAllocate
GET / POST/v3/partners/capital-allocators/✓✓
GET/v3/partners/capital-allocators/{uuid}/✓✓
GET/v3/partners/capital-allocators/{uuid}/investments/✓✓
GET/v3/partners/capital-allocators/{uuid}/aum/✓✓
GET/v3/partners/capital-allocators/{uuid}/commitments/✓✗
GET/v3/partners/capital-allocators/{uuid}/financials/✓✓
GET/v3/partners/capital-allocators/{uuid}/captable/✓✓
GET/v3/partners/capital-allocators/{uuid}/funds/✓✓
GET/v3/partners/capital-allocators/{uuid}/news/✓✓

Funds

MethodEndpointAtlasAllocate
GET / POST/v3/partners/funds/✓✗
GET/v3/partners/funds/{uuid}/✓✗
GET/v3/partners/funds/{uuid}/performance/✓✗
GET/v3/partners/funds/{uuid}/aum/✓✗
GET/v3/partners/funds/{uuid}/commitments/✓✗
GET/v3/partners/funds/{uuid}/investments/✓✗
GET/v3/partners/funds/{uuid}/news/✓✗

Investors

MethodEndpointAtlasAllocate
GET / POST/v3/partners/investors/✓✓

People

MethodEndpointAtlasAllocate
GET / POST/v3/partners/people/✓✓
GET/v3/partners/people/{uuid}/✓✓
GET/v3/partners/people/{uuid}/roles/✓✓
POST/v3/partners/people/roles/✓✓
GET/v3/partners/people/{uuid}/investments/✓✓
GET/v3/partners/people/{uuid}/news/✓✓

Service Providers

MethodEndpointAtlasAllocate
GET / POST/v3/partners/service-providers/✓✓
GET/v3/partners/service-providers/{uuid}/✓✓

Reference Data

MethodEndpointAtlasAllocate
GET/v3/partners/reference-data/✓✓

Handling 403 Forbidden

A 403 on an endpoint listed above as ✗ for your plan is expected — it means your subscription doesn’t include that endpoint. To add access, contact support@alternatives.pe to upgrade your plan or arrange a custom plan.

Migrating from v2 to v3

Last updated: 9 October 2026

The v3 Partner API is a complete redesign of the legacy VentureCap v2 API. It is not a wire-compatible upgrade — it is a new contract. This guide covers everything you need to update your integration.

What Changed at a Glance

Areav2v3
Base URLhttps://api.alternatives.pe/api/v2/https://api.altdmp.io/v3/partners/
AuthenticationOAuth2 client credentials → /api/v2/oauth/tokenAPI key → POST /v3/token/issue/ (via PropelAuth)
IdentifiersInteger IDs ("id": 1234)UUIDs ("uuid": "c16a0ffd-…")
Pagination envelope{"data": {"data": [...], "total_records": N, ...}}{"count": N, "next": "…", "previous": "…", "results": [...]}
Max page size1001000 (default 20)
Orderingorder_by + order_directionSingle ordering param with - prefix for descending
FilteringQuery-string onlyQuery-string and JSON body (POST)
Companies/api/v2/companies//v3/partners/capital-receivers/
Investors/api/v2/investors//v3/partners/capital-allocators/ + /v3/partners/investors/
Funds/api/v2/funds//v3/partners/funds/
People/api/v2/directors/, /api/v2/founders//v3/partners/people/ (unified)
Auditors/api/v2/auditors//v3/partners/service-providers/?service_type=service_provider_type_audit
ClassificationSector / theme drivenFive dimensions: themes, techs, business_models, industries, horizontals
Money fieldsMixed currency for fund performance fields; company financials already USDUSD-normalized, _usd suffix (explicit in field names for both)

Breaking Semantic Changes

These are not syntactic renames — they change meaning and require deliberate handling:

Authentication

v2

curl -X POST https://api.alternatives.pe/api/v2/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=YOUR_ID&client_secret=YOUR_SECRET"

Returns {"token": "..."}. Use as a Bearer token.

v3

v3 credentials are issued through PropelAuth. Contact support@alternatives.pe to provision an API key, then exchange it for a short-lived bearer token:

curl -X POST https://api.altdmp.io/v3/token/issue/ \
  -H "Content-Type: application/json" \
  -d '{"api_key": "YOUR_API_KEY"}'
import requests

token = requests.post(
    "https://api.altdmp.io/v3/token/issue/",
    json={"api_key": "YOUR_API_KEY"},
).json()["access_token"]

headers = {"Authorization": f"Bearer {token}"}

The token is valid for 24 hours. Request new credentials before starting your migration.

Identifiers

v2 used integer IDs throughout. v3 uses UUIDs everywhere — no internal integer IDs are exposed.

# v2
GET /api/v2/companies/4821/

# v3
GET /v3/partners/capital-receivers/c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67/
# v3 — store uuid from list results for later detail calls
results = requests.get(f"{BASE}/capital-receivers/?search=shopback", headers=headers).json()
uuid = results["results"][0]["uuid"]
detail = requests.get(f"{BASE}/capital-receivers/{uuid}/", headers=headers).json()

There is no mapping endpoint between v2 integer IDs and v3 UUIDs. Re-query by name or registration number to find the corresponding v3 UUID.

Pagination

v2 used a custom doubly-nested envelope:

{
  "data": {
    "total_records": 1000,
    "no_of_pages": 10,
    "limit": 100,
    "offset": 0,
    "data": [{ "id": 1234, "name": "Example Co" }]
  }
}

v3 uses a standard limit/offset envelope:

{
  "count": 1000,
  "next": "https://api.altdmp.io/v3/partners/capital-receivers/?limit=100&offset=100",
  "previous": null,
  "results": [
    {
      "uuid": "a1b2c3d4-0000-0000-0000-000000000001",
      "legal_entity": {"uuid": "b2c3d4e5-0000-0000-0000-000000000002", "display_name": "Example Co"}
    }
  ]
}
# v2
GET /api/v2/companies/?page=2&page_size=20

# v3
GET /v3/partners/capital-receivers/?limit=20&offset=20

Ordering

# v2
GET /api/v2/companies/?order_by=name&order_direction=desc

# v3 (prefix field with - for descending)
GET /v3/partners/capital-receivers/?ordering=display_name
GET /v3/partners/capital-receivers/?ordering=-aggregate_updated_at

Each endpoint accepts a fixed set of sortable fields. An unsupported ordering value returns 400 Bad Request with the list of valid fields on every list and sub-resource route except four: the people list, /investors/, and both deals endpoints, where it is still ignored — you get 200 in the default order, with nothing saying the sort was dropped. Check the spelling rather than relying on an error there. See Filtering → Sorting for how nulls and ties sort.

Filtering Changes

v2 supported query-string filters only. v3 adds a JSON body filter via POST for complex conditions. Simple filters (search, ordering, registration_number) are still supported as query parameters.

The POST body accepts only the filters object. search, ordering, limit, and offset are always query parameters — sending them in the body returns 400 Bad Request.

There is no ?filters= query parameter. Coming from v2 it is a natural guess, and it returns 400 Bad Request. The advanced filter tree is a body parameter only. This is also the practical reason to move bulk conditions into the body: a long filter tree in the query string can push the URL past the 8190-byte request-line limit, which fails before the API runs and returns HTML rather than JSON.

# v2
GET /api/v2/companies?countries=SGP,THA&sectors=22,44&themes=35

# v3 — advanced filter via POST body (pagination and sorting go on the query string)
curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/?search=platform&ordering=-latest_valuation_usd&limit=100" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq",  "field": "headquarters_country_iso_alpha3", "value": "SGP"},
        {"op": "in",  "field": "themes_keys", "value": ["themes_payments", "themes_climate_green_clean"]},
        {"op": "gte", "field": "latest_valuation_usd", "value": 10000000}
      ]
    }
  }'

Keys vs. display values

v2 mostly returned a single string per concept. v3 returns two: a stable key and a human-readable label. Build every join, lookup, filter, and grouping on the key — type_key, profile_type_key, allocation_type_key, allocation_deal_type_key, investor_type, and the key inside any {key, name} object. The label half (type, profile_type, *_name) is presentation only, and its spelling can change without the underlying thing changing. See Keys and Display Values.

If you are porting v2 code that keyed off display strings such as sector or theme names, this is the change to make while you are in there.

Filter Operators

CategoryOperators
Equalityeq, ne
Set membershipin, nin
Stringcontains, ncontains, startswith, endswith
Numeric / dategt, gte, lt, lte, range
Blankisnull, notnull — “no value recorded”, which on a text field includes a blank value
Logicalall (AND), any (OR), not (NOT)

range takes a list of exactly two values, low then high — {"op": "range", "field": "date_founded", "value": ["2015-01-01", "2020-12-31"]}. Any other number of values returns 400 Bad Request.

filters must be a JSON object. A list or a scalar — {"filters": ["any"]} — returns 400 Bad Request rather than applying no filter. Omitting filters, or sending null or {}, is the way to ask for an unfiltered list.

Every filter body must start with a logical operator. filters takes all, any, or not at its top level — a bare condition such as {"filters": {"op": "eq", "field": "…", "value": …}} returns 400 Bad Request. Wrap even a single condition:

{"filters": {"all": [{"op": "eq", "field": "headquarters_country_iso_alpha3", "value": "SGP"}]}}

all and any take an array of conditions. not takes a single condition object:

{"filters": {"not": {"op": "eq", "field": "headquarters_country_iso_alpha3", "value": "SGP"}}}

An array under not is also accepted and is negated as a whole — equivalent to wrapping it in all and negating that.

not needs something to negate. An empty negation — {"not": []}, {"not": {}}, or {"not": {"all": []}} — returns 400 Bad Request. There is no useful reading of “exclude nothing in particular”, and the alternative is a body that asks to exclude rows and returns every one of them. An empty all or any is different: it means you supplied no conditions, so it stays a permissive no-op and returns the unfiltered list.

Conditions nest — an entry inside all or any may itself be another all / any / not block.

A malformed condition anywhere in the body returns 400 Bad Request with a message describing the problem — a non-object where a condition was expected, or a range without exactly two values. This holds in both the nested form ({"all": ["oops"]}) and the flat, field-keyed form ({"name": {"all": ["oops"]}}).

Derived filter fields on capital receivers

POST /v3/partners/capital-receivers/ filters on the fields returned in the response and also on a set of derived ones: financials_latest_financial_year_end, latest_investment_stage_name, latest_investment_stage_key, latest_operating_revenue_usd, operating_revenue_growth_yoy_pct, latest_valuation_usd, and total_funding_usd. Each is computed only when it appears in the filter body or in ordering, so plain list and search requests are unaffected. Filter, ordering, and response all use the same field names.

Geography filters on capital allocators and funds

POST /v3/partners/capital-allocators/ and POST /v3/partners/funds/ both support geography filters via domicile_country_name, domicile_country_iso_alpha3, headquarters_country_name, and headquarters_country_iso_alpha3. For capital allocators, geography comes from the linked legal entity — allocators backed by a Person rather than a legal entity return null and are excluded when a country filter is applied.

Filter migration — capital receivers

v2 query paramv3 equivalent
querysearch query param
countriesPOST headquarters_country_iso_alpha3 or domicile_country_iso_alpha3
sectorsDeprecated — industries_codes is available but uses a different classification. A code matches itself and every code beneath it; see Industry Codes
themesPOST themes_names or themes_keys (taxonomy overhauled)
investment_stagePOST latest_investment_stage_name or latest_investment_stage_key
valuation_min / valuation_maxPOST latest_valuation_usd with gte / lte
revenue_min / revenue_maxPOST latest_operating_revenue_usd with gte / lte
revenue_growth_min / revenue_growth_maxPOST operating_revenue_growth_yoy_pct with gte / lte
total_funding_min / total_funding_maxtotal_funding_min / total_funding_max query params, or POST total_funding_usd with gte / lte
statusPOST trading_status_name
female_founderPOST is_female_founder
iso_codePOST domicile_country_iso_alpha3
order_by + order_directionordering (single param, - prefix for desc)

Classification Framework

v3 replaces v2’s sector/theme model with a five-dimension classification. Do not treat any v2 dimension as a direct equivalent:

DimensionDescription
themesThematic groupings (e.g., FinTech, AI, ClimateTech). Taxonomy was overhauled — not 1:1 with v2 themes.
techsUnderlying technology focus.
business_modelsHow the business makes money (Marketplace, SaaS, etc.).
industriesDifferent classification system from v2 sectors. Uses string codes ("08", "10") instead of integer IDs.
horizontalsCross-cutting capability (Customer Stack, Infra Stack, etc.).

v2:

{ "sectors": [{"id": 22, "name": "Financial Services"}] }

v3:

{
  "industries": [{"code": "10", "description": "Financial and Insurance Activities"}],
  "themes":     [{"key": "themes_digital_neo_banking", "name": "Digital / Neo Banking"}],
  "techs":      [{"key": "techs_fintech", "name": "FinTech"}]
}

USD Normalization

v3 normalizes all monetary fields to USD using a deterministic FX policy. Fields are suffixed _usd (e.g., total_funding_usd, latest_valuation_usd, committed_capital_usd). Stop applying FX rates client-side once you migrate.

v3 wraps registration and identity data under a legal_entity object:

{
  "uuid": "...",
  "legal_entity": {
    "uuid": "...",
    "display_name": "Company Name",
    "domicile_country": {"iso_alpha3": "SGP", "name": "Singapore"},
    "headquarters": {
      "country_name": "Singapore",
      "country_iso_alpha3": "SGP",
      "state_name": "Central Singapore",
      "city_name": "Singapore"
    },
    "trading_status": {"key": "operating", "name": "Operating"},
    "year_founded": 2020,
    "registration_numbers": [
      {
        "reg_number": "202012345A",
        "authority_type": "UEN",
        "authority_name": "Accounting and Corporate Regulatory Authority"
      }
    ]
  },
  "themes": [{"key": "themes_payments", "name": "Payments"}],
  "industries": [{"code": "10", "description": "Financial and Insurance Activities"}]
}

v2 flat fields mapped to v3 nested equivalents:

# v2 flat field
"uen": "201411189G"

# v3 nested
"legal_entity": {
  "registration_numbers": [
    {
      "reg_number": "201411189G",
      "authority_name": "Accounting and Corporate Regulatory Authority",
      "authority_type": "UEN"
    }
  ]
}

Endpoint Mapping

Companies → Capital Receivers

v2 Endpointv3 EquivalentNotes
GET /api/v2/companies/GET /v3/partners/capital-receivers/List / search
—POST /v3/partners/capital-receivers/Advanced JSON filtering
GET /api/v2/companies/{id}/GET /v3/partners/capital-receivers/{uuid}/Use UUID, not integer ID
GET /api/v2/companies/{uen}/uenGET /v3/partners/capital-receivers/?registration_number={uen}UEN lookup via query param
GET /api/v2/companies/{id}/financialsMultiple v3 endpointsSee Company Financials below
GET /api/v2/companies/{id}/shareholders/GET /v3/partners/capital-receivers/{uuid}/investors/—
GET /api/v2/companies/{id}/deals/GET /v3/partners/capital-receivers/{uuid}/deals/—

Company Financials

The v2 monolithic /companies/{id}/financials splits across multiple v3 endpoints:

v2 sourcev3 equivalent
Basic funding blockGET /v3/partners/capital-receivers/{uuid}/ (detail — funding object)
Funding rounds (fundings[])GET /v3/partners/capital-receivers/{uuid}/deals/; field mapping in Company Financials: Funding Rounds
ShareholdersGET /v3/partners/capital-receivers/{uuid}/investors/
Cap tableGET /v3/partners/capital-receivers/{uuid}/captable/
Multi-year financialsGET /v3/partners/capital-receivers/{uuid}/ (financials[] array on detail)

Capital Providers → Capital Allocators

v2 Endpointv3 Equivalent
GET /api/v2/investors/GET /v3/partners/capital-allocators/
GET /api/v2/investors/{id}/GET /v3/partners/capital-allocators/{uuid}/
GET /api/v2/investors/{id}/portfolio/GET /v3/partners/capital-allocators/{uuid}/investments/
GET /api/v2/investors/{id}/commitments/GET /v3/partners/capital-allocators/{uuid}/commitments/

Funds

v2 Endpointv3 Equivalent
GET /api/v2/funds/GET /v3/partners/funds/
GET /api/v2/funds/{id}/GET /v3/partners/funds/{uuid}/
GET /api/v2/funds/{id}/performance/GET /v3/partners/funds/{uuid}/performance/
GET /api/v2/funds/{id}/lps/GET /v3/partners/funds/{uuid}/commitments/
GET /api/v2/fund-performances/?fund_id={id}GET /v3/partners/funds/{uuid}/performance/
GET /api/v2/commitment-deals/?fund_id={id}GET /v3/partners/funds/{uuid}/commitments/
GET /api/v2/commitment-deals/?limited_partner_id={id}GET /v3/partners/capital-allocators/{uuid}/commitments/

People

v2 had separate /directors/ and /founders/ endpoints. v3 unifies them under /people/ with a role_type_key filter:

v2 Endpointv3 Equivalent
GET /api/v2/directors/GET /v3/partners/people/?role_type_key=person_association_director
GET /api/v2/founders/GET /v3/partners/people/?role_type_key=person_association_founder
GET /api/v2/directors/{id}/GET /v3/partners/people/{uuid}/
GET /api/v2/founders/{id}/GET /v3/partners/people/{uuid}/
GET /api/v2/people/?first_name=XGET /v3/partners/people/?search=X

Auditors → Service Providers

v2 Endpointv3 Equivalent
GET /api/v2/auditors/GET /v3/partners/service-providers/?service_type=service_provider_type_audit
GET /api/v2/auditors/{id}/GET /v3/partners/service-providers/{uuid}/

New in v3 (No v2 Equivalent)

EndpointWhat It Provides
GET /v3/partners/investors/Cross-entity investor discovery (capital allocators, legal entities, persons, shareholder groups)
GET /v3/partners/service-providers/Professional-services firms (auditors, law firms, etc.)
GET /v3/partners/reference-data/Enums, countries, cities, industries, SIC codes
GET /v3/partners/capital-allocators/{uuid}/aum/Allocator AUM time series
GET /v3/partners/capital-allocators/{uuid}/investments/Per-allocator portfolio with share-level data
GET /v3/partners/capital-allocators/{uuid}/funds/Fund profiles managed by an allocator
GET /v3/partners/capital-allocators/{uuid}/captable/Cap table for allocator’s legal entity
GET /v3/partners/capital-allocators/{uuid}/news/News linked to a capital allocator
GET /v3/partners/funds/{uuid}/aum/Fund AUM / size history
GET /v3/partners/funds/{uuid}/investments/Per-fund investments with share-level data
GET /v3/partners/funds/{uuid}/news/News linked to a fund
GET /v3/partners/people/{uuid}/roles/Person-to-organization role associations
GET /v3/partners/people/{uuid}/investments/Per-person portfolio
GET /v3/partners/people/{uuid}/news/News linked to a person
GET /v3/partners/capital-receivers/{uuid}/news/News linked to a capital receiver

Key Field Mappings

Companies → Capital Receivers

v2 Fieldv3 FieldNotes
iduuidChanged to UUID
namelegal_entity.display_nameNested
uenlegal_entity.registration_numbers[].reg_numberNested; each item includes authority_type and authority_name
descriptionlegal_entity.descriptionNested
url / websitelegal_entity.website_urlRenamed
databaselegal_entity.domicile_country.iso_alpha3Renamed to domicile
headquaterslegal_entity.headquarters.country_iso_alpha3Spelling fixed; nested object
date_incorporatedlegal_entity.date_foundedFull date, with date_founded_precision; year_founded also available
investment_stagefunding.latest_investment_stage_nameDerived from most recent active deal
total_equity_fundingfunding.reported_and_filed_equity_usdDetail only; USD. Total equity is also broken out into filed_equity_usd / reported_equity_usd; funding.total_funding_usd adds debt
last_valuationfunding.latest_valuation_usdAvailable on list and detail; v2 values were already USD at import — v3 makes it explicit
size_of_last_roundfunding.latest_investment_amountDetail only
date_of_last_roundfunding.latest_investment_date—
revenuelatest_financials.operating_revenue_usdLatest snapshot on list; historical array on detail; v2 values were already USD at import — v3 makes it explicit
financial_year_endlatest_financials.financial_year_end—
revenue_growthlatest_financials.operating_revenue_growth_yoy_pctYoY percentage
ebitlatest_financials.earnings_before_tax_usdRenamed; v2 values were already USD at import — v3 makes it explicit
liabilitieslatest_financials.liabilities_usdv2 values were already USD at import — v3 makes it explicit
statuslegal_entity.trading_status.namev3 trading status enum; do not preserve v2 semantics
company_raisingis_raising_nowBoolean (was nullable)
female_founderlegal_entity.is_female_founderBoolean (was 0/1)
sectors(deprecated)Do not treat industries as a direct replacement
themesthemes[]Taxonomy overhauled; {"key": "themes_payments", "name": "Payments"} — match on key, not name. Note “FinTech” is now a tech, not a theme — it appears in techs[] as {"key": "techs_fintech", "name": "FinTech"}
updated_ataggregate_updated_atISO datetime in v3. Moves when the company or anything shown on it changes, and can be filtered and sorted, so use it to sync incrementally; see Incremental Sync. last_updated_at is still returned but deprecated
exit_type, liquidation, liquidation_details(deprecated)Use funding_status + transaction flags
(n/a)financial_statements_audited[]New in v3, detail only: audited financial statements for the company’s underlying legal entity — year, date_of_file, and a signed url that expires one hour after the response. See the detail-only fields
(n/a)financial_statements_extracted[]New in v3, detail only: machine-extracted statements, same shape as audited

Company Financials: Funding Rounds

v2 returned per-round data as fundings[] on GET /api/v2/companies/{id}/financials. In v3 each round is a row on GET /v3/partners/capital-receivers/{uuid}/deals/. See Deal Fields.

v2 fundings[] fieldv3 deal fieldNotes
labelself_declared_labelRound name as the company describes it
seriesallocation_deal_type_name / allocation_deal_type_keyNormalized round stage, e.g. "Series F" / "series_f"
investment_quarterinvestment_quarterNow a label such as "Q2 2024", not an integer
first_investment_date / last_investment_datefirst_investment_date / last_investment_dateFirst and last transaction dates within the round
share_class_idshare_classShare class names, comma-separated; the raw integer ID is not exposed
total_fundingtotal_deal_size_usdUSD. deal_size_usd / reported_deal_size_usd split the same figure by provenance
post_money_valuationpost_money_valuation_usdUSD
max_share_price_paidprice_per_share_usdHighest per-share price across the round’s transactions
total_shares_allocatedno_shares_issuedShares issued in this round, one figure per deal. Counts equity transactions only, so shares on a debt leg such as a conversion are left out. For an investor’s cumulative shares across rounds, use total_shares_allocated on /capital-receivers/{uuid}/investors/
pre_money_valuation(not exposed)See Coverage Gap Summary
average_share_price_paid(not exposed)See Coverage Gap Summary

Capital Providers → Capital Allocators

v2 Fieldv3 FieldNotes
investor_iduuid
namedisplay_name
typetypes[].name
countrylegal_entity.domicile_country.name
aumlatest_aum.aum_value_usd
stagespreferred_allocation_deal_types[].name

Fund Performance

v2 Fieldv3 FieldNotes
irrirrv3 irr is a fraction — 0.1800 means 18%. v3 also returns irr_pct (18.00) if you want the percentage without multiplying. Check the scale of whatever your v2 code assumed before porting
tvpitvpiTotal Value to Paid-In
dpidpiDistributions to Paid-In
rvpirvpiResidual Value to Paid-In
net_irrnet_irr
gross_irrgross_irr
as_of_dateas_of_datePerformance date
currencycurrency.iso_codev2 currency described the as-reported currency (mixed per record); v3 monetary fields (profit_usd, drawdowns_usd, committed_capital_usd) are always USD-normalized — this is a real semantic change, not just a rename
provenanceprovenance.nameData source

Commitment Deals

v2 Fieldv3 FieldNotes
iduuidChanged to UUID
fund_idParent URLUse /funds/{uuid}/commitments/
fund_nameParent resourceGet from fund detail
lp_idDifferent endpointUse /capital-allocators/{uuid}/commitments/
lp_nameVia allocator detail
commitment_amountinvestment_amount_usd
commitment_datedate
vintagefund.vintage_yearFrom fund
currencytransaction_currency.iso_code
provenanceprovenance.name

People Roles

Role data moved off the person record and onto /people/{uuid}/roles/, or the batch POST /v3/partners/people/roles/ for up to 1,000 person UUIDs per request.

v2 Fieldv3 FieldNotes
job_titletitleRenamed
role_type_keyrole_type.keyNested
role_type_namerole_type.nameNested

role_type is now a nested {key, name} object. role_type_key and role_type_name still exist in v3, but only as filters — query parameters on the people and roles endpoints, and body filter fields on the batch POST /people/roles/ — never as response fields.

Method Matrix

Complete endpoint reference for the v3 Partner API surface.

Capital Receivers

MethodEndpointPurposeKey params / body
GET/v3/partners/capital-receivers/Listsearch, ordering, registration_number, limit, offset
POST/v3/partners/capital-receivers/FilterJSON filters, search, ordering, limit, offset
GET/v3/partners/capital-receivers/{uuid}/Detail—
GET/v3/partners/capital-receivers/{uuid}/financials/Detailed financialslimit, offset
GET/v3/partners/capital-receivers/{uuid}/investors/Investors in this entitylimit, offset
GET/v3/partners/capital-receivers/{uuid}/deals/Funding rounds + transactionssearch, ordering, limit, offset
POST/v3/partners/capital-receivers/{uuid}/deals/Filter funding roundsJSON filters, search, ordering, limit, offset
GET/v3/partners/capital-receivers/{uuid}/news/News articlesordering, limit, offset

Capital Allocators

MethodEndpointPurposeKey params / body
GET/v3/partners/capital-allocators/Listsearch, ordering, limit, offset
POST/v3/partners/capital-allocators/FilterJSON filters, search, ordering, limit, offset
GET/v3/partners/capital-allocators/{uuid}/Detail—
GET/v3/partners/capital-allocators/{uuid}/commitments/Commitments by allocatorordering, limit, offset
GET/v3/partners/capital-allocators/{uuid}/aum/Allocator AUM historyordering, limit, offset
GET/v3/partners/capital-allocators/{uuid}/investments/Investments by allocatorlimit, offset
GET/v3/partners/capital-allocators/{uuid}/financials/Historical financial statementslimit, offset
GET/v3/partners/capital-allocators/{uuid}/captable/Cap table for allocator’s legal entitylimit, offset
GET/v3/partners/capital-allocators/{uuid}/funds/Fund profiles managed by allocatorlimit, offset
GET/v3/partners/capital-allocators/{uuid}/news/News articlesordering, limit, offset

Funds

MethodEndpointPurposeKey params / body
GET/v3/partners/funds/Listsearch, ordering, limit, offset
POST/v3/partners/funds/FilterJSON filters, search, ordering, limit, offset
GET/v3/partners/funds/{uuid}/Detail—
GET/v3/partners/funds/{uuid}/performance/Performance historyordering, limit, offset
GET/v3/partners/funds/{uuid}/aum/AUM / size historyordering, limit, offset
GET/v3/partners/funds/{uuid}/commitments/Commitments to fundordering, limit, offset
GET/v3/partners/funds/{uuid}/investments/Investments by fundlimit, offset
GET/v3/partners/funds/{uuid}/news/News articlesordering, limit, offset

People

MethodEndpointPurposeKey params / body
GET/v3/partners/people/Listsearch, role_type_key, role_type_name, ordering, limit, offset
POST/v3/partners/people/FilterJSON filters, search, role_type_key, role_type_name, ordering, limit, offset
GET/v3/partners/people/{uuid}/Detail—
GET/v3/partners/people/{uuid}/roles/Org rolesrole_type_key, role_type_name, limit, offset
GET/v3/partners/people/{uuid}/investments/Investments by personlimit, offset
GET/v3/partners/people/{uuid}/news/News articlesordering, limit, offset

Investors

MethodEndpointPurposeKey params / body
GET/v3/partners/investors/Discover investorsinvestor_type, search, invested_in_stage, invested_on_from, invested_on_to, ordering, limit, offset
POST/v3/partners/investors/Filter discoveryJSON filters with investor_type and/or name/search, plus pagination

Service Providers

MethodEndpointPurposeKey params / body
GET/v3/partners/service-providers/Listservice_type, search, ordering, limit, offset
POST/v3/partners/service-providers/FilterJSON filters, service_type, search, ordering, limit, offset
GET/v3/partners/service-providers/{uuid}/Detail—

Reference Data

MethodEndpointPurposeKey params / body
GET/v3/partners/reference-data/Enums, countries, cities, industries, SIC codestype, enum_categories

Coverage Gap Summary

v2 fields, filters, and endpoints that have no direct v3 equivalent.

Fields Not in v3

Categoryv2 field(s)SeverityWorkaround
Companiesadditional_idsLowNot migrated.
Companiessize_of_last_round, date_of_last_roundMediumAvailable on CR detail as funding.latest_investment_amount and funding.latest_investment_date.
Companiesdate_incorporated (full date)LowAvailable in v3 as legal_entity.date_founded (with date_founded_precision); year_founded also retained.
Companiesliquidation_detailsLowDeprecated; no v3 replacement.
Company Financialsfundings[] pre-money valuationMediumPer-round detail available via /capital-receivers/{uuid}/deals/. pre_money_valuation is not exposed.
Company Financialsfundings[].average_share_price_paidLowNot exposed. deals[].price_per_share_usd carries the highest price in the round, not the average.
Company Financialsadditional_fundings[] (news-sourced rounds)MediumNot in Partner API.
Company Financialsrevenue[] multi-year historyMediumCR detail has a condensed financials[]. For the full statement history use /capital-receivers/{uuid}/financials/.
Company Financialsshareholders[].value_of_investment_at_last_round_valuationMediumNot in Partner API.
Company Financialsper_share_class_summary[] (raw share_class_id)LowLargely covered by deals[].transactions[]. Raw integer share_class_id is unavailable.
Investorsinvestor_uen on listLowUse /capital-receivers/{uuid}/investors/ registration_numbers[].reg_number.
Investorsinvestment_date on listLowUse first_investment_date / latest_investment_date on /capital-receivers/{uuid}/investors/.
InvestorsPer-company stage amounts on /investments/MediumOnly on investor list as investments_by_deal_type; not per-company on /investments/.
InvestorsValuation calculations (value_of_investment_at_last_round_valuation*)MediumNot in Partner API.
InvestorsShare-class amounts (amount_invested_ordinary, _preference)LowNot in Partner API.
Investorsmax_price_per_shareLowNot in Partner API.
Fund listInline performance metrics (irr, dpi, rvpi, net_multiple)MediumSeparate call to /funds/{uuid}/performance/.
Fund listsize on listMediumSeparate call to /funds/{uuid}/aum/.
Fund Performancesource_name, capital_provider_source_acting_asLowNot exposed.
Fund Performancereport_path, reporting_periodLowUse year + quarter; report path is internal.
Commitment Dealssize (deal size)LowNot in Partner API.

Filters Not in v3

Categoryv2 filter(s)Workaround
Companiestotal_funding_min/max (server-side aggregate)Not available as a server-side filter.
Investorssectors, themes, invested_in_stage, invested_on_from/toNot available in Partner API; filter client-side or via other endpoints.
Investorsorder_by + order_directionNot available; no server-side ordering on investor results.
Fundsnet_irr_min/max, net_multiple_min/max, dpi_min/max, rvpi_min/maxNot available; performance is separate from fund list.
Fundslast_report_quarterNot available.
Commitment Dealsfund_type, queryFilter funds first, then query per-fund commitments.

v2 Endpoints Fully Deprecated

v2 endpointReason / replacement
GET /api/v2/companies/{uen}/uen/financialsUse ?registration_number= + detail + /financials/.
GET /api/v2/fund-performances/ (standalone list)Performance is per-fund only via /funds/{uuid}/performance/.
GET /api/v2/commitment-deals/ (standalone list)Commitments are per-fund or per-allocator only.
GET /api/v2/commitment-deals/{dealId} (direct detail)Match by UUID in nested results.

Worked Examples

Authenticate and list capital receivers

# 1. Exchange API key for access token
curl -sS https://api.altdmp.io/v3/token/issue/ \
  -H 'Content-Type: application/json' \
  -d '{"api_key": "YOUR_API_KEY"}' \
  | jq -r .access_token > /tmp/token.txt

# 2. List capital receivers
curl -sS "https://api.altdmp.io/v3/partners/capital-receivers/?search=Bunker&limit=20" \
  -H "Authorization: Bearer $(cat /tmp/token.txt)" | jq .

Filter capital receivers (POST body)

curl -sS "https://api.altdmp.io/v3/partners/capital-receivers/?search=platform&ordering=-latest_valuation_usd&limit=100" \
  -H "Authorization: Bearer $(cat /tmp/token.txt)" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq",  "field": "headquarters_country_iso_alpha3", "value": "SGP"},
        {"op": "gte", "field": "year_founded",                     "value": 2018},
        {"op": "in",  "field": "themes_keys",                      "value": ["themes_payments", "themes_climate_green_clean"]}
      ]
    }
  }' | jq .

Discover an investor and pull their portfolio

TOKEN=$(cat /tmp/token.txt)

# 1. Discover the investor — returns investor_type to determine which profile to call
INV=$(curl -sS "https://api.altdmp.io/v3/partners/investors/?investor_type=legal_entity&search=Temasek" \
  -H "Authorization: Bearer $TOKEN" | jq -r '.results[0]')
UUID=$(echo "$INV" | jq -r '.uuid')
TYPE=$(echo "$INV" | jq -r '.investor_type')

# 2. Fetch the portfolio using the appropriate profile endpoint
# investor_type=legal_entity may be a capital allocator or capital receiver — try allocator first
if [ "$TYPE" = "capital_allocator" ]; then
  URL="https://api.altdmp.io/v3/partners/capital-allocators/$UUID/investments/"
elif [ "$TYPE" = "fund" ]; then
  URL="https://api.altdmp.io/v3/partners/funds/$UUID/investments/"
elif [ "$TYPE" = "person" ]; then
  URL="https://api.altdmp.io/v3/partners/people/$UUID/investments/"
else
  # For legal_entity, try capital-allocators first
  URL="https://api.altdmp.io/v3/partners/capital-allocators/$UUID/investments/"
fi
curl -sS "$URL" -H "Authorization: Bearer $TOKEN" | jq .

Walk fund performance and AUM

FUND_UUID="your-fund-uuid-here"

curl -sS "https://api.altdmp.io/v3/partners/funds/$FUND_UUID/performance/?ordering=-date" \
  -H "Authorization: Bearer $TOKEN" | jq .

curl -sS "https://api.altdmp.io/v3/partners/funds/$FUND_UUID/aum/?ordering=-date" \
  -H "Authorization: Bearer $TOKEN" | jq .

Bootstrap reference data

# Cache enums and countries on app start
curl -sS "https://api.altdmp.io/v3/partners/reference-data/?type=enums,countries" \
  -H "Authorization: Bearer $TOKEN" \
  > reference_cache.json

# Use to populate filter dropdowns
jq '.enums.themes[] | "\(.key)\t\(.name)"' reference_cache.json

URL Cheat Sheet

v2 callv3 equivalent
POST /api/v2/oauth/tokenPOST /v3/token/issue/
GET /api/v2/companies?query=X&countries=SGPPOST /v3/partners/capital-receivers/ with search=X and {"filters":{"all":[{"op":"eq","field":"headquarters_country_iso_alpha3","value":"SGP"}]}}
GET /api/v2/companies/123GET /v3/partners/capital-receivers/{uuid}/
GET /api/v2/companies/201935876D/uenGET /v3/partners/capital-receivers/?registration_number=201935876D
GET /api/v2/companies/123/financialsGET /v3/partners/capital-receivers/{uuid}/ (funding block + financials[])
GET /api/v2/investors?query=XGET /v3/partners/investors/?search=X
GET /api/v2/investors/4GET /v3/partners/investors/ → find UUID → /capital-allocators/{uuid}/investments/
GET /api/v2/directors?query=XGET /v3/partners/people/?role_type_key=person_association_director&search=X
GET /api/v2/directors/4GET /v3/partners/people/{uuid}/
GET /api/v2/founders?query=XGET /v3/partners/people/?role_type_key=person_association_founder&search=X
GET /api/v2/founders/2GET /v3/partners/people/{uuid}/
GET /api/v2/auditors?query=XGET /v3/partners/service-providers/?service_type=service_provider_type_audit&search=X
GET /api/v2/auditors/2GET /v3/partners/service-providers/{uuid}/
GET /api/v2/capital-providers?category=fund-managerGET /v3/partners/capital-allocators/ (no direct category filter)
GET /api/v2/capital-providers/7034/GET /v3/partners/capital-allocators/{uuid}/
GET /api/v2/funds/?vintage_year_min=2020POST /v3/partners/funds/ with {"filters":{"all":[{"op":"gte","field":"vintage_year","value":2020}]}}
GET /api/v2/funds/477GET /v3/partners/funds/{uuid}/
GET /api/v2/fund-performances/?fund_id=508GET /v3/partners/funds/{fund-uuid}/performance/
GET /api/v2/commitment-deals/?fund_id=764GET /v3/partners/funds/{fund-uuid}/commitments/
GET /api/v2/commitment-deals/?limited_partner_id=3521GET /v3/partners/capital-allocators/{allocator-uuid}/commitments/
GET /api/v2/people/?first_name=ArjunGET /v3/partners/people/?search=Arjun

Migration Checklist

Frequently Asked Questions

How long will v2 keep running?

v2 will be supported through a managed migration window communicated to each customer individually. Confirm your timeline with your customer support representative.

Do I need new credentials?

Yes. v3 issues API keys via PropelAuth, which are exchanged for short-lived bearer tokens via POST /v3/token/issue/. Request new credentials before starting your migration.

Will v3 expose everything v2 did?

Most v2 capability is preserved — either as a direct mapping or via a different shape. A small number of v2 fields are intentionally not surfaced (raw share-class IDs, news-sourced funding rounds, pre-money valuation). See the Coverage Gap Summary for the full list with workarounds.

What if my integration depends on a deprecated v2 field?

Email support@alternatives.pe with the specific field and use case. Many gaps have nearby v3 equivalents that can be adopted without losing functionality.

Filtering

Last updated: 6 October 2026

Every list endpoint in the Partner API accepts the same filter grammar: a filters object in a POST body, built from a fixed set of operators over a documented set of field names per endpoint. This page is the reference for that grammar — the operators, their exact matching behavior, the body shapes that are accepted, the supported filter fields for each endpoint, and what happens to a field name an endpoint does not support.

The endpoint pages repeat the fields most callers reach for first. This page is the authoritative list.

Where Filters Go

filters is a request-body parameter and is accepted only on POST. search, ordering, limit, and offset are query parameters and are accepted only on the query string. The two directions are not interchangeable, and swapping them never works:

You sendResult
filters in a POST bodyApplied
filters in ?filters=…400 Bad Request on both GET and POST
?filters= with no valueAccepted — expresses no filter
search / ordering / limit / offset in the body400 Bad Request. Only filters may be sent in the body
search / ordering / limit / offset on the query stringApplied, alongside a POST filter body

A POST request goes to the same path as the GET list — there is no /filter/ suffix:

curl -X POST https://api.altdmp.io/v3/partners/capital-receivers/?limit=50 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq",  "field": "domicile_country_iso_alpha3", "value": "SGP"},
        {"op": "gte", "field": "latest_valuation_usd",        "value": 50000000}
      ]
    }
  }'
resp = requests.post(
    f"{BASE}/capital-receivers/",
    headers=headers,
    params={"limit": 50},
    json={
        "filters": {
            "all": [
                {"op": "eq",  "field": "domicile_country_iso_alpha3", "value": "SGP"},
                {"op": "gte", "field": "latest_valuation_usd",        "value": 50000000},
            ]
        },
    },
)

/v3/partners/investors/ is the one endpoint that ignores a query-string filters instead of rejecting it, and it accepts a narrower filter grammar than the one on this page. See Investors → Advanced Filter (POST). Do not rely on the difference.


Filter Body Structure

filters must be a JSON object. A list or a scalar — {"filters": ["any"]} — returns 400 Bad Request. To ask for an unfiltered list, omit filters or send null or {}.

Two shapes are accepted at the root, and they can be combined in one body.

Logical form

The root holds all, any, or not, and each leaf condition names its own field:

{"filters": {"all": [{"op": "eq", "field": "domicile_country_iso_alpha3", "value": "SGP"}]}}

A leaf condition is {"op": …, "field": …, "value": …}. A bare leaf at the root is rejected — {"filters": {"op": "eq", "field": "…", "value": …}} returns 400 Bad Request, so wrap even a single condition in all.

Blocks nest to any depth. An entry inside all or any may itself be another all / any / not:

{
  "filters": {
    "all": [
      {"op": "eq", "field": "domicile_country_iso_alpha3", "value": "SGP"},
      {"any": [
        {"op": "gte", "field": "total_funding_usd", "value": 100000000},
        {"op": "eq",  "field": "is_raising_now",    "value": true}
      ]},
      {"not": {"op": "eq", "field": "trading_status_key", "value": "trading_status_ceased"}}
    ]
  }
}

Field-keyed form

The root can instead key on the field name, with the condition as its value and no field inside it:

{"filters": {"year_founded": {"op": "gte", "value": 2015}}}

Multiple field keys at the root are ANDed together. Within one field key you may nest all / any / not and omit field from every leaf — the field name is taken from the key:

{
  "filters": {
    "domicile_country_iso_alpha3": {"op": "eq", "value": "SGP"},
    "year_founded": {"all": [
      {"op": "gte", "value": 2015},
      {"op": "lte", "value": 2020}
    ]}
  }
}

The field-keyed form does not work on the computed capital-receiver fields. total_funding_usd, latest_valuation_usd, latest_operating_revenue_usd, operating_revenue_growth_yoy_pct, latest_investment_date, latest_investment_stage_name, latest_investment_stage_key, and captable_is_managed are computed only when the endpoint sees the field inside a field key — which the field-keyed form does not produce. {"filters": {"latest_valuation_usd": {"op": "gte", "value": 50000000}}} returns 400 Bad Request. Use the logical form for these fields:

{"filters": {"all": [{"op": "gte", "field": "latest_valuation_usd", "value": 50000000}]}}

financials_latest_financial_year_end has the same restriction and more — see its caveat below. This is the reason to prefer the logical form generally: it is the only form every field supports.

Two shorthands are accepted in this form:

ShorthandEquivalent to
{"themes_keys": ["themes_payments", "themes_climate_green_clean"]}{"op": "in", "field": "themes_keys", "value": [...]}
{"domicile_country_iso_alpha3": "SGP"}{"op": "eq", "field": "domicile_country_iso_alpha3", "value": "SGP"}

A field key may sit alongside a logical block at the root; the two are ANDed. The logical form is the better default for anything a machine generates — it is uniform, and every condition carries its own field name.

Rejected bodies

BodyResult
{"not": []}, {"not": {}}, {"not": {"all": []}}400 — a body asking to exclude rows must not return every row
{"all": []}, {"any": []}Accepted, permissive no-op — a caller who supplied no conditions
{"all": ["oops"]} — a non-object where a condition was expected400
{"op": "range", "field": "…", "value": [1]} — range without exactly two values400
{"all": [{"field": "display_name", "value": "Acme"}]} — a condition missing op400
{"display_name": {"value": "Acme"}} — the same omission in the field-keyed form400
{"op": "eq", "field": 7, "value": "Acme"} — a field that is a number or an empty string400
{"op": "eq", "field": "is_female_founder", "value": "yes"} — a value that cannot be read as the field’s type400, naming the field

Malformed conditions return 400 in both root forms — {"all": ["oops"]} and {"name": {"all": ["oops"]}} behave the same way.

An incomplete condition used to be dropped silently. A leaf one key short of working — no op, or no field — contributed nothing to the query, so the request returned 200 and every row the other conditions allowed. It now returns 400 naming the keys it received. This changed in September 2026. If a saved filter has been quietly returning too many rows, this is a likely cause, and it will now fail loudly instead.

Send booleans as JSON true / false, and dates as valid ISO dates. A string standing in for a boolean is not reliably converted: "yes" and a lowercase "true" both return 400, as does an impossible date such as "2025-13-45". These used to return 500. The startup filters go further and accept only a JSON boolean.

field must be a field name, and a JSON array or object there is not handled cleanly on one endpoint. On /capital-allocators/, {"op": "eq", "field": [], "value": "Acme"} returns 500 rather than a 400; every other endpoint returns 400. Either way the request is malformed — pass the field name as a string. Do not treat the 500 as a retryable server fault.

filters as a JSON string

filters can also arrive as a string holding JSON — the only option in a form-encoded or multipart body. The capital receivers list and every batch endpoint parse it exactly as they parse the object form, so {"filters": "{\"all\": [...]}"} and {"filters": {"all": [...]}} return the same rows. A string that is not valid JSON returns 400. Prefer the object form in a JSON body; the string form exists for clients that cannot send one.


Operators

OperatorMeaningvalue shapeCase-sensitive
eqEqualsscalarUsually — depends on the field
neDoes not equalscalarUsually — depends on the field
inEquals any ofarrayUsually — depends on the field
ninEquals none ofarrayUsually — depends on the field
containsContains substringstringNo
ncontainsDoes not contain substringstringNo
startswithStarts withstringNo
endswithEnds withstringNo
gt / gteGreater than / or equalnumber or date stringn/a
lt / lteLess than / or equalnumber or date stringn/a
rangeBetween two bounds, inclusivearray of exactly 2, low then highn/a
isnullNo value recordedtruen/a
notnullA value is recordedtruen/a

exact is accepted as an alias for eq, and icontains as an alias for contains. Prefer the names in the table.

An unrecognized operator returns 400. {"op": "like", "field": "display_name", "value": "Acme"} is rejected, and the message lists every operator the field accepts:

{
  "status_code": 400,
  "detail": ["Operator 'like' is not supported for 'display_name'. Supported operators: contains, endswith, eq, gt, gte, in, isnull, lt, lte, ncontains, ne, nin, notnull, range, startswith. Also accepted: exact, icontains."]
}

(contains is the substring operator — there is no like.) This changed in September 2026. Previously an unrecognized operator was silently treated as eq and returned 200, so a typo in op read as a working filter. If you have a filter that has been returning plausible results with a misspelled operator, it will now fail — check your operator spelling against the table above.

Case sensitivity

The substring operators are always case-insensitive. For eq, ne, in, and nin, it depends on the field — and where they are case-sensitive, the failure is an empty result set rather than an error:

{"op": "eq", "field": "themes_names", "value": "fintech"}

That returns zero rows. "FinTech" returns the set tagged with that theme.

OperatorsBehavior
contains, ncontains, startswith, endswithCase-insensitive on every field
eq, ne, in, ninCase-insensitive on the single-valued text fields listed below; case-sensitive on the multi-value classification fields

These fields match eq, ne, in, and nin case-insensitively:

EndpointFields
/v3/partners/capital-receivers/display_name, description, domicile_country_name, domicile_country_iso_alpha3, headquarters_country_name, headquarters_country_iso_alpha3, headquarters_state_name, headquarters_city_name, trading_status_name, trading_status_key, latest_investment_stage_name, latest_investment_stage_key
/v3/partners/capital-allocators/display_name, description, domicile_country_name, domicile_country_iso_alpha3, headquarters_country_name, headquarters_country_iso_alpha3, preferred_allocation_type_name, preferred_allocation_type_key
/v3/partners/funds/display_name, description, structure, status_name, status_key, domicile_country_name, domicile_country_iso_alpha3, headquarters_country_name, headquarters_country_iso_alpha3
/v3/partners/people/given_name, family_name, location_country_name, location_country_iso_alpha3
/v3/partners/people/roles/role_type_name, role_type_key
/v3/partners/service-providers/display_name, description
/v3/partners/capital-receivers/{uuid}/deals/ and the batch /deals/deal_label, self_declared_label, description, source, allocation_type, allocation_subtype, deal_transaction_type, provenance, and the matching allocation_type_key, allocation_subtype_key, deal_transaction_type_key, provenance_key

{"op": "eq", "field": "family_name", "value": "smid"} and "Smid" return the same rows, and so do {"op": "eq", "field": "domicile_country_iso_alpha3", "value": "sgp"} and "SGP".

The multi-value classification fields still require exact casing. Every *_names / *_keys field — themes_names, techs_keys, preferred_countries_names, types_names, service_type_name, and the rest — matches eq, ne, in, and nin exactly as written. A company can carry several themes, and matching those case-insensitively would change what ne and nin mean on them: today {"op": "ne", "field": "themes_names", "value": "Fintech"} returns companies with no Fintech theme, and that meaning is preserved rather than traded for case tolerance. service_type_name on service providers and nationality_name on people behave the same way.

Also case-sensitive: any field that is not text at all — uuid, dates, numbers, and booleans — where casing does not apply.

The safe practice is the same either way: match the casing the API returns. Take values from Reference Data or from a response body rather than typing them by hand. A filter that already passes exact casing keeps working as more fields become case-insensitive; one that relies on a case mismatch to exclude something does not.

And prefer *_key fields over *_name fields for exact matching: keys are lowercase, underscore-delimited, and contractual, so there is no casing to get wrong. {"op": "eq", "field": "trading_status_key", "value": "trading_status_ceased"} is more robust than the same filter on trading_status_name.

range

value is an array of exactly two elements, low bound first, and both bounds are inclusive. Any other length returns 400 Bad Request.

{"op": "range", "field": "date_founded", "value": ["2015-01-01", "2020-12-31"]}

isnull and notnull

Pass true as the value. {"op": "isnull", "field": "latest_valuation_usd", "value": true} returns rows with no known valuation; notnull returns rows that have one.

They mean “no value recorded”, not strictly “null”. On a text field, a blank value counts as no value recorded, so isnull returns it and notnull does not. This changed in September 2026 — a notnull filter on a text field used to return every row, because a blank was treated as a value. If you have been using notnull on a text field as a way to get the full result set, it will now return fewer rows.

The two are exact complements. For any field, isnull and notnull partition the result set: their counts always sum to the unfiltered total, with no row in both and none in neither.

The value is ignored. {"op": "isnull", "value": false} still means “no value recorded” — it does not invert to notnull. Use notnull for the other direction.

Dates

Date and date-time fields take ISO 8601 strings — "2020-01-01" for a date, "2026-08-19T00:00:00Z" for a timestamp — and work with eq, ne, gt, gte, lt, lte, and range.

aggregate_updated_at is stricter. It accepts only eq, gt, gte, lt, and lte, and only a date-time with a UTC offset ("2026-09-24T00:00:00Z" or "2026-09-24T08:00:00+08:00"). Anything else returns 400 naming the field:

{
  "status_code": 400,
  "detail": ["'aggregate_updated_at' requires an ISO 8601 date-time with a UTC offset, such as \"2026-09-24T00:00:00Z\"; got '2026-09-24'."]
}

See Incremental Sync for how to page on it.


Nulls and Filters

null means unknown. It is not zero and not false, and it never satisfies a comparison — so rows with a null in the filtered field are excluded from every gt / gte / lt / lte / range filter on that field, in both directions. Filtering {"op": "lt", "field": "total_funding_usd", "value": 1000000} does not return companies whose funding is unknown.

Use isnull when you want the unknowns, and any when you want both:

{"filters": {"any": [
  {"op": "lt",     "field": "total_funding_usd", "value": 1000000},
  {"op": "isnull", "field": "total_funding_usd", "value": true}
]}}

Nullable booleans behave the same way. is_raising_now, is_open_to_co_investment, and the fund classification flags return null when nothing has been declared, which is distinct from false — an {"op": "eq", "value": false} filter returns only the explicit false rows. See Field Types and Nulls.


Keys and Display Names

Most taxonomy concepts are filterable two ways: a *_keys / *_key field taking the stable key, and a *_names / *_name field taking the display label. Filter on the key. Labels can be respelled without the underlying thing changing, which silently re-partitions anything matched on the display string.

{"op": "in", "field": "themes_keys", "value": ["themes_payments", "themes_climate_green_clean"]}

GET /partners/reference-data/?type=enums returns the valid keys for every such field. See Keys and Display Values for the full doctrine, including which fields are the key half of a pair with no _key suffix.

Deal filter fields publish both halves — allocation_type takes the display name "Equity", allocation_type_key takes the key — and both match case-insensitively. See Capital Receiver Deal Filter Fields below.


Industry Codes

industries_codes on capital receivers and preferred_industries_codes on capital allocators and funds match along the industry tree rather than as flat values. Codes are 2 digits for a sector, 4 for a group, and 6 for a leaf, and each code starts with its parent’s: 160101 (Hospital Activities) sits under 1601 (Human Health and Social Work Activities), which sits under 16 (Health and Social Care). The full list is the industries section of Reference Data; a code’s parent is its first 2 or 4 digits.

{"filters": {"all": [
  {"op": "in", "field": "industries_codes", "value": ["08", "160101"]}
]}}

That returns companies tagged anywhere under sector 08, plus companies tagged Hospital Activities. It does not return a company tagged only 16.

Changed in October 2026. These fields used to match codes flat: eq and in matched only the exact code, and contains was a raw substring match, so "0101" also matched 030101 in another sector. Results move in both directions. A parent code now returns records tagged anywhere beneath it, so expect more rows; a contains that relied on substring matching returns fewer. Re-check saved queries that filter on industry.


Supported Filter Fields

The lists below are the supported filter field names for each endpoint — the contractual surface, and the only names covered by our compatibility guarantees. Build against these.

The lists are enforced. On the five parent list endpoints, the per-company deals endpoint, the batch endpoints, and /investors/, a field name the endpoint does not support returns 400 Bad Request naming it and listing what that endpoint accepts:

{
  "status_code": 400,
  "detail": ["This endpoint does not filter on 'middle_name'. Supported filter fields: 'date_of_birth', 'family_name', 'given_name', 'location_country_iso_alpha3', 'location_country_name', 'uuid'."]
}

The check reads the whole filter body, so an unsupported name nested under all, any, or not does not get past it, and neither does the field-keyed form — including the scalar shorthand {"middle_name": "Ann"}, which is applied as an equality match when the name is supported. A field named inside a field-keyed condition, or an all / any / not nested under one, is checked too.

The filter surface on the five parent list endpoints is now exactly the list published for each one. Until September 2026 an undocumented name was resolved against internal storage rather than rejected, so a name that was never part of the API — a relation path with __ in it, say — could appear to work. Undocumented names now return 400, including the six the Fields Accepted but Not Applied section said to expect nothing from. Every documented field still filters exactly as before, so a filter body built from the lists below needs no change. If one of yours carries a name that is not in its endpoint’s list, that request now fails instead of quietly returning rows the filter should have excluded — worth checking before your next run. The five are capital receivers, capital allocators, funds, people, and service providers; the batch endpoints and /investors/ already worked this way.

A few legacy spellings kept for older integrations show up in the Supported filter fields: list in that error without appearing below. They are compatibility aliases, not contract — build against the documented name.

The per-company POST /v3/partners/capital-receivers/{uuid}/deals/ enforces its list too, as of September 2026. It was the last permissive endpoint: an unlisted name there filtered on whatever it happened to match, and transaction_label was accepted and ignored. Both now return 400. It accepts exactly the Capital Receiver Deal Filter Fields, the same list as the batch POST /v3/partners/capital-receivers/deals/.

uuid is a filter field on all five list endpoints: the profile’s own UUID on capital receivers, capital allocators, funds, and service providers, and the person UUID on people. With in it re-fetches a known set of records in one request.

aggregate_updated_at is a filter field and an ordering value on all five list endpoints, and so is uuid as an ordering value. Together they are how you pull only what changed since your last sync; see Incremental Sync.

Field names are per-endpoint. display_name is filterable on capital receivers, capital allocators, funds, and service providers, but the people endpoint has given_name and family_name instead.

Capital Receiver Filter Fields

POST /v3/partners/capital-receivers/

Identity and description — uuid, display_name, description, registration_number

Geography — domicile_country_name, domicile_country_iso_alpha3, headquarters_country_name, headquarters_country_iso_alpha3, headquarters_state_name, headquarters_city_name

Categorization — themes_names, themes_keys, horizontals_names, horizontals_keys, techs_names, techs_keys, business_models_names, business_models_keys, industries_codes

Status and attributes — trading_status_name, trading_status_key, is_female_founder, is_raising_now, is_startup, captable_is_managed, year_founded, date_founded

Funding and financials — total_funding_usd, latest_valuation_usd, latest_investment_stage_name, latest_investment_stage_key, latest_investment_date, latest_operating_revenue_usd, operating_revenue_growth_yoy_pct, financials_latest_financial_year_end

Change tracking: aggregate_updated_at

ordering accepts display_name, year_founded, total_funding_usd, latest_valuation_usd, latest_operating_revenue_usd, operating_revenue_growth_yoy_pct, latest_investment_date, financials_latest_financial_year_end, created_at, updated_at, aggregate_updated_at, uuid, and the deprecated last_updated_at. Without ordering, companies come back in the order they were added to the platform, not by name — send ordering=display_name for A to Z.

The funding and financials fields are computed on demand — only when they appear in the filter body or in ordering — so a plain list request pays nothing for them. Filter, ordering, and the response funding object all use the same names. See Common Filter Fields for types and examples. All eight are restricted to the logical form; see the field-keyed form caveat.

is_startup selects startups, or everything else. A company is a startup when it has ever raised venture capital, or when its funding status is Unfunded startup. {"op": "eq", "field": "is_startup", "value": true} returns startups, false returns every other company, and leaving the field out returns every company, as before. It works under all, any, and not, and combines with every other field:

{"filters": {"all": [
  {"op": "in", "field": "domicile_country_iso_alpha3", "value": ["SGP", "MYS", "THA", "IDN", "VNM", "PHL"]},
  {"op": "eq", "field": "is_startup",                  "value": true}
]}}

The value must be a JSON boolean, and the operator eq or ne — "true", 1, null, or any other operator returns 400. It is a filter only; list rows do not carry it. See Startup filters for the full rule and how current it is.

Startup filters

Four endpoints can scope results to startups, all using the same definition of a startup:

EndpointField or parameterWhere it goes
POST /v3/partners/capital-receivers/is_startupFilter body
POST /v3/partners/capital-allocators/has_startup_investmentsFilter body
GET / POST /v3/partners/investors/capital_receiver_is_startupQuery string on GET, filters.all on POST
GET / POST /v3/partners/people/, POST /v3/partners/people/roles/organization_is_startupQuery string

A company is a startup when either is true:

A company that raised venture capital stays a startup after it lists or takes private equity. A company funded only by venture debt or convertible debt is not a startup.

Classification is updated periodically, not on every change. A company created, published, or newly funded since the last update reads as a non-startup until the next one, so a company that has just raised its first venture round can take a while to appear under true. A company that is unpublished or deleted drops out immediately, whatever the classification says.

On capital receivers and capital allocators, true and false split the result set. Every record matches exactly one of them, and together they return what leaving the filter out returns. The other two split by transaction or by role instead, so one record can match both: an investor in startups and non-startups appears under each with different totals (see Investors → Startup scope), and a person with roles at both kinds of company appears under each (see People → Startup filter).

financials_latest_financial_year_end is narrower than the rest, and it fails silently. It resolves through a separate lookup, which only recognizes a leaf carrying an explicit field key, directly under all, with one of eq, gt, gte, lt, lte. Everything else returns 200 with the condition dropped or misapplied:

ShapeWhat happens
{"all": [{"op": "gte", "field": "financials_latest_financial_year_end", "value": "2025-01-01"}]}Applied
{"op": "range", …}, ne, in, nin, isnull, notnullDropped — full unfiltered result set, 200
{"financials_latest_financial_year_end": {"op": "gte", …}} (field-keyed)Dropped — full unfiltered result set, 200
The leaf inside notInverted — returns exactly the rows you asked to exclude, 200
The leaf inside anyANDed — narrower than the any you wrote

Use only eq / gt / gte / lt / lte, only directly under all, and never inside not or any. For a two-sided window, pass gte and lte as two conditions in the same all rather than range.

Capital Allocator Filter Fields

POST /v3/partners/capital-allocators/

Identity and description — uuid, display_name, description

Geography — domicile_country_name, domicile_country_iso_alpha3, headquarters_country_name, headquarters_country_iso_alpha3

Allocator type — types_names, types_keys, preferred_allocation_type_name, preferred_allocation_type_key

Stated preferences — preferred_countries_names, preferred_countries_iso_alpha3, preferred_themes_names, preferred_themes_keys, preferred_horizontals_names, preferred_horizontals_keys, preferred_business_models_names, preferred_business_models_keys, preferred_techs_names, preferred_techs_keys, preferred_fund_types_names, preferred_fund_types_keys, preferred_allocation_subtypes_names, preferred_allocation_subtypes_keys, preferred_allocation_deal_types_names, preferred_allocation_deal_types_keys, preferred_industries_codes

Amounts — cheque_size_min, cheque_size_avg, cheque_size_max, dry_powder, median_valuation, current_allocation, target_allocation, stated_count_of_investments

The seven monetary fields are filtered in whole US dollars, the same unit and the same client-facing names the response and ordering use — pass 1000000, not "1,000,000" or "1m". Use the numeric operators (gt, gte, lt, lte, range, eq, ne); contains and the other substring operators do not apply to them. cheque_size_min / cheque_size_avg / cheque_size_max are three independent values, so a band around a firm’s typical cheque is a filter on cheque_size_avg rather than a range across the min and max fields. As with every numeric field, an allocator that has not stated the figure is null and is excluded from a comparison in both directions — see Nulls and Filters.

{"all": [
  {"op": "gte", "field": "cheque_size_avg", "value": 1000000},
  {"op": "lte", "field": "cheque_size_avg", "value": 10000000},
  {"op": "gt",  "field": "dry_powder",      "value": 50000000}
]}

Preference flags — is_open_to_first_time_fund, is_open_to_co_investment, has_preference_for_balanced_funds, has_preference_for_hybrid_funds, has_preference_for_special_situation_funds, has_preference_for_separate_account

Portfolio — has_startup_investments

Change tracking: aggregate_updated_at

has_startup_investments selects firms that have invested in a startup. true returns firms where GET /capital-allocators/{uuid}/investments/ has at least one row for a startup; false returns every other firm, including firms with no investments at all. That covers the firm’s own equity investments as buyer (or as seller in a secondary), and those of the legal entities below it, the funds it manages, and the legal entities below those funds. Nothing linked to an unpublished or deleted record counts. The value must be a JSON boolean with eq or ne:

{"filters": {"all": [
  {"any": [
    {"op": "in", "field": "headquarters_country_iso_alpha3", "value": ["SGP", "MYS", "THA", "IDN", "AUS", "VNM", "PHL"]},
    {"op": "in", "field": "domicile_country_iso_alpha3",     "value": ["SGP", "MYS", "THA", "IDN", "AUS", "VNM", "PHL"]}
  ]},
  {"op": "eq", "field": "has_startup_investments", "value": true}
]}}

It is a filter only, and there is no query-parameter form, since this list takes filters from the body alone.

ordering accepts display_name, cheque_size_min, cheque_size_avg, cheque_size_max, dry_powder, median_valuation, current_allocation, target_allocation, stated_count_of_investments, created_at, updated_at, aggregate_updated_at, uuid. The default is display_name ascending.

The preferred_* fields are what an allocator states, not what it has done. To segment on actual behavior, compare against actual_allocation_deal_types on the response. Geography resolves through the allocator’s linked legal entity, so allocators backed by a Person return null and are excluded when a country filter is applied.

Fund Filter Fields

POST /v3/partners/funds/

Identity and description — uuid, display_name, description, fund_manager_name

Geography — domicile_country_name, domicile_country_iso_alpha3, headquarters_country_name, headquarters_country_iso_alpha3

Vehicle attributes — date_founded, vintage_year, status_name, status_key, structure, term_years

Stated preferences — preferred_countries_names, preferred_countries_iso_alpha3, preferred_themes_names, preferred_themes_keys, preferred_horizontals_names, preferred_horizontals_keys, preferred_business_models_names, preferred_business_models_keys, preferred_techs_names, preferred_techs_keys, preferred_allocation_types_names, preferred_allocation_types_keys, preferred_allocation_subtypes_names, preferred_allocation_subtypes_keys, preferred_allocation_deal_types_names, preferred_allocation_deal_types_keys, preferred_industries_codes

Classification flags — is_first_time_fund, is_raising_now, is_foia_reportable, is_captive_fund, is_single_deal_fund, is_continuation_fund, is_hybrid_fund, is_balanced_fund, is_special_situation_fund

Economics — management_fee_percentage, gp_commitment_percentage, hurdle_percentage, carry_percentage

Change tracking: aggregate_updated_at

ordering accepts display_name, vintage_year, term_years, created_at, updated_at, aggregate_updated_at, uuid. The default is display_name ascending.

Note preferred_allocation_types_* here is plural, against preferred_allocation_type_* singular on capital allocators. The classification flags are nullable — null means undeclared, not false.

fund_manager_name asks about the fund’s visible managers, one condition at a time. A fund can have several managers. Each fund_manager_name condition asks “does some manager match?” — or for ne, nin, ncontains, and not, “does no manager match?” — and managers that are unpublished or deleted are never considered. Two consequences:

  • all of two fund_manager_name conditions matches a fund whose managers meet one each. {"all": [{"op": "eq", "field": "fund_manager_name", "value": "Alpha Capital"}, {"op": "eq", "field": "fund_manager_name", "value": "Beta Partners"}]} returns funds co-managed by both. Until September 2026 it required a single manager to meet both conditions, so it never matched anything.
  • isnull matches a fund with no visible manager, and notnull one with at least one. search on the funds list does not match a hidden manager’s name either.

Person Filter Fields

POST /v3/partners/people/ — uuid, given_name, family_name, location_country_name, location_country_iso_alpha3, date_of_birth, aggregate_updated_at

ordering accepts family_name, given_name, date_of_birth, created_at, updated_at, aggregate_updated_at, uuid.

Role, country, and startup filtering on people is done with query parameters, not filter-body fields. role_type_key, role_type_name, domicile_country_iso_alpha3, headquarters_country_iso_alpha3, and organization_is_startup go on the query string and combine with the body filters. Sending any of them inside filters returns 400 Bad Request. See People.

Person Role Filter Fields

POST /v3/partners/people/roles/ — person_uuid (required, see Batch Endpoints), role_type_key, role_type_name. organization_is_startup is accepted on the query string — see People → Startup filter.

Service Provider Filter Fields

POST /v3/partners/service-providers/ — uuid, display_name, description, legal_entity_uuid, service_type_key, service_type_name, aggregate_updated_at

ordering accepts display_name, created_at, updated_at, aggregate_updated_at, uuid. The default is display_name ascending, and an unrecognized value returns 400 Bad Request listing them.

This is the only list endpoint that exposes legal_entity_uuid as a filter field, which makes it the one place you can look a service provider up by the legal entity behind it.

ordering=display_name replaced a relation-path value in September 2026. This endpoint previously accepted only legal_entity__display_name, and ignored anything it did not recognize. display_name is now the name to send; the old value returns 400.

Capital Receiver Deal Filter Fields

POST /v3/partners/capital-receivers/{uuid}/deals/

These fields apply on the batch POST /v3/partners/capital-receivers/deals/ too, alongside capital_receiver_uuid. Both endpoints return 400 for any other name, transaction_label included. The batch endpoint additionally enforces a date lower bound on market-wide queries — see Batch Deals.

Labels and classification — deal_label, self_declared_label, allocation_type, allocation_subtype, deal_transaction_type, and the matching key fields allocation_type_key, allocation_subtype_key, deal_transaction_type_key

Provenance — date, provenance, provenance_key, description, source

Amounts and shares — total_deal_size_usd, deal_size_usd, reported_deal_size_usd, deal_transaction_size_usd, post_money_valuation_usd, no_shares_issued, no_shares_bought, no_shares_sold

Prefer the *_key fields for classification and provenance. allocation_type, allocation_subtype, deal_transaction_type, and provenance match on display names, which can be reworded; the key fields take the stable key exactly as it appears in a deal response or in Reference Data. Casing is not the deciding factor here — all eight of these match case-insensitively under eq / ne / in / nin, so "equity" and "Equity" return the same deals — but a display name is presentation text and a key is contractual.

Three fields carry a round size, and they are not interchangeable:

Filter fieldMatches
total_deal_size_usdEvery deal with a size, whatever its provenance
deal_size_usdOnly deals whose provenance is not Reported
reported_deal_size_usdOnly deals whose provenance is Reported
deal_transaction_size_usdSame rows as total_deal_size_usd — an older name for it

{"op": "gte", "field": "deal_size_usd", "value": 1000000} silently excludes every Reported deal, because those are null in that field. Use total_deal_size_usd unless the source of the figure is part of what you are asking for.

deal_transaction_size_usd filters and sorts but has no response field of the same name — you cannot read back the value you matched on. no_shares_bought and no_shares_sold are returned on the deal row, and a filter or sort on either matches the value shown there.

The three share fields count equity transactions only; shares recorded on a debt transaction, such as a conversion or refinancing leg, are left out. The share fields and the size fields are also computed without any transaction whose buyer or seller is unpublished or deleted, so a filter or sort never selects a deal because of a party the response does not show.

Sorting deals

Both deals endpoints accept these on ?ordering=, with a - prefix for descending. The default is -date.

date, total_deal_size_usd, deal_size_usd, reported_deal_size_usd, deal_transaction_size_usd, post_money_valuation_usd, provenance, no_shares_issued, no_shares_bought, no_shares_sold

The size fields sort the way they filter. ?ordering=-total_deal_size_usd ranks every deal together; ?ordering=-deal_size_usd ranks only the non-Reported ones and pushes the rest to the end, since they hold null there.

Any other value returns 400 Bad Request naming the keys above. Before 2026-09-30 these endpoints ignored an unrecognized value and returned 200 in -date order; see Sorting.

Batch Financials Filter Fields

POST /v3/partners/capital-receivers/financials/ — capital_receiver_uuid (required), financial_year_end, uuid


Fields Accepted but Not Applied

No field name is accepted and then ignored any more. Every endpoint returns 400 Bad Request for a name it does not apply.

Seven names left this list in September 2026 and now return 400 naming the field:

None of the seven ever filtered anything, so no working filter changed — the endpoints now say so instead of accepting the condition and ignoring it.

One silent-drop case remains, and it is shape-dependent rather than name-dependent: financials_latest_financial_year_end outside its supported shapes. Four other cases have left this section — an unrecognized op, an incomplete condition, any unsupported condition on a batch endpoint, and the seven names above all now return 400.


Batch Endpoints

The batch endpoints take an in filter on the parent UUID and return one flat, paginated result set across many parents, so you fetch a sub-resource for a page of entities in one request rather than one request per entity.

Each one accepts a fixed set of filter fields and returns 400 for anything else. The sets are not uniform, because the endpoints are not:

EndpointParent UUID in filterOther fields accepted
POST /capital-receivers/deals/capital_receiver_uuid — optional, see Batch DealsThe full deal filter field set
POST /capital-receivers/financials/capital_receiver_uuid — requiredfinancial_year_end, uuid
POST /people/roles/person_uuid — requiredrole_type_key, role_type_name
POST /capital-receivers/news/capital_receiver_uuid — requiredNone
POST /capital-receivers/deal-share-types/capital_receiver_uuid — requiredNone
POST /capital-allocators/aum/capital_allocator_uuid — requiredNone
POST /funds/performance/fund_uuid — requiredNone
curl -X POST https://api.altdmp.io/v3/partners/capital-receivers/deals/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "in", "field": "capital_receiver_uuid", "value": [
          "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
          "fee788ac-cffe-46f0-9bb5-fedb62992bd8"
        ]}
      ]
    }
  }'

Rules, on every batch endpoint except POST /capital-receivers/deals/:

And on all of them, batch deals included:

Both of these used to pass silently, and that is the change most likely to break an existing integration. A condition beyond the required in was accepted and dropped: asking for “news for these 500 companies, but only since January” returned every news row for all 500, with a 200 and nothing marking the discarded condition. A body limit was ignored the same way, returning the default page size. Both now fail loudly. If you send extra conditions on any of the four endpoints whose “Other fields accepted” is None, those requests start returning 400 — drop the conditions and filter client-side, or call the per-entity endpoint where the fields are honored.

Batch Deals: filtering and ordering

This is the one batch endpoint with a real filter surface, and the only one where the parent UUID is optional.

capital_receiver_uuid is optional. A date filter on its own is a valid query, returning deals across every company on the platform:

{"filters": {"all": [{"op": "gte", "field": "date", "value": "2025-01-01"}]}}

A market-wide query must carry a date lower bound. With no capital_receiver_uuid, the request needs a date condition using eq, in, gt, gte, or range — something that puts a floor under the result set. lt, lte, and ne bound only the upper side and do not qualify. A request with neither company UUIDs nor a lower bound returns 400.

The bound has to hold for every row the filter can return, which is stricter than “a lower bound appears somewhere in the body”:

Filter bodyResult
{"all": [{"op": "gte", "field": "date", …}, <anything>]}Accepted — all inherits the bound from any one child
{"any": [<branch with a bound>, <branch without>]}400 — every branch of an any needs its own bound
{"not": {"op": "gte", "field": "date", …}}400 — negating a lower bound gives an upper one

Ordering is by deal date, most recent first, by default. Pass any key from Sorting deals on the query string, such as ?ordering=-total_deal_size_usd; anything else returns 400. Results carry a stable secondary sort, so paging through a large market-wide set does not repeat or skip rows between pages.

The other deal filter fields apply here too — the same set as the per-company endpoint, listed under Capital Receiver Deal Filter Fields, plus capital_receiver_uuid.

On top of the rules above, these return 400 here:

A date under any or not filters, but does not scope. The lower-bound check reads the logical structure, so placement decides whether a market-wide query is allowed — see the table above. Keep the bound in the top-level all and it always counts.


Sorting

ordering is a query parameter on every list and sub-resource route that returns more than one row. Pass a field name for ascending, or prefix it with - for descending. Each endpoint page lists the values its routes accept.

An unrecognized value returns 400 Bad Request naming the values the route accepts:

{
  "status_code": 400,
  "detail": ["Invalid ordering field 'bogus'. Valid ordering fields are: date, dpi, irr, irr_pct, net_multiple, rvpi."]
}

A route whose order is fixed returns 400 for any ordering, even a value that matches its own order, such as -date on batch news:

{
  "status_code": 400,
  "detail": ["This endpoint returns rows in a fixed order and does not accept `ordering`. Remove the parameter."]
}

Those routes are both cap tables, every financials/ route, the company investors/ route, person roles/, reference-data/, and the batch aum/, news/, deal-share-types/, performance/, and roles/ routes. Each endpoint page says so. On a route for one record, an unknown UUID still returns 404 first.

The same rules hold on every route that sorts:

These rules took effect across the sub-resource routes in September 2026. Before then, the AUM, news, commitments, fund performance, and investments routes differed: several returned 500 for an unknown value, two silently fell back to -date, the investments pages ignored ordering altogether, and ties came back in no guaranteed order. Two changes can move rows you already fetch: commitments with no date now come last under the default -date rather than first, and on fund performance a descending sort now puts null values last. If you page through a route and de-duplicate by UUID to cope with repeats, that is no longer needed.


Errors

A rejected filter body returns 400 Bad Request with a message naming the problem — a range with the wrong number of bounds, a malformed condition, a filter field name the endpoint does not support, or a batch UUID list that is missing, oversized, or invalid. An unsupported ordering value returns 400 on every route, as does any ordering on a route whose order is fixed; see Sorting.

Not everything wrong with a filter body is an error. These return 200 with results that do not match what you asked for, and nothing in the response says so:

CauseEffect
financials_latest_financial_year_end outside its supported shapesCondition dropped, or inverted under not
Wrong casing under eq / ne / in / nin on a case-sensitive fieldZero rows, or — for ne / nin — nothing excluded

Three entries left this table in September 2026 and now return 400: an unrecognized op, any condition beyond the required in on a batch endpoint, and any filter field name outside an endpoint’s supported list, including the seven that used to be accepted and dropped.

Test a new filter against a small known result set before wiring it into a sync.

One case keys the message by the parameter at fault rather than using detail — filters passed on the query string. See 400 response bodies for the exact shapes.

Capital Receivers

Last updated: 5 October 2026

Capital receivers are companies and startups that have received investment. This is the most frequently accessed entity type in the Partner API.

Endpoints

MethodEndpointAccess
GET/v3/partners/capital-receivers/Subscription required
POST/v3/partners/capital-receivers/Subscription required
GET/v3/partners/capital-receivers/{uuid}/Subscription required
GET/v3/partners/capital-receivers/{uuid}/financials/Subscription required
POST/v3/partners/capital-receivers/financials/Subscription required
GET/v3/partners/capital-receivers/{uuid}/captable/Subscription required
GET/v3/partners/capital-receivers/{uuid}/investors/Subscription required
GET/v3/partners/capital-receivers/{uuid}/deals/Subscription required
POST/v3/partners/capital-receivers/{uuid}/deals/Subscription required
POST/v3/partners/capital-receivers/deals/Subscription required
POST/v3/partners/capital-receivers/deal-share-types/Subscription required
GET/v3/partners/capital-receivers/{uuid}/news/Subscription required
POST/v3/partners/capital-receivers/news/Subscription required

List Capital Receivers

Returns a paginated list of capital receiver profiles.

curl "https://api.altdmp.io/v3/partners/capital-receivers/?search=shopback" \
  -H "Authorization: Bearer YOUR_TOKEN"
import requests

BASE = "https://api.altdmp.io/v3/partners"
headers = {"Authorization": "Bearer YOUR_TOKEN"}

resp = requests.get(f"{BASE}/capital-receivers/", params={"search": "shopback"}, headers=headers)
data = resp.json()
# data["results"] is the list, data["count"] is total matches

Example response (ShopBack):

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
      "legal_entity": {
        "uuid": "fee788ac-cffe-46f0-9bb5-fedb62992bd8",
        "display_name": "Shopback",
        "description": "ShopBack operates a consumer-facing cashback and rewards platform.",
        "year_founded": 2014,
        "date_founded": "2014-04-01",
        "date_founded_precision": "month",
        "is_female_founder": true,
        "domicile_country": {"iso_alpha3": "SGP", "name": "Singapore"},
        "headquarters": {
          "country_name": "Singapore",
          "country_iso_alpha3": "SGP",
          "state_name": null,
          "city_name": "Singapore"
        },
        "trading_status": {"key": "operating", "name": "Operating"},
        "registration_numbers": [
          {
            "reg_number": "201411189G",
            "authority_type": "UEN",
            "authority_name": "Accounting and Corporate Regulatory Authority"
          }
        ],
        "website_url": "https://www.shopback.sg"
      },
      "captable_source": {"type": "managed"},
      "description": "ShopBack operates a consumer-facing cashback and rewards platform.",
      "themes": [
        {"key": "themes_payments", "name": "Payments"},
        {"key": "themes_business_applications_saas", "name": "Business Applications / SaaS"}
      ],
      "horizontals": [{"key": "horizontals_marketplaces", "name": "Marketplaces"}],
      "techs": [{"key": "techs_fintech", "name": "FinTech"}],
      "business_models": [{"key": "business_models_b2c", "name": "B2C (Business-to-Consumer)"}],
      "industries": [{"code": "0602", "description": "Retail Trade (Except Vehicles)"}],
      "is_raising_now": false,
      "last_updated_at": "2025-10-16T07:33:30Z",
      "aggregate_updated_at": "2026-09-24T03:12:45.118204Z",
      "funding": {
        "latest_investment_date": "2022-12-13",
        "latest_investment_stage_name": "Series F",
        "latest_investment_stage_key": "series_f",
        "latest_valuation_usd": 925652927.23,
        "latest_valuation_date": "2022-12-13"
      },
      "latest_financials": {
        "financial_year_end": "2025-03-31",
        "operating_revenue_usd": 96318754.34,
        "earnings_before_tax_usd": -12045980.11,
        "liabilities_usd": 184220310.00,
        "operating_revenue_growth_yoy_pct": -2.40,
        "is_audited": true,
        "is_consolidated": false,
        "is_restated": false,
        "audit_opinion": {"key": "audit_opinion_unqualified", "name": "Unqualified Opinion"}
      }
    }
  ]
}

Identity fields live on legal_entity, not on the profile. display_name, year_founded, date_founded, date_founded_precision, domicile_country, headquarters, trading_status, and registration_numbers are all nested under legal_entity — the capital receiver profile itself carries only uuid, description, the classification arrays, captable_source, is_raising_now, last_updated_at, aggregate_updated_at, funding, and latest_financials. See Data Model.

funding and latest_financials are null when the company has no deals or no filed financials respectively.

List Query Parameters

ParameterTypeDescription
searchstringMatches company name, alternate names, and registration number
registration_numberstringFilter by company registration number
orderingstringField to sort by. Prefix with - for descending (e.g. -aggregate_updated_at). Without it, companies come back in the order they were added to the platform — not by name; send display_name for A to Z. The accepted values are listed under Capital Receiver Filter Fields, and anything else returns 400
limitintegerResults per page (default: 20)
offsetintegerPagination offset
total_funding_minnumberMinimum total equity raised (USD)
total_funding_maxnumberMaximum total equity raised (USD)

is_raising_now is a filter field on the advanced POST filter, not a GET query parameter.


Advanced Filter (POST)

Use POST with a JSON body for complex filters — range queries, multi-value matches, and boolean logic. The body accepts only the filters object; pass search, ordering, limit, and offset as query parameters. filters cannot go the other way — a value in ?filters= returns 400 Bad Request on both GET and POST.

Filter grammar, operators, and the supported filter field list are on the Filtering page. In short: filters must be an object whose top level is all (AND), any (OR), or not (NOT) — wrap even a single condition in all. Blocks nest to any depth. eq, ne, in, and nin match case-insensitively on this endpoint’s single-valued text fields — display_name, description, the geography fields, trading_status_*, and latest_investment_stage_* — and case-sensitively on the multi-value themes_*, horizontals_*, techs_*, and business_models_* fields; the substring operators are case-insensitive everywhere, and an unrecognized op returns 400 Bad Request. Use the all/any/not form rather than the field-keyed shorthand: the computed funding and financials fields below are only honored in that form.

# Singapore companies at Series B or later
curl -X POST https://api.altdmp.io/v3/partners/capital-receivers/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq", "field": "headquarters_country_iso_alpha3", "value": "SGP"},
        {"op": "in", "field": "latest_investment_stage_name",
         "value": ["Series B", "Series C", "Series D", "Series E", "Series F"]}
      ]
    }
  }'
resp = requests.post(
    f"{BASE}/capital-receivers/",
    headers=headers,
    json={
        "filters": {
            "all": [
                {"op": "eq", "field": "headquarters_country_iso_alpha3", "value": "SGP"},
                {"op": "in", "field": "latest_investment_stage_name",
                 "value": ["Series B", "Series C", "Series D", "Series E", "Series F"]},
            ]
        },
    },
    params={"limit": 20, "offset": 0},
)

Common Filter Fields

FieldTypeExample
uuidstring"c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67" — the capital receiver profile UUID; pair with in to re-fetch a known set
headquarters_country_iso_alpha3string"SGP"
domicile_country_iso_alpha3string"SGP"
latest_investment_stage_namestring"Series A"
latest_investment_stage_keystring"series_a"
latest_investment_datedate string"2022-12-13"
latest_valuation_usdnumber50000000
latest_operating_revenue_usdnumber10000000
operating_revenue_growth_yoy_pctnumber15.0
total_funding_usdnumber5000000
financials_latest_financial_year_enddate string"2023-01-01"
captable_is_managedbooleantrue
is_female_founderbooleantrue
is_raising_nowbooleanfalse
is_startupbooleantrue — filter only, not returned on rows; see below
year_foundedinteger2018
date_foundeddate string"2014-04-01"
aggregate_updated_atdate-time string"2026-09-24T00:00:00Z": the change watermark. eq, gt, gte, lt, and lte only, with a UTC offset. See Incremental Sync

date_founded filters on the full founding date and supports gte, lte, range, and eq. The response also exposes date_founded_precision ("year", "month", or "day") so you can tell how exact a date_founded value is. year_founded (year only) is retained for backward compatibility; sorting with ?ordering=year_founded is unchanged, though it now orders by the full founding date.

financials_latest_financial_year_end, latest_investment_stage_name, latest_investment_stage_key, latest_investment_date, latest_operating_revenue_usd, operating_revenue_growth_yoy_pct, latest_valuation_usd, and total_funding_usd are computed on demand — only when they appear in the filter body or in ordering — so a plain list request pays nothing for them.

latest_investment_date is the date of a company’s most recent active deal. Sort by it (?ordering=-latest_investment_date) or filter by it (e.g. {"op": "gte", "field": "latest_investment_date", "value": "2026-01-01"}) to pull only recently-funded companies — the v3 equivalent of the v2 date_of_last_round sort. Companies with no deals sort last in both directions.

These field names match the response funding object, so you read, filter, and sort by the same key (e.g. ?ordering=-total_funding_usd).

funding_status and has_vc_transactions are response fields, not filter fields. Both are on the funding object, and neither has ever narrowed a result set — a filter body naming one used to be accepted and ignored, and returns 400 Bad Request naming the field since September 2026. Read them off the rows you get back. Any name outside the supported list is rejected the same way.

captable_is_managed: true returns companies with a managed cap table (full transaction-level data available via /captable/ and /investors/); false returns snapshot-only companies. null (no filter) includes both. Companies with no cap table at all are excluded from both eq:true and eq:false queries.

is_startup: true returns startups — companies that have ever raised venture capital, or whose funding status is Unfunded startup — and false returns every other company. The value must be a JSON boolean with eq or ne. The classification is updated periodically, so a newly funded company can take a while to appear under true. See Filtering → Startup filters for the full rule.

Filter Examples

# Startups domiciled in Southeast Asia
curl -X POST https://api.altdmp.io/v3/partners/capital-receivers/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"all": [
        {"op": "in", "field": "domicile_country_iso_alpha3", "value": ["SGP", "MYS", "THA", "IDN", "VNM", "PHL"]},
        {"op": "eq", "field": "is_startup", "value": true}
      ]}}'

# Companies that filed financials for FY2023 or later
curl -X POST https://api.altdmp.io/v3/partners/capital-receivers/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"all": [{"op": "gte", "field": "financials_latest_financial_year_end", "value": "2023-01-01"}]}}'

# Companies with a managed cap table (full transaction-level data available)
curl -X POST https://api.altdmp.io/v3/partners/capital-receivers/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"all": [{"op": "eq", "field": "captable_is_managed", "value": true}]}}'

# Companies with a snapshot cap table only
curl -X POST https://api.altdmp.io/v3/partners/capital-receivers/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"all": [{"op": "eq", "field": "captable_is_managed", "value": false}]}}'
# Companies that filed financials for FY2023 or later
resp = requests.post(
    f"{BASE}/capital-receivers/",
    headers=headers,
    json={"filters": {"all": [{"op": "gte", "field": "financials_latest_financial_year_end", "value": "2023-01-01"}]}},
)

# Companies with a managed cap table
resp = requests.post(
    f"{BASE}/capital-receivers/",
    headers=headers,
    json={"filters": {"all": [{"op": "eq", "field": "captable_is_managed", "value": True}]}},
)

Get Capital Receiver Detail

Returns the full profile for a single capital receiver, including additional fields not present in the list response.

curl "https://api.altdmp.io/v3/partners/capital-receivers/c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67/" \
  -H "Authorization: Bearer YOUR_TOKEN"
uuid = "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67"
detail = requests.get(f"{BASE}/capital-receivers/{uuid}/", headers=headers).json()

The detail response returns every field from the list response except latest_financials, with these differences: legal_entity is the fuller variant (adding alternate_names, website_url, email, phone, founders, directors, auditors), funding is extended with the full equity/debt breakdown, and seven additional top-level fields are present.

{
  "uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
  "legal_entity": {
    "uuid": "fee788ac-cffe-46f0-9bb5-fedb62992bd8",
    "display_name": "Shopback",
    "description": "ShopBack operates a consumer-facing cashback and rewards platform.",
    "year_founded": 2014,
    "date_founded": "2014-04-01",
    "date_founded_precision": "month",
    "is_female_founder": true,
    "domicile_country": {"iso_alpha3": "SGP", "name": "Singapore"},
    "headquarters": {
      "country_name": "Singapore",
      "country_iso_alpha3": "SGP",
      "state_name": null,
      "city_name": "Singapore"
    },
    "trading_status": {"key": "operating", "name": "Operating"},
    "registration_numbers": [
      {
        "reg_number": "201411189G",
        "authority_type": "UEN",
        "authority_name": "Accounting and Corporate Regulatory Authority"
      }
    ],
    "alternate_names": [
      {"name": "Shopback Pte. Ltd.", "type": "Legal Name", "type_key": "alt_name_legal_name"}
    ],
    "website_url": "https://www.shopback.sg",
    "email": null,
    "phone": null,
    "founders": [
      {"uuid": "f2eac778-a47b-497e-a4c6-ddd71335a404", "name": "Shanru Lai"},
      {"uuid": "9c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f", "name": "Henry Chan"}
    ],
    "directors": [
      {"uuid": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "name": "Willson Cuaca"}
    ],
    "auditors": [
      {"uuid": "ad9be164-4e19-4822-987e-54efe73d8974", "name": "Ernst & Young LLP"}
    ]
  },
  "captable_source": {"type": "managed"},
  "description": "ShopBack operates a consumer-facing cashback and rewards platform.",
  "themes": [{"key": "themes_payments", "name": "Payments"}],
  "horizontals": [{"key": "horizontals_marketplaces", "name": "Marketplaces"}],
  "techs": [{"key": "techs_fintech", "name": "FinTech"}],
  "business_models": [{"key": "business_models_b2c", "name": "B2C (Business-to-Consumer)"}],
  "industries": [{"code": "0602", "description": "Retail Trade (Except Vehicles)"}],
  "is_raising_now": false,
  "last_updated_at": "2025-10-16T07:33:30Z",
  "aggregate_updated_at": "2026-09-24T03:12:45.118204Z",
  "funding": {
    "latest_investment_date": "2022-12-13",
    "latest_investment_stage_name": "Series F",
    "latest_investment_stage_key": "series_f",
    "latest_valuation_usd": 925652927.23,
    "latest_valuation_date": "2022-12-13",
    "reported_and_filed_equity_usd": 203586016.85,
    "filed_equity_usd": 203586016.85,
    "reported_equity_usd": null,
    "reported_and_filed_debt_usd": null,
    "filed_debt_usd": null,
    "reported_debt_usd": null,
    "total_funding_usd": 203586016.85,
    "latest_investment_amount": 50000000.00
  },
  "financials": [
    {
      "financial_year_end": "2025-03-31",
      "operating_revenue_usd": 96318754.34,
      "earnings_before_tax_usd": -12045980.11,
      "liabilities_usd": 184220310.00,
      "operating_revenue_growth_yoy_pct": -2.40,
      "is_audited": true,
      "is_consolidated": false,
      "is_restated": false,
      "audit_opinion": {"key": "audit_opinion_unqualified", "name": "Unqualified Opinion"}
    }
  ],
  "financial_statements_audited": [
    {
      "year": 2024,
      "date_of_file": "2025-06-30",
      "url": "https://assets.alternatives.pe/assets/file-attachments/996955694181.pdf?Expires=...&Signature=...&Key-Pair-Id=..."
    }
  ],
  "financial_statements_extracted": [
    {
      "year": 2024,
      "date_of_file": null,
      "url": "https://assets.alternatives.pe/assets/file-attachments/08b6d224b06e.pdf?Expires=...&Signature=...&Key-Pair-Id=..."
    }
  ],
  "funding_status": "VC-Funded",
  "has_vc_transactions": true,
  "has_pe_transactions": false,
  "has_ma_transactions": false,
  "has_debt_transactions": false,
  "has_liquidity_events": false,
  "has_distressed_transactions": false,
  "has_partial_exits": true
}

The detail-only fields

FieldTypeDescription
financialsarrayCondensed financial history, newest financial year end first. Nine fields per row — not the same shape as the /financials/ endpoint, which returns the full statement. Use /financials/ when you need the complete breakdown.
financial_statements_auditedarrayAudited financial-statement documents: year, date_of_file, and a signed, expiring url — see the note below.
financial_statements_extractedarrayMachine-extracted statement documents, same shape as above.
funding_statusstringOne of "Public Listed", "PE-Funded", "VC-Funded", "Other Unlisted", "Unfunded startup", or "n/a".
has_vc_transactions / has_pe_transactions / has_ma_transactions / has_debt_transactions / has_liquidity_events / has_distressed_transactions / has_partial_exitsboolean, nullableWhether the company has any deal of that kind on record.

The url values are signed and expire one hour after the response. Every request mints a fresh signature, and the Expires parameter in the URL carries the exact deadline as a Unix timestamp — read it rather than hard-coding a lifetime, in case the window changes. Three consequences:

  • Download promptly; don’t store the URL. A URL persisted anywhere — a database, a queue, a cached response — is dead within the hour. Re-request the detail endpoint to mint a new one.
  • An expired link is not a JSON error. The asset host returns 403 Forbidden with an XML body (<Error><Code>AccessDenied</Code></Error>), not the JSON error shape the rest of the API uses. Handle it separately from an API error response.
  • Treat a live URL as a credential. The signature alone authorizes the download — no Authorization header is involved — so anyone holding the link can fetch the document until it expires.

The funding object

Monetary amounts are returned as numbers to two decimal places, or null. Equity and debt are each broken down into filed, reported, and their combined reported-and-filed total:

FieldDescription
filed_equity_usdEquity from official filings (or, for some domiciles, paid-up capital)
reported_equity_usdEquity from reported (e.g. announced) rounds not yet reflected in filings
reported_and_filed_equity_usdfiled_equity_usd + reported_equity_usd
filed_debt_usd / reported_debt_usd / reported_and_filed_debt_usdThe equivalent breakdown for debt
total_funding_usdreported_and_filed_equity_usd + reported_and_filed_debt_usd
latest_valuation_usd / latest_valuation_dateMost recent known valuation and its date
latest_investment_amount / latest_investment_date / latest_investment_stage_name / latest_investment_stage_keyMost recent funding round summary

null funding amounts. A funding amount is null when it is unknown or known to net to zero — these fields never return 0.00. As a result, companies whose funding rolls up to zero sort to the end of funding-based orderings in both directions, and are excluded from numeric range filters (gte/lte, and the total_funding_min/total_funding_max filters), since a null value never satisfies a numeric comparison.

Public-listed companies. Companies classified as public no longer return funding or valuation — every amount in the funding object (and latest_valuation_usd) is null, since a public company has a market capitalization rather than private funding. funding_status is still populated (e.g. "Public Listed").

What counts toward totals. Funding totals count all primary funding across a company’s deals, regardless of deal subtype or type. Secondary transactions are never included.


Cap Table

Returns the current shareholding structure. The shape of the response depends on captable_source.type — always check this field before interpreting results. The order is fixed: this route takes no ordering, and any value returns 400.

curl "https://api.altdmp.io/v3/partners/capital-receivers/c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67/captable/" \
  -H "Authorization: Bearer YOUR_TOKEN"
captable = requests.get(f"{BASE}/capital-receivers/{uuid}/captable/", headers=headers).json()
source_type = captable["captable_source"]["type"]  # "managed" or "snapshot"

Managed Cap Table Response

When captable_source.type is "managed", the cap table is computed from transaction records. Investment amounts and ownership-hierarchy rollups are available.

Every share metric comes in two variants: _absolute is the holding recorded against the shareholder itself, and _aggregate rolls in the holdings of its child_entities (SPVs and other entities it controls). For a shareholder with no children the two are identical.

{
  "count": 4,
  "next": null,
  "previous": null,
  "results": [
    {
      "shareholder": {
        "uuid": "37c44afc-e55e-420c-bb2a-f9225a0d1c8e",
        "name": "East Ventures Fund 2",
        "type_key": "capital_allocator",
        "type": "CapitalAllocatorProfile",
        "allocation_type": "Equity"
      },
      "shares_issued_absolute": 1500000,
      "shares_issued_aggregate": 1500000,
      "shares_bought_secondary_absolute": 0,
      "shares_bought_secondary_aggregate": 0,
      "shares_sold_absolute": 250000,
      "shares_sold_aggregate": 250000,
      "total_invested_usd_absolute": 7500000.0,
      "total_invested_usd_aggregate": 7500000.0,
      "is_held_in_treasury": false,
      "shares_currently_held_absolute": 1250000,
      "shares_currently_held_aggregate": 1250000,
      "percentage_held_absolute": 4.80,
      "percentage_held_aggregate": 4.80,
      "holding_value_usd": 50000000.00,
      "child_entities_count": 0,
      "child_entities": []
    }
  ],
  "captable_source": {"type": "managed"},
  "as_of_date": "2026-08-07",
  "aggregations": {
    "shares_issued_aggregate": 26000000,
    "shares_bought_secondary_aggregate": 0,
    "shares_sold_aggregate": 250000,
    "total_invested_usd_aggregate": 203586016.85,
    "shares_currently_held_aggregate": 26000000,
    "percentage_held_aggregate": 100.00,
    "holding_value_usd": 925652927.23
  }
}
FieldTypeNotes
shareholderobjectuuid, name, type_key, and type. Join on type_key — one of capital_allocator, capital_receiver, fund, legal_entity, person, shareholder_group, or other — and use type for display only. See Buyer and Seller Objects for how to route uuid by type_key. allocation_type is added when the shareholder is a capital allocator that has declared a preferred allocation type. Two special rows represent a group rather than a named shareholder, and both carry a null uuid: ESOP ({"uuid": null, "name": "ESOP", "type_key": null, "type": null}) and pooled minorities ({"uuid": null, "name": "Other Shareholders", "type_key": "other", "type": "Other"}).
shares_issued_* / shares_bought_secondary_* / shares_sold_* / shares_currently_held_*integerShare counts.
total_invested_usd_*number, nullableCash invested in USD.
percentage_held_* / holding_value_usdnumber, nullablepercentage_held_* is a percentage — 18.50 means 18.5% of the company.
is_held_in_treasurybooleanShares held in treasury by the company itself.
child_entities_count / child_entitiesinteger / arrayControlled entities whose holdings roll into the _aggregate figures. Each entry has the same shape as a top-level row, so the structure nests.

The visible rows can sum to less than 100%. A shareholder whose record is unpublished or deleted has no row and is left out of count and aggregations, but every remaining row keeps its percentage of the whole company. So aggregations.percentage_held_aggregate can read below 100.00 — 61.80 where hidden holders held the rest. A visible holder’s own figures are unchanged, including shares bought from a hidden seller, so shares_bought_secondary_aggregate can exceed shares_sold_aggregate. The snapshot cap table below leaves hidden holders out the same way.

Snapshot Cap Table Response

When captable_source.type is "snapshot", data is sourced from a periodic shareholder register. Investment-amount data is not available.

{
  "count": 12,
  "next": null,
  "previous": null,
  "results": [
    {
      "shareholder": {"uuid": "...", "name": "Temasek Holdings", "type_key": "legal_entity", "type": "LegalEntity"},
      "number_of_shares": 5000000,
      "percentage_held": 35.00000,
      "is_former_shareholder": false,
      "start_date": "2021-03-15",
      "end_date": null
    }
  ],
  "captable_source": {"type": "snapshot"},
  "as_of_date": "2024-12-31",
  "aggregations": null
}

shareholder.type_key here is one of person, legal_entity, shareholder_group, or alt — the last for a register entry recorded only as a name, in which case shareholder.uuid is null. Join on type_key, not on type. aggregations is always null for snapshot cap tables.

percentage_held is a number and a percentage — 35.00000 means 35% of the company. It was a quoted string until 2026-08-14. Note that it carries five decimal places here, where the managed cap table’s percentage_held_absolute / _aggregate carry two.

When the company has no cap table at all — no transactions and no snapshot on file — the response drops the pagination keys entirely and returns just {"captable_source": {...}, "as_of_date": null, "results": [], "aggregations": null}. Read results rather than assuming count is present.

/investors/ returns HTTP 400 for snapshot companies — use /captable/ instead.


Investors

Returns investor-centric aggregates for companies with a managed cap table. Each row represents one investor’s aggregate position. The order is fixed: this route takes no ordering, and any value returns 400.

curl "https://api.altdmp.io/v3/partners/capital-receivers/c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67/investors/" \
  -H "Authorization: Bearer YOUR_TOKEN"
investors = requests.get(f"{BASE}/capital-receivers/{uuid}/investors/", headers=headers).json()

Example response (ShopBack):

{
  "count": 24,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "37c44afc-e55e-420c-bb2a-f9225a0d1c8e",
      "name": "East Ventures Fund 2",
      "investor_type": "fund",
      "registration_numbers": [
        {
          "reg_number": "201411189G",
          "authority_type": "UEN",
          "authority_name": "Accounting and Corporate Regulatory Authority"
        }
      ],
      "first_investment_date": "2019-04-30",
      "latest_investment_date": "2022-03-01",
      "shares_currently_held": 1250000.0,
      "current_share_holding_percentage": 4.8,
      "total_shares_allocated": 1500000.0,
      "total_secondary_shares": 0.0,
      "total_shares_sold": 250000.0,
      "amount_invested_usd": 7500000.0,
      "investment_by_stage_amount_usd": {
        "pre_seed": 0.0,
        "seed": 0.0,
        "series_a": 7500000.0,
        "series_b": 0.0,
        "series_c_and_beyond": 0.0,
        "other": 0.0
      }
    }
  ]
}
FieldTypeNotes
uuid / name / investor_typestringThe investor. investor_type is a key, not a label — one of capital_allocator, fund, capital_receiver, legal_entity, person, shareholder_group, or service_provider — and uuid is the ID to use with that key’s detail endpoint.
registration_numbersarrayPresent for entity-backed investors: reg_number, authority_type, authority_name.
registration_numberstring, nullablePresent instead of registration_numbers for person investors — a single identifier rather than a list.
shares_* / current_share_holding_percentage / amount_invested_usdnumber, nullablecurrent_share_holding_percentage is a percentage — 12.5 means 12.5%.
investment_by_stage_amount_usdobjectAlways the same six buckets: pre_seed, seed, series_a, series_b, series_c_and_beyond, other. Buckets with no investment return 0.0 rather than being omitted.

An investor row carries either registration_numbers (a list, for legal entities, capital allocators, and funds) or registration_number (a single string, for people) — never both. Check for whichever key is present rather than assuming one.

Only filed transactions count — Filed with Regulator and Filed via FOIA. Reported transactions are left out, as on /captable/, so an investor known only from reported rounds has no row. An investor also needs at least one recorded purchase to be listed. current_share_holding_percentage is the investor’s share of the shares all investors currently hold, not of the shares issued in funding rounds.

An investor whose record is unpublished or deleted has no row and is not in count. The remaining rows keep their percentages of the whole company, so current_share_holding_percentage across the rows can sum to less than 100%.


Deals

Returns funding rounds and individual transactions for a capital receiver. ShopBack, for example, has 25 recorded deals.

curl "https://api.altdmp.io/v3/partners/capital-receivers/c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67/deals/" \
  -H "Authorization: Bearer YOUR_TOKEN"
deals = requests.get(f"{BASE}/capital-receivers/{uuid}/deals/", headers=headers).json()

Example response (ShopBack Series F):

{
  "count": 25,
  "next": "https://api.altdmp.io/v3/partners/capital-receivers/c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67/deals/?limit=20&offset=20",
  "previous": null,
  "results": [
    {
      "uuid": "3869e63e-9b89-4986-9937-b7b21d2a2234",
      "capital_receiver_uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
      "date": "2022-12-13",
      "investment_quarter": "Q4 2022",
      "first_investment_date": "2022-07-20",
      "last_investment_date": "2023-05-08",
      "self_declared_label": "Series F",
      "allocation_deal_type_name": "Series F",
      "allocation_deal_type_key": "series_f",
      "allocation_type_key": "equity",
      "allocation_type_name": "Equity",
      "allocation_subtype_key": "venture_capital",
      "allocation_subtype_name": "Venture Capital (VC)",
      "share_class": "Ordinary, Series F Preferred",
      "no_shares_issued": 23023201,
      "provenance": {"key": "deal_txn_provenance_reported", "name": "Reported"},
      "total_deal_size_usd": 80000000.0,
      "deal_size_usd": null,
      "reported_deal_size_usd": 80000000.0,
      "post_money_valuation_usd": 925652927.23,
      "price_per_share_usd": 40.0,
      "description": "ShopBack, Southeast Asia's leading cashback and rewards platform, has closed an $80M Series F round...",
      "headline": "ShopBack Secures $80M in Series F Funding Round",
      "count_of_transactions": 14,
      "transactions": [
        {
          "uuid": "05eaa0d4-a73f-48f7-ad5b-04f0daf918d3",
          "date": "2022-12-13",
          "nature_of_transaction": "Primary",
          "transaction_type": "Series F",
          "allocation_type_key": "equity",
          "allocation_type_name": "Equity",
          "allocation_subtype_key": "venture_capital",
          "allocation_subtype_name": "Venture Capital (VC)",
          "share_class": "Series F Preferred",
          "provenance": {"key": "deal_txn_provenance_reported", "name": "Reported"},
          "number_of_shares": 2000000,
          "transaction_size_usd": 80000000.0,
          "price_per_share_usd": 40.0000,
          "buyer": {
            "uuid": "9f8e7d6c-5b4a-3928-1706-f5e4d3c2b1a0",
            "name": "GIC Private Limited",
            "type_key": "legal_entity",
            "type": "LegalEntity",
            "registration_numbers": [
              {
                "reg_number": "198100869P",
                "authority_type": "UEN",
                "authority_name": "Accounting and Corporate Regulatory Authority"
              }
            ]
          },
          "seller": {
            "uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
            "name": "Shopback",
            "type_key": "capital_receiver",
            "type": "CapitalReceiverProfile",
            "registration_numbers": [
              {
                "reg_number": "201411189G",
                "authority_type": "UEN",
                "authority_name": "Accounting and Corporate Regulatory Authority"
              }
            ]
          }
        }
      ]
    }
  ]
}

Deal Fields

FieldTypeDescription
uuidstringThe deal’s own UUID — stable across requests, so use it to deduplicate or to key deals in your own store.
capital_receiver_uuidstring, nullableThe company the deal belongs to. Present on the per-company endpoint too, not only the batch endpoint.
datedate stringDeal date.
investment_quarterstring, nullableCalendar quarter of date, e.g. "Q4 2022".
first_investment_date / last_investment_datedate string, nullableFirst and last transaction dates within the round; both fall back to date when the round has a single dated event.
self_declared_labelstring, nullableThe round name as the company describes it. Can differ from allocation_deal_type_name.
allocation_deal_type_name / allocation_deal_type_keystring, nullableNormalized round stage, e.g. "Series F" / "series_f".
allocation_type_name / allocation_type_keystring, nullableAsset class, e.g. "Equity" / "equity".
allocation_subtype_name / allocation_subtype_keystring, nullableSub-classification, e.g. "Venture Capital (VC)" / "venture_capital".
share_classstring, nullableAll distinct share classes across the round’s transactions, comma-separated and alphabetically sorted. A round issuing two classes returns "Ordinary, Series F Preferred". For a per-transaction class read transactions[].share_class.
no_shares_issuedinteger, nullableShares issued by the round, counting equity transactions only — shares recorded on a debt transaction, such as a conversion or refinancing leg, are left out.
no_shares_boughtinteger, nullableTotal shares bought in the deal’s Secondary transactions that name a buyer. null when the deal has none.
no_shares_soldinteger, nullableTotal shares sold in the deal’s Secondary transactions that name a seller. null when the deal has none.
provenanceobject, nullableWhere the deal’s information came from, as {key, name} — e.g. {"key": "deal_txn_provenance_filed_regulator", "name": "Filed with Regulator"}. This is what decides which of the two split size fields below is populated.
total_deal_size_usdnumber, nullableRound size in USD, whatever the provenance. Populated on every deal that has a size at all, so this is the field to read, sort, and filter on unless you specifically need the provenance split. null only when no amount is known.
deal_size_usdnumber, nullableRound size in USD when the deal’s provenance is not Reported (i.e. it is filed or otherwise sourced).
reported_deal_size_usdnumber, nullableRound size in USD when the deal’s provenance is Reported. At most one of deal_size_usd and this is ever populated — the other is null. Both are null when no amount is known, and total_deal_size_usd carries whichever one applies.
post_money_valuation_usdnumber, nullablePost-money valuation in USD.
price_per_share_usdnumber, nullableHighest per-share price across the round’s transactions, falling back to the deal-level price when no transaction records one.
descriptionstring, nullableBody of the linked news article, falling back to the deal’s public notes.
headlinestring, nullableHeadline of the linked news article, falling back to the deal’s public notes.
count_of_transactionsintegerNumber of transactions in transactions.
transactionsarrayIndividual buy/sell events making up the round.

A transaction whose buyer or seller is unpublished or deleted is left out — of transactions, and of every figure built from them: count_of_transactions, the share counts, the transaction part of the deal sizes, price_per_share_usd, and the first and last investment dates. The deal itself is still returned, even when none of its transactions remain.

Which size field to use. total_deal_size_usd is the one that ranks every deal together. The other two split the same figure by provenance, so sorting on either pushes the deals it does not cover to the end of the result set — ?ordering=-deal_size_usd starts at the largest filed round and never surfaces a Reported one, because those are null in that field. Use total_deal_size_usd for “biggest rounds”, and the split pair only when the source of the number matters to you.

Transaction Fields

FieldTypeDescription
uuidstringTransaction UUID.
datedate stringTransaction date — can differ from the deal’s date.
nature_of_transactionstring, nullablee.g. "Primary", "Secondary".
transaction_typestring, nullableTransaction-level stage label, e.g. "Series F".
allocation_type_key / allocation_type_name / allocation_subtype_key / allocation_subtype_namestring, nullableTransaction-level classification, which can differ from the deal-level values.
share_classstring, nullableThe single share class for this transaction.
provenanceobject, nullableWhere this transaction’s information came from, as {key, name}. Can differ from the deal-level provenance.
number_of_sharesintegerShares transacted.
transaction_size_usdnumber, nullableCash plus non-cash consideration in USD. null when it nets to zero.
price_per_share_usdnumber, nullablePrice per share, rounded to four decimal places — e.g. 40.0000. The deal-level price_per_share_usd is the same figure carried at full stored precision, so the two can print with a different number of trailing digits.
buyer / sellerobject, nullableThe counterparties. null when the side is not recorded.

Buyer and Seller Objects

FieldTypeDescription
uuidstring, nullableIdentifier of the counterparty, in whichever namespace type_key names.
namestring, nullableDisplay name.
type_keystring, nullableWhat kind of thing uuid is — join on this. One of capital_allocator, capital_receiver, fund, legal_entity, person, or shareholder_group.
typestring, nullableHuman-readable label for the same thing. Presentation only — do not join on it.
registration_numbersarrayPresent for entity counterparties: a list of {reg_number, authority_type, authority_name}.
registration_numberstring, nullablePresent instead for person counterparties — a single string.
allocation_typestringAdded when the counterparty is a capital allocator that has declared a preferred allocation type, e.g. "Equity".

uuid is not always a legal-entity ID. It points at the most specific record the counterparty has, and type_key tells you which. A firm that has a capital allocator profile returns that profile’s uuid with type_key: "capital_allocator"; the same firm without one returns its legal-entity uuid with type_key: "legal_entity". Joining buyer.uuid against a single namespace without branching on type_key will silently drop rows.

Route by type_key:

type_keyUse uuid with
capital_allocator/capital-allocators/{uuid}/
capital_receiver/capital-receivers/{uuid}/ — for a seller on a primary round this matches the deal’s own capital_receiver_uuid
fund/funds/{uuid}/
person/people/{uuid}/
legal_entityNo detail endpoint. Use it as a correlation key to group rows for the same registered company
shareholder_groupNo detail endpoint. name and uuid are all that is exposed

Read type_key, not type. type currently shows values like "CapitalReceiverProfile", but a future release changes what it presents — see Keys and Display Values.

Every amount on a deal row is a JSON number. Deal-level total_deal_size_usd, deal_size_usd, reported_deal_size_usd, post_money_valuation_usd, price_per_share_usd, and both transactions[] amounts. transactions[].price_per_share_usd was a quoted string until 2026-08-14 and is no longer.

Filter Deals via POST

Use POST with a JSON body to filter deals by type, date, or other attributes. Supports the same operators and logical combinators (all, any, not) as the main list filter, with the same requirement that filters starts with one of them.

Deal Filter Fields

The fields below are the common ones; the full list is under Capital Receiver Deal Filter Fields. allocation_type, allocation_subtype, and deal_transaction_type take display names such as "Equity"; each has a *_key twin that takes the stable key and is the better choice for exact matching. Any name outside the list returns 400 Bad Request listing the valid fields.

FieldTypeExample
datedate string"2020-01-01"
deal_labelstring"Series A"
self_declared_labelstring"Series A"
allocation_typestring"Equity"
allocation_subtypestring"Venture Capital (VC)"
deal_transaction_typestring"Seed", "Series A"
deal_size_usdnumber5000000
post_money_valuation_usdnumber50000000
no_shares_issuedinteger1000000
no_shares_boughtinteger500000
no_shares_soldinteger100000
# Only equity rounds after 2020
curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67/deals/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq",  "field": "allocation_type", "value": "Equity"},
        {"op": "gte", "field": "date", "value": "2020-01-01"}
      ]
    }
  }'

Batch Deals via POST

Use POST /v3/partners/capital-receivers/deals/ (note: no {uuid} in the path) to pull deals for many companies in one paginated request instead of calling the per-company .../{uuid}/deals/ endpoint once per company. Each returned deal row carries a capital_receiver_uuid field so you can join it back to the company it belongs to.

curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/deals/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "in", "field": "capital_receiver_uuid", "value": [
          "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
          "fee788ac-cffe-46f0-9bb5-fedb62992bd8"
        ]}
      ]
    }
  }'

Deal rows use exactly the same shape as the per-company deals endpoint — including capital_receiver_uuid, which is present on both — so you can point the same parsing code at either.

Filtering by deal date

Add a date condition and the window is applied server-side, rather than you pulling every round per company and discarding the out-of-window ones:

curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/deals/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "in", "field": "capital_receiver_uuid", "value": [
          "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67"
        ]},
        {"op": "range", "field": "date", "value": ["2025-01-01", "2025-06-30"]}
      ]
    }
  }'

date takes eq, ne, in, nin, gt, gte, lt, lte, range, isnull, and notnull. Values are ISO dates ("2025-01-01"), a range takes exactly two of them, and a one-sided window is gte or lte. Anything else — a substring operator, a non-ISO value, a range with the wrong number of ends — returns 400 naming the operators the field does accept.

Recent deals across the market

capital_receiver_uuid is optional. A date filter on its own returns deals across every company on the platform, which is the query to use for “what closed recently” rather than paging a company list:

curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/deals/?limit=1000" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "gte", "field": "date", "value": "2025-01-01"}
      ]
    }
  }'
deals = requests.post(
    f"{BASE}/capital-receivers/deals/",
    headers=headers,
    params={"limit": 1000, "ordering": "-date"},
    json={"filters": {"all": [{"op": "gte", "field": "date", "value": "2025-01-01"}]}},
).json()

A market-wide query must carry a date lower bound — eq, in, gt, gte, or range. lt and lte bound only the upper side and are not enough on their own; a request with neither company UUIDs nor a lower bound returns 400. Keep the bound in the top-level all, where it always counts: inside an any it counts only if every branch has one, and under not it never does.

Ordering

Results come back by deal date, most recent first. Pass ?ordering=date for ascending, or any other key from Sorting deals such as -total_deal_size_usd. Anything else returns 400 Bad Request naming the accepted keys. A stable secondary sort means paging through a large market-wide result set does not repeat or skip rows between pages.

Errors

See Filtering → Batch Deals for the full rules and the other deal fields you can filter on.

Batch Deal Share Types via POST

Use POST /v3/partners/capital-receivers/deal-share-types/ to pull distinct deal-share-type rows for many companies in one paginated request. It returns a flat, deduplicated row for each unique (capital_receiver_uuid, deal_uuid, share_class, transaction_type) combination — a lightweight alternative to fetching full deals per company when you only need the share classes and transaction types issued. The body must contain a non-empty capital_receiver_uuid in filter; up to 1,000 UUIDs are accepted per request.

curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/deal-share-types/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "in", "field": "capital_receiver_uuid", "value": [
          "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
          "fee788ac-cffe-46f0-9bb5-fedb62992bd8"
        ]}
      ]
    }
  }'

Example response:

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "capital_receiver_uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
      "deal_uuid": "9b1f2c3d-4e5a-6b7c-8d9e-0f1a2b3c4d5e",
      "deal_type_name": "Secondary Transaction",
      "deal_type_key": "other_equity_secondary_transaction",
      "share_class": "Ordinary",
      "transaction_type": "Secondary Transaction"
    }
  ]
}

Every field except capital_receiver_uuid is nullable. deal_type_name / deal_type_key are the deal’s normalized stage (the same values as allocation_deal_type_name / allocation_deal_type_key on a full deal row), and transaction_type is the transaction’s type display name — not a key, and not "primary" / "secondary".


Financials

Returns historical financial records ordered by most recent financial year end first. The order is fixed: this route takes no ordering, and any value returns 400. Each row includes year-over-year growth annotations for revenue, expenses, earnings, and liabilities. Each monetary metric is available in both its original reporting currency (bare field, e.g. operating_revenue) and USD-normalized (_usd suffix, e.g. operating_revenue_usd); reporting_currency identifies the source currency. In v2, revenue, EBIT, and valuation values were imported as USD — v3 preserves those same values in the _usd fields while also exposing the original reporting-currency figure.

Growth-% values are capped at ±1000%. Every year-over-year growth field (the annual_*_yoy_growth_pct fields here, and operating_revenue_growth_yoy_pct on the list endpoint) returns null when the computed change exceeds +1000% or falls below −1000%. Values at exactly ±1000% are retained. This suppresses meaningless swings off a near-zero base. A null therefore means the value is missing, has a zero denominator, or is out of bounds — and out-of-bound rows are excluded from numeric range filters and sort to the end of ordering on these fields, since null never satisfies a numeric comparison.

curl "https://api.altdmp.io/v3/partners/capital-receivers/c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67/financials/" \
  -H "Authorization: Bearer YOUR_TOKEN"
financials = requests.get(f"{BASE}/capital-receivers/{uuid}/financials/", headers=headers).json()

Example financials row:

{
  "capital_receiver_uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
  "uuid": "7f3a1b2c-4d5e-6f70-8192-a3b4c5d6e7f8",
  "financial_year_end": "2023-12-31",
  "total_revenue": 12450000.00,
  "total_revenue_usd": 9410850.00,
  "annual_total_revenue_yoy_growth_pct": 42.00,
  "operating_revenue": 12450000.00,
  "operating_revenue_usd": 9410850.00,
  "annual_operating_revenue_yoy_growth_pct": 42.00,
  "cogs": 4980000.00,
  "cogs_usd": 3762780.00,
  "annual_cogs_yoy_growth_pct": 31.00,
  "gross_profit": 7470000.00,
  "gross_profit_usd": 5645070.00,
  "annual_gross_profit_yoy_growth_pct": null,
  "total_expenses": 9200000.00,
  "total_expenses_usd": 6952200.00,
  "annual_total_expenses_yoy_growth_pct": null,
  "earnings_before_tax": -1730000.00,
  "earnings_before_tax_usd": -1307130.00,
  "annual_earnings_before_tax_yoy_growth_pct": null,
  "income_tax_expenses": null,
  "income_tax_expenses_usd": null,
  "annual_income_tax_expenses_yoy_growth_pct": null,
  "earnings_after_tax": null,
  "earnings_after_tax_usd": null,
  "annual_earnings_after_tax_yoy_growth_pct": null,
  "liabilities": 5630000.00,
  "liabilities_usd": 4251030.00,
  "annual_liabilities_yoy_growth_pct": null,
  "reporting_currency": "SGD",
  "notes": null,
  "is_audited": true,
  "audit_opinion": {"key": "audit_opinion_unqualified", "name": "Unqualified Opinion"},
  "is_consolidated": false,
  "is_restated": false
}

Every monetary and percentage value is a number or null, with growth percentages stated as percentages — 59.00 means +59%, not +0.59%. uuid identifies the financial record itself, and capital_receiver_uuid identifies the company — both are returned on the per-company endpoint as well as the batch endpoint.

Batch Financials via POST

Use POST /v3/partners/capital-receivers/financials/ (note: no {uuid} in the path) to pull financial records for many companies in one paginated request instead of calling the per-company .../{uuid}/financials/ endpoint once per company. The body must contain a non-empty capital_receiver_uuid in filter; up to 1,000 UUIDs are accepted per request. Each returned row carries a capital_receiver_uuid field so you can join it back to the company it belongs to.

curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/financials/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "in", "field": "capital_receiver_uuid", "value": [
          "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
          "fee788ac-cffe-46f0-9bb5-fedb62992bd8"
        ]}
      ]
    }
  }'

Rows are ordered by most recent financial year end first and use exactly the same shape as the per-company financials endpoint — capital_receiver_uuid is present on both.


News

Returns paginated news articles linked to this capital receiver.

curl "https://api.altdmp.io/v3/partners/capital-receivers/c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67/news/" \
  -H "Authorization: Bearer YOUR_TOKEN"
news = requests.get(f"{BASE}/capital-receivers/{uuid}/news/", headers=headers).json()

Example response:

{
  "count": 3,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "d4f2a891-1bc3-4e7d-9f32-abc123def456",
      "date": "2022-12-13",
      "headline": "ShopBack Secures $80M in Series F Funding Round",
      "body": "ShopBack, Southeast Asia's leading cashback and rewards platform, has closed an $80M Series F round...",
      "source_url": "https://techcrunch.com/2022/12/13/shopback-series-f/",
      "type": {"key": "news_type_funding", "name": "Funding"}
    }
  ]
}

News Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -date). Supported: date, headline, body, source_url, type, uuid, created_at, updated_at. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

Batch News via POST

Use POST /v3/partners/capital-receivers/news/ (note: no {uuid} in the path) to pull news articles for many companies in one paginated request instead of calling the per-company .../{uuid}/news/ endpoint once per company. The body must contain a non-empty capital_receiver_uuid in filter; up to 1,000 UUIDs are accepted per request. Each returned article carries a capital_receiver_uuid field so you can join it back to the company it belongs to.

curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/news/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "in", "field": "capital_receiver_uuid", "value": [
          "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
          "fee788ac-cffe-46f0-9bb5-fedb62992bd8"
        ]}
      ]
    }
  }'

Articles are ordered most recent first. This is the one batch endpoint whose rows genuinely differ from the per-company shape: they add a capital_receiver_uuid join field that the per-company endpoint does not return.

{
  "count": 4,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "d4f2a891-1bc3-4e7d-9f32-abc123def456",
      "capital_receiver_uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
      "date": "2022-12-13",
      "headline": "ShopBack Secures $80M in Series F Funding Round",
      "body": "ShopBack, Southeast Asia's leading cashback and rewards platform, has closed an $80M Series F round...",
      "source_url": "https://techcrunch.com/2022/12/13/shopback-series-f/",
      "type": {"key": "news_type_funding", "name": "Funding"}
    }
  ]
}

Capital Allocators

Last updated: 6 October 2026

Capital allocators are the entities that deploy capital — VC firms, private equity firms, family offices, corporate venture arms, sovereign wealth funds, and more.

Endpoints

MethodEndpointAccess
GET/v3/partners/capital-allocators/Subscription required
POST/v3/partners/capital-allocators/Subscription required
GET/v3/partners/capital-allocators/{uuid}/Subscription required
GET/v3/partners/capital-allocators/{uuid}/investments/Subscription required
GET/v3/partners/capital-allocators/{uuid}/aum/Subscription required
POST/v3/partners/capital-allocators/aum/Subscription required
GET/v3/partners/capital-allocators/{uuid}/commitments/Subscription required
GET/v3/partners/capital-allocators/{uuid}/financials/Subscription required
GET/v3/partners/capital-allocators/{uuid}/captable/Subscription required
GET/v3/partners/capital-allocators/{uuid}/funds/Subscription required
GET/v3/partners/capital-allocators/{uuid}/news/Subscription required

List Capital Allocators

curl "https://api.altdmp.io/v3/partners/capital-allocators/?search=Wavemaker" \
  -H "Authorization: Bearer YOUR_TOKEN"
import requests

BASE = "https://api.altdmp.io/v3/partners"
headers = {"Authorization": "Bearer YOUR_TOKEN"}

resp = requests.get(
    f"{BASE}/capital-allocators/",
    params={"search": "Wavemaker"},
    headers=headers,
)

The ?search= parameter matches against the allocator’s own display_name and the linked legal entity’s display_name.

One firm can appear as several rows. A firm holds a separate allocator profile per preferred allocation type, so a manager running equity and debt strategies returns two rows with two different uuid values and the same underlying legal_entity.uuid. Deduplicate on the root UUID before counting firms or totaling amounts — see How Many Profiles Per Root Record.

Example response:

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "f80754f5-16c7-4aac-9cab-4149285c7220",
      "display_name": "Wavemaker Partners",
      "description": "Wavemaker Partners is an early-stage VC firm investing across Southeast Asia and the US.",
      "profile_type_key": "legal_entity",
      "profile_type": "legalentity",
      "legal_entity": {
        "uuid": "b9ade3bb-05d1-4b0c-81e1-e91b8722b564",
        "display_name": "Wavemaker Partners",
        "year_founded": 2012,
        "date_founded": "2012-03-08",
        "date_founded_precision": "day",
        "domicile_country": {"iso_alpha3": "SGP", "name": "Singapore"},
        "trading_status": {"key": "operating", "name": "Operating"},
        "registration_numbers": [
          {
            "reg_number": "201205678K",
            "authority_type": "UEN",
            "authority_name": "Accounting and Corporate Regulatory Authority"
          }
        ]
      },
      "person": null,
      "types": [{"key": "ca_type_venture_capital_firm", "name": "Venture Capital Firm"}],
      "preferred_allocation_type": {"key": "equity", "name": "Equity"},
      "preferred_countries": [
        {"iso_alpha3": "SGP", "name": "Singapore"},
        {"iso_alpha3": "IDN", "name": "Indonesia"}
      ],
      "preferred_themes": [{"key": "themes_payments", "name": "Payments"}],
      "preferred_horizontals": [{"key": "horizontals_marketplaces", "name": "Marketplaces"}],
      "preferred_business_models": [
        {"key": "business_models_b2b", "name": "B2B (Business-to-Business)"}
      ],
      "preferred_techs": [{"key": "techs_fintech", "name": "FinTech"}],
      "preferred_fund_types": [
        {"key": "venture_general_other", "name": "Venture - General / Other"}
      ],
      "preferred_allocation_subtypes": [
        {"key": "venture_capital", "name": "Venture Capital (VC)"}
      ],
      "preferred_allocation_deal_types": [
        {"key": "seed", "name": "Seed"},
        {"key": "series_a", "name": "Series A"}
      ],
      "preferred_industries": [
        {"code": "09", "description": "Information and Communication"}
      ],
      "cheque_size_avg": 3000000,
      "cheque_size_min": 500000,
      "cheque_size_max": 10000000,
      "dry_powder": 85000000,
      "median_valuation": 45000000,
      "current_allocation": 115000000,
      "target_allocation": 200000000,
      "stated_count_of_investments": 38,
      "is_open_to_first_time_fund": null,
      "is_open_to_co_investment": true,
      "has_preference_for_balanced_funds": null,
      "has_preference_for_hybrid_funds": null,
      "has_preference_for_special_situation_funds": null,
      "has_preference_for_separate_account": null,
      "last_updated_at": "2025-10-16T07:33:30Z",
      "aggregate_updated_at": "2026-09-24T03:12:45.118204Z"
    }
  ]
}

profile_type_key tells you which of legal_entity / person is populated: "legal_entity" fills the legal_entity object and leaves person as null; "person" does the reverse and returns {"uuid", "given_name", "family_name", "display_name", "location_country"} under person. Branch on profile_type_key, not on the profile_type label. The legal_entity object on the list response is the compact variant — the detail response returns a fuller one.

The self-declared boolean preferences (is_open_to_*, has_preference_for_*) are nullable and commonly null — null means “not stated”, which is not the same as false.

List Query Parameters

ParameterDescription
searchFull-text search (see above)
orderingSort field. Prefix with - for descending. Supported: display_name, cheque_size_min, cheque_size_max, cheque_size_avg, target_allocation, median_valuation, current_allocation, dry_powder, stated_count_of_investments, created_at, updated_at, aggregate_updated_at, uuid. Default display_name. An unsupported value returns 400 Bad Request listing the valid fields
limitResults per page
offsetPagination offset

Advanced Filter (POST)

Use POST with a JSON body for complex filters. The body accepts only the filters object; pass search, ordering, limit, and offset as query parameters. filters cannot go the other way — a value in ?filters= returns 400 Bad Request on both GET and POST.

Operators, body shapes, and the supported filter field list for this endpoint are on the Filtering page. Note that eq, ne, in, and nin match case-insensitively on this endpoint’s single-valued text fields — display_name, description, the four domicile_* / headquarters_* country fields, and preferred_allocation_type_name / preferred_allocation_type_key — and case-sensitively on the multi-value preferred_* and types_* fields. An unrecognized op returns 400 Bad Request. See Case sensitivity.

# VC firms in Singapore that invest at Seed and Series A
curl -X POST https://api.altdmp.io/v3/partners/capital-allocators/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq", "field": "domicile_country_iso_alpha3", "value": "SGP"},
        {"op": "in", "field": "preferred_allocation_deal_types_names", "value": ["Seed", "Series A"]}
      ]
    }
  }'
resp = requests.post(
    f"{BASE}/capital-allocators/",
    headers=headers,
    json={
        "filters": {
            "all": [
                {"op": "eq", "field": "domicile_country_iso_alpha3", "value": "SGP"},
                {"op": "in", "field": "preferred_allocation_deal_types_names",
                 "value": ["Seed", "Series A"]},
            ]
        }
    },
)

Common Filter Fields

FieldTypeExample
uuidstring"3d9a5f74-2c18-4b6e-9a03-8e7f1c2d4b56" — the capital allocator profile UUID; pair with in to re-fetch a known set
aggregate_updated_atdate-time string"2026-09-24T00:00:00Z": the change watermark. eq, gt, gte, lt, and lte only, with a UTC offset. See Incremental Sync
domicile_country_iso_alpha3string"SGP"
domicile_country_namestring"Singapore"
headquarters_country_iso_alpha3string"SGP"
headquarters_country_namestring"Singapore"
preferred_allocation_deal_types_namesstring"Seed"
preferred_allocation_deal_types_keysstring"seed"
types_names / types_keysstring"Venture Capital Firm" / "ca_type_venture_capital_firm"
preferred_allocation_type_name / preferred_allocation_type_keystring"Equity" / "equity"
preferred_allocation_subtypes_names / preferred_allocation_subtypes_keysstring"Venture Capital (VC)" / "venture_capital"
preferred_countries_names / preferred_countries_iso_alpha3string"Singapore" / "SGP"
preferred_themes_names / preferred_themes_keysstring"Payments" / "themes_payments"
preferred_horizontals_names / preferred_horizontals_keysstring"Marketplaces" / "horizontals_marketplaces"
preferred_business_models_names / preferred_business_models_keysstring"B2B (Business-to-Business)" / "business_models_b2b"
preferred_techs_names / preferred_techs_keysstring"FinTech" / "techs_fintech"
preferred_fund_types_names / preferred_fund_types_keysstring"Venture - General / Other" / "venture_general_other"
preferred_industries_codesstring"09" — matches the code and every code beneath it; see Industry Codes
cheque_size_avg / cheque_size_min / cheque_size_maxnumber3000000
dry_powder / median_valuation / current_allocation / target_allocationnumber85000000
stated_count_of_investmentsinteger38
is_open_to_first_time_fund / is_open_to_co_investment / has_preference_for_balanced_funds / has_preference_for_hybrid_funds / has_preference_for_special_situation_funds / has_preference_for_separate_accountbooleantrue
display_name / descriptionstring"Meridian"
has_startup_investmentsbooleantrue — filter only, not returned on rows; see below

has_startup_investments returns firms whose investments include at least one startup with true, and every other firm — including firms with no investments — with false. It counts the firm’s own equity investments and those of the legal entities below it, the funds it manages, and the legal entities below those funds. The value must be a JSON boolean with eq or ne. There is no query-parameter form. See Capital Allocator Filter Fields for an example.

Multi-value preference fields are plural. The name is the plural response field plus _names or _keys — preferred_allocation_deal_types_names, not preferred_allocation_deal_type_name. Singular forms return 400 Bad Request. The one exception is the single-valued preferred_allocation_type, which takes preferred_allocation_type_name / preferred_allocation_type_key.

Geography filters (domicile_country_*, headquarters_country_*) resolve through the allocator’s linked legal entity. Capital allocators backed by a Person (rather than a legal entity) return null for these fields and are excluded when a country filter is applied.


Get Capital Allocator Detail

curl "https://api.altdmp.io/v3/partners/capital-allocators/f80754f5-16c7-4aac-9cab-4149285c7220/" \
  -H "Authorization: Bearer YOUR_TOKEN"
uuid = "f80754f5-16c7-4aac-9cab-4149285c7220"
detail = requests.get(f"{BASE}/capital-allocators/{uuid}/", headers=headers).json()

The detail response returns every field from the list response, with a fuller legal_entity object, plus twenty additional fields covering AUM, stated preferences not on the list, and the actual_* behavioral rollups.

{
  "uuid": "f80754f5-16c7-4aac-9cab-4149285c7220",
  "display_name": "Meridian Ventures Pte. Ltd.",
  "description": "Meridian Ventures is a Singapore-based early-stage VC firm investing across Southeast Asia, with a focus on B2B SaaS, FinTech, and HealthTech.",
  "profile_type_key": "legal_entity",
  "profile_type": "legalentity",
  "legal_entity": {
    "uuid": "b9ade3bb-05d1-4b0c-81e1-e91b8722b564",
    "display_name": "Meridian Ventures Pte. Ltd.",
    "description": "Meridian Ventures is a Singapore-based early-stage VC firm.",
    "year_founded": 2013,
    "date_founded": "2013-06-14",
    "date_founded_precision": "day",
    "is_female_founder": true,
    "domicile_country": {"iso_alpha3": "SGP", "name": "Singapore"},
    "headquarters": {
      "country_name": "Singapore",
      "country_iso_alpha3": "SGP",
      "state_name": null,
      "city_name": "Singapore"
    },
    "trading_status": {"key": "operating", "name": "Operating"},
    "registration_numbers": [
      {
        "reg_number": "201316157E",
        "authority_type": "UEN",
        "authority_name": "Accounting and Corporate Regulatory Authority"
      }
    ],
    "alternate_names": [
      {"name": "Meridian Ventures", "type": "Other", "type_key": "alt_name_other"}
    ],
    "website_url": "https://www.meridian.vc",
    "email": "ir@meridian.vc",
    "phone": null,
    "founders": [{"uuid": "7b8c9d0e-1f2a-3b4c-5d6e-7f8a9b0c1d2e", "name": "Priya Raman"}],
    "directors": [{"uuid": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "name": "Jia Wei Tan"}],
    "auditors": [{"uuid": "ad9be164-4e19-4822-987e-54efe73d8974", "name": "Baker Tilly LSC"}]
  },
  "person": null,
  "types": [{"key": "ca_type_venture_capital_firm", "name": "Venture Capital Firm"}],
  "preferred_allocation_type": {"key": "equity", "name": "Equity"},
  "preferred_countries": [
    {"iso_alpha3": "IDN", "name": "Indonesia"},
    {"iso_alpha3": "SGP", "name": "Singapore"},
    {"iso_alpha3": "VNM", "name": "Vietnam"}
  ],
  "preferred_themes": [
    {"key": "themes_payments", "name": "Payments"},
    {"key": "themes_business_applications_saas", "name": "Business Applications / SaaS"}
  ],
  "preferred_horizontals": [{"key": "horizontals_marketplaces", "name": "Marketplaces"}],
  "preferred_business_models": [
    {"key": "business_models_b2b", "name": "B2B (Business-to-Business)"}
  ],
  "preferred_techs": [{"key": "techs_fintech", "name": "FinTech"}],
  "preferred_fund_types": [
    {"key": "venture_general_other", "name": "Venture - General / Other"}
  ],
  "preferred_allocation_subtypes": [
    {"key": "venture_capital", "name": "Venture Capital (VC)"}
  ],
  "preferred_allocation_deal_types": [
    {"key": "seed", "name": "Seed"},
    {"key": "series_a", "name": "Series A"},
    {"key": "series_b", "name": "Series B"}
  ],
  "preferred_industries": [
    {"code": "09", "description": "Information and Communication"}
  ],
  "cheque_size_avg": 3000000,
  "cheque_size_min": 500000,
  "cheque_size_max": 10000000,
  "dry_powder": 85000000,
  "median_valuation": 45000000,
  "current_allocation": 115000000,
  "target_allocation": 200000000,
  "stated_count_of_investments": 38,
  "is_open_to_first_time_fund": null,
  "is_open_to_co_investment": true,
  "has_preference_for_balanced_funds": null,
  "has_preference_for_hybrid_funds": null,
  "has_preference_for_special_situation_funds": null,
  "has_preference_for_separate_account": null,
  "last_updated_at": "2025-10-16T07:33:30Z",
  "aggregate_updated_at": "2026-09-24T03:12:45.118204Z",
  "preferred_other_preferences": null,
  "route_to_market": ["Direct Deals"],
  "current_allocation_date": null,
  "latest_aum": {
    "uuid": "da38f125-485d-45ca-b637-73a71f31c8c1",
    "aum_value_date": "2024-12-31",
    "aum_value": 200000000.00,
    "aum_value_usd": 200000000.00,
    "reporting_currency": {"iso_code": "USD", "name": "US Dollar"}
  },
  "actual_count_of_investments": 52,
  "actual_allocation_types": ["Commitment", "Equity"],
  "actual_allocation_subtypes": ["Commitment", "Venture Capital (VC)"],
  "actual_allocation_deal_types": ["Pre-Seed", "Seed", "Series A", "Series B", "Series C"],
  "actual_fund_types": ["Pre-Seed", "Seed", "Series A", "Series B"],
  "actual_themes": ["Business Applications / SaaS", "Payments"],
  "actual_horizontals": ["Customer Stack", "Marketplaces"],
  "actual_business_models": ["B2B (Business-to-Business)", "B2C (Business-to-Consumer)"],
  "actual_techs": ["FinTech", "RetailTech"],
  "actual_industries": [
    {"code": "0602", "description": "Retail Trade (Except Vehicles)"},
    {"code": "09", "description": "Information and Communication"}
  ],
  "actual_countries": ["Indonesia", "Malaysia", "Singapore"],
  "actual_min_investment_amount_usd": 250000.0,
  "actual_max_investment_amount_usd": 8500000.0
}

Profile Fields

FieldTypeDescription
uuidstringCapital allocator profile UUID
display_namestringFirm or individual name
profile_type_keystring"legal_entity" or "person" — branch on this. Tells you which of the legal_entity / person objects is populated; the other is null
profile_typestringHuman-readable label for the same thing, currently "legalentity" / "person". Presentation only — do not branch on it, and see Keys and Display Values
legal_entityobject, nullableUnderlying registered company. See Data Model
personobject, nullableuuid, given_name, family_name, display_name, location_country — for person-backed allocators
typesarrayFirm-type classification (e.g. Venture Capital Firm, Family Office)
preferred_allocation_typeobject, nullableThe asset class this profile covers, not the firm’s only one. A firm allocating both equity and debt has a separate allocator profile per allocation type, each with its own UUID — see How Many Profiles Per Root Record
preferred_allocation_deal_typesarrayStated preferred deal stages
preferred_allocation_subtypesarrayStated preferred allocation subtypes
preferred_countriesarrayTarget geographies (iso_alpha3 + name)
preferred_themesarrayTarget investment themes
preferred_horizontalsarrayTarget horizontal tech categories
preferred_business_modelsarrayTarget business models
preferred_techsarrayTarget technology focus areas
preferred_fund_typesarrayFund types the firm allocates to
preferred_industriesarrayTarget industries (code + description)
preferred_other_preferencesstring, nullableFree-text preferences that don’t fit the structured fields
route_to_marketarray of strings, nullableHow the firm deploys, e.g. ["Direct Deals"]
cheque_size_avg / cheque_size_min / cheque_size_maxinteger, nullableCheque size in USD
dry_powderinteger, nullableUndeployed capital in USD
median_valuationinteger, nullableMedian entry valuation in USD
current_allocation / target_allocationinteger, nullableCapital deployed and target total, in USD
current_allocation_datedate string, nullableAs-of date for current_allocation
stated_count_of_investmentsinteger, nullableSelf-stated portfolio company count
is_open_to_first_time_fund / is_open_to_co_investment / has_preference_for_balanced_funds / has_preference_for_hybrid_funds / has_preference_for_special_situation_funds / has_preference_for_separate_accountboolean, nullableSelf-declared preferences. null means “not stated” — treat it as unknown, not as false
latest_aumobject, nullableMost recent AUM record: uuid, aum_value_date, aum_value, aum_value_usd, reporting_currency
actual_count_of_investmentsintegerDistinct companies the firm has invested in, from transaction records — primary and secondary buys. See the note below
actual_allocation_types / actual_allocation_subtypes / actual_allocation_deal_types / actual_fund_types / actual_themes / actual_horizontals / actual_business_models / actual_techsarray of stringsClassifications the firm has actually transacted in — display names, not keys, and flat strings rather than {key, name} objects
actual_industriesarrayIndustries actually invested in — {code, description} objects, unlike the other actual_* arrays
actual_countriesarray of stringsCountries of portfolio companies as country names (e.g. "Singapore"), not ISO alpha-3 codes
actual_min_investment_amount_usd / actual_max_investment_amount_usdnumber, nullableSmallest and largest recorded investment in USD

actual_* fields are derived from real transaction records on the platform. preferred_* fields are self-declared by the firm. Compare the two to assess stated vs. actual investment behavior.

What feeds the actual_* fields. A transaction counts when the firm — or an entity in its hierarchy, including the funds it manages — is the buyer, and the transaction is primary or secondary. A transaction with no recorded nature does not count, nor does an ESOP pool grant, nor anything linked to an unpublished or deleted company or deal. Counting is by distinct company, so a firm that led a round and later bought more on the secondary market counts that company once. One carve-out: for a firm whose preferred allocation type is Commitment, actual_count_of_investments counts each commitment regardless of nature.

Secondary buys were added in September 2026. Firms with secondary-market activity show higher actual_count_of_investments values and longer actual_* lists than before, with no underlying data changed. The /investors/ endpoint’s no_of_invested_companies still counts primary transactions only, so the two figures can differ for the same firm; they are different measures and should not be reconciled.

The two families use different value shapes. preferred_* arrays return {key, name} (or {code, description} for industries) so they can be matched against reference data. Most actual_* arrays return bare display-name strings with no key — match them on name, not key.


Investments

Returns the portfolio — all companies this allocator has invested in.

curl "https://api.altdmp.io/v3/partners/capital-allocators/f80754f5-16c7-4aac-9cab-4149285c7220/investments/" \
  -H "Authorization: Bearer YOUR_TOKEN"
investments = requests.get(
    f"{BASE}/capital-allocators/{uuid}/investments/", headers=headers
).json()

Example response row:

{
  "count": 52,
  "next": "https://api.altdmp.io/v3/partners/capital-allocators/f80754f5-.../investments/?limit=20&offset=20",
  "previous": null,
  "results": [
    {
      "capital_receiver_uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
      "legal_entity_uuid": "fee788ac-cffe-46f0-9bb5-fedb62992bd8",
      "capital_receiver_name": "Shopback",
      "shares_issued": 1500000.0,
      "shares_bought_secondary": 0.0,
      "shares_sold": 250000.0,
      "shares_currently_held": 1250000.0,
      "total_invested_usd": 7500000.00,
      "first_investment_date": "2019-04-30",
      "latest_investment_date": "2022-03-01"
    }
  ]
}

Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -latest_investment_date). Supported: latest_investment_date, first_investment_date, capital_receiver_name, shares_issued, shares_bought_secondary, shares_sold, shares_currently_held, total_invested_usd. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

A row is one company. Companies that are unpublished or deleted get no row, and transactions under a deleted deal are left out of the totals. Investments reached through a subsidiary or managed fund count only while that subsidiary or fund is itself visible. Share counts cover equity transactions only.


AUM History

Returns a time series of assets under management records.

curl "https://api.altdmp.io/v3/partners/capital-allocators/f80754f5-16c7-4aac-9cab-4149285c7220/aum/" \
  -H "Authorization: Bearer YOUR_TOKEN"
aum = requests.get(f"{BASE}/capital-allocators/{uuid}/aum/", headers=headers).json()

Example response:

{
  "count": 3,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "da38f125-485d-45ca-b637-73a71f31c8c1",
      "aum_value_date": "2024-12-31",
      "aum_value": 270000000.00,
      "aum_value_usd": 200000000.00,
      "reporting_currency": {"iso_code": "SGD", "name": "Singapore Dollar"}
    }
  ]
}
FieldTypeDescription
uuidstringUUID of the AUM record
aum_value_datedate stringAs-of date
aum_valuenumberAmount in the reporting currency
aum_value_usdnumberSame amount normalized to USD
reporting_currencyobject, nullableiso_code and name of the currency aum_value is denominated in

aum_value is in the reporting currency and aum_value_usd is normalized — they are equal only when reporting_currency.iso_code is "USD".

Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -aum_value_date). Supported: aum_value_date, aum_value, aum_value_usd, reporting_currency, uuid, created_at, updated_at. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

Batch AUM via POST

Use POST /v3/partners/capital-allocators/aum/ (note: no {uuid} in the path) to pull AUM history for many allocators in one paginated request instead of calling the per-allocator .../{uuid}/aum/ endpoint once per allocator. The body must contain a non-empty capital_allocator_uuid in filter; up to 1,000 UUIDs are accepted per request. Each returned record carries a capital_allocator_uuid field so you can join it back to the allocator it belongs to.

curl -X POST "https://api.altdmp.io/v3/partners/capital-allocators/aum/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "in", "field": "capital_allocator_uuid", "value": [
          "f80754f5-16c7-4aac-9cab-4149285c7220",
          "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
        ]}
      ]
    }
  }'

Records are ordered by most recent AUM value date first and use the same shape as the per-allocator AUM endpoint, plus the capital_allocator_uuid join field that the per-allocator endpoint does not return.

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "da38f125-485d-45ca-b637-73a71f31c8c1",
      "capital_allocator_uuid": "f80754f5-16c7-4aac-9cab-4149285c7220",
      "aum_value_date": "2024-12-31",
      "aum_value": 270000000.00,
      "aum_value_usd": 200000000.00,
      "reporting_currency": {"iso_code": "SGD", "name": "Singapore Dollar"}
    }
  ]
}

LP Commitments

Returns commitments made by this allocator to funds (i.e. where this allocator acts as an LP).

Access to this endpoint is subject to your subscription.

curl "https://api.altdmp.io/v3/partners/capital-allocators/f80754f5-16c7-4aac-9cab-4149285c7220/commitments/" \
  -H "Authorization: Bearer YOUR_TOKEN"
commitments = requests.get(
    f"{BASE}/capital-allocators/{uuid}/commitments/", headers=headers
).json()

Each row is one commitment transaction by this allocator, with the receiving fund nested under fund. There is no buyer / seller pair here — the allocator is implied by the path, and the counterparty is fund. (The mirror-image feed, LP commitments into a fund, has a different shape again.)

Example response:

{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "a8c7be86-7b7b-480a-a742-adbf94260b6a",
      "fund": {
        "uuid": "4ee71a3c-3926-4002-992a-4032bc93a8ea",
        "display_name": "Sembrani Nusantara",
        "vintage_year": 2020,
        "status": {"key": "fund_status_closed", "name": "Closed"}
      },
      "date": "2022-03-15",
      "allocation_type": {"key": "allocation_type_commitment", "name": "Commitment"},
      "allocation_subtype": {"key": "allocation_subtype_commitment", "name": "Commitment"},
      "provenance": {
        "key": "deal_txn_provenance_filed_regulator",
        "name": "Filed with Regulator"
      },
      "transaction_currency": {"iso_code": "USD", "name": "US Dollar"},
      "cash_value_transacted": 25000000.00,
      "cash_value_transacted_usd": 25000000.00
    }
  ]
}

Response Fields

FieldTypeDescription
uuidstringCommitment transaction UUID
fundobject, nullableThe fund receiving the commitment: uuid, display_name, vintage_year, status. null when the receiving legal entity has no fund profile — use fund.uuid with /funds/{uuid}/
datedate string, nullableCommitment date. Frequently null, since regulator filings often carry no date
allocation_type / allocation_subtypeobject, nullable{key, name} classification of the commitment
provenanceobject, nullableWhere the record came from, e.g. Filed with Regulator, Filed via FOIA, Reported
transaction_currencyobject, nullableiso_code and name of the currency cash_value_transacted is denominated in
cash_value_transactednumber, nullableCommitted amount in the transaction currency
cash_value_transacted_usdnumber, nullableSame amount normalized to USD

The amount fields here are named cash_value_transacted*, not investment_amount_usd. Sort with ?ordering=-date and expect a long tail of null dates and amounts, since regulator-filed commitments are often disclosed without either — rows with no value sort last in either direction.

A commitment into a fund whose record is unpublished or deleted is left out, as is one whose seller is hidden or that still sits under a deleted deal. A commitment with no deal is kept.

Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -date). Supported: date, cash_value_transacted, cash_value_transacted_usd, allocation_type, allocation_subtype, provenance, transaction_currency, uuid, created_at, updated_at. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

Financials

Returns historical financial records for this capital allocator’s underlying legal entity, ordered by most recent financial year end first. Each row includes year-over-year growth annotations. The order is fixed: this route takes no ordering, and any value returns 400.

Returns an empty list for allocators backed by a Person rather than a legal entity.

Growth-% values are capped at ±1000%. Each annual_*_yoy_growth_pct field returns null when the computed change exceeds +1000% or falls below −1000%; values at exactly ±1000% are retained. A null therefore means the value is missing, has a zero denominator, or is out of bounds.

curl "https://api.altdmp.io/v3/partners/capital-allocators/f80754f5-16c7-4aac-9cab-4149285c7220/financials/" \
  -H "Authorization: Bearer YOUR_TOKEN"
financials = requests.get(
    f"{BASE}/capital-allocators/{uuid}/financials/", headers=headers
).json()

Example financials row:

{
  "capital_receiver_uuid": null,
  "uuid": "5c8d9e0f-1a2b-3c4d-5e6f-708192a3b4c5",
  "financial_year_end": "2023-12-31",
  "total_revenue": 246422.00,
  "total_revenue_usd": 186310.00,
  "annual_total_revenue_yoy_growth_pct": 59.00,
  "operating_revenue": 246422.00,
  "operating_revenue_usd": 186310.00,
  "annual_operating_revenue_yoy_growth_pct": 59.00,
  "cogs": null,
  "cogs_usd": null,
  "annual_cogs_yoy_growth_pct": null,
  "gross_profit": null,
  "gross_profit_usd": null,
  "annual_gross_profit_yoy_growth_pct": null,
  "total_expenses": null,
  "total_expenses_usd": null,
  "annual_total_expenses_yoy_growth_pct": null,
  "earnings_before_tax": -970799.00,
  "earnings_before_tax_usd": -733918.00,
  "annual_earnings_before_tax_yoy_growth_pct": null,
  "income_tax_expenses": null,
  "income_tax_expenses_usd": null,
  "annual_income_tax_expenses_yoy_growth_pct": null,
  "earnings_after_tax": null,
  "earnings_after_tax_usd": null,
  "annual_earnings_after_tax_yoy_growth_pct": null,
  "liabilities": 832063.00,
  "liabilities_usd": 629187.00,
  "annual_liabilities_yoy_growth_pct": null,
  "reporting_currency": "SGD",
  "notes": null,
  "is_audited": true,
  "audit_opinion": {"key": "audit_opinion_unqualified", "name": "Unqualified Opinion"},
  "is_consolidated": false,
  "is_restated": false
}

Rows carry the same fields as the capital receiver financials endpoint, including a uuid for the financial record itself and a capital_receiver_uuid. Here capital_receiver_uuid is populated only when the same company also appears as a capital receiver; otherwise it is null. Every monetary and percentage value is a number or null. Growth percentages are percentages — 59.00 means +59%, not +0.59%.

Financials Query Parameters

ParameterDescription
limitResults per page
offsetPagination offset

Cap Table

Returns the shareholding structure for this capital allocator’s underlying legal entity. The response shape depends on captable_source.type. The order is fixed: this route takes no ordering, and any value returns 400.

Returns an empty response for allocators backed by a Person.

curl "https://api.altdmp.io/v3/partners/capital-allocators/f80754f5-16c7-4aac-9cab-4149285c7220/captable/" \
  -H "Authorization: Bearer YOUR_TOKEN"
captable = requests.get(
    f"{BASE}/capital-allocators/{uuid}/captable/", headers=headers
).json()
source_type = captable["captable_source"]["type"]  # "managed" or "snapshot"

The response structure mirrors the Capital Receivers cap table — check captable_source.type to determine which fields are present.

Managed Cap Table Response

When captable_source.type is "managed", the cap table is derived from transaction records. Share counts and investment amounts are available.

{
  "count": 6,
  "next": null,
  "previous": null,
  "results": [
    {
      "shareholder": {
        "uuid": "37c44afc-e55e-420c-bb2a-f9225a0d1c8e",
        "name": "Sequoia Capital Southeast Asia Fund III",
        "type_key": "fund",
        "type": "FundProfile"
      },
      "shares_issued_absolute": 2000000,
      "shares_issued_aggregate": 2400000,
      "shares_bought_secondary_absolute": 0,
      "shares_bought_secondary_aggregate": 0,
      "shares_sold_absolute": 0,
      "shares_sold_aggregate": 0,
      "total_invested_usd_absolute": 12000000.0,
      "total_invested_usd_aggregate": 14400000.0,
      "is_held_in_treasury": false,
      "shares_currently_held_absolute": 2000000,
      "shares_currently_held_aggregate": 2400000,
      "percentage_held_absolute": 18.50,
      "percentage_held_aggregate": 22.20,
      "holding_value_usd": 24000000.00,
      "child_entities_count": 1,
      "child_entities": [
        {
          "shareholder": {
            "uuid": "8b7a6c5d-4e3f-2a1b-0c9d-8e7f6a5b4c3d",
            "name": "Sequoia SEA III SPV 1",
            "type_key": "legal_entity",
            "type": "LegalEntity"
          },
          "shares_issued_absolute": 400000,
          "shares_issued_aggregate": 400000,
          "shares_bought_secondary_absolute": 0,
          "shares_bought_secondary_aggregate": 0,
          "shares_sold_absolute": 0,
          "shares_sold_aggregate": 0,
          "total_invested_usd_absolute": 2400000.0,
          "total_invested_usd_aggregate": 2400000.0,
          "is_held_in_treasury": false,
          "shares_currently_held_absolute": 400000,
          "shares_currently_held_aggregate": 400000,
          "percentage_held_absolute": 3.70,
          "percentage_held_aggregate": 3.70,
          "holding_value_usd": 4000000.00,
          "child_entities_count": 0,
          "child_entities": []
        }
      ]
    }
  ],
  "captable_source": {"type": "managed"},
  "as_of_date": "2026-05-08",
  "aggregations": {
    "shares_issued_aggregate": 10800000,
    "shares_bought_secondary_aggregate": 0,
    "shares_sold_aggregate": 0,
    "total_invested_usd_aggregate": 64800000.00,
    "shares_currently_held_aggregate": 10800000,
    "percentage_held_aggregate": 100.00,
    "holding_value_usd": 108000000.00
  }
}

The fields are identical to the capital receiver cap table — see there for what each one means. In particular, join shareholder.uuid by shareholder.type_key rather than by the type label, aggregations keys are *_aggregate names rather than total_shares_issued / total_shares_held, and child_entities rows nest with the same shape as top-level rows. As there, shareholders whose record is unpublished or deleted are left out of both the managed and the snapshot cap table, so the visible rows can sum to less than 100% — see the note on the capital receiver cap table.

Snapshot Cap Table Response

When captable_source.type is "snapshot", the cap table is sourced from a shareholder-register filing. Investment amounts are not available.

{
  "count": 4,
  "next": null,
  "previous": null,
  "results": [
    {
      "shareholder": {
        "uuid": "fee788ac-cffe-46f0-9bb5-fedb62992bd8",
        "name": "GIC Private Limited",
        "type_key": "legal_entity",
        "type": "LegalEntity"
      },
      "number_of_shares": 3500000,
      "percentage_held": 32.40000,
      "is_former_shareholder": false,
      "start_date": "2020-06-01",
      "end_date": null
    }
  ],
  "captable_source": {"type": "snapshot"},
  "as_of_date": "2024-12-31",
  "aggregations": null
}

When the allocator’s legal entity has no cap table at all — and for person-backed allocators, which have no legal entity — the pagination keys are omitted and the response is just {"captable_source": {...}, "as_of_date": null, "results": [], "aggregations": null}.


Funds

Returns a paginated list of funds managed by this capital allocator.

Returns an empty list for allocators backed by a Person. To retrieve investments for a specific fund, use /partners/funds/{uuid}/investments/.

curl "https://api.altdmp.io/v3/partners/capital-allocators/f80754f5-16c7-4aac-9cab-4149285c7220/funds/" \
  -H "Authorization: Bearer YOUR_TOKEN"
funds = requests.get(
    f"{BASE}/capital-allocators/{uuid}/funds/", headers=headers
).json()

Example response:

{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "legal_entity": {
        "uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
        "display_name": "Sequoia Capital SEA Fund III",
        "year_founded": 2021,
        "date_founded": "2021-02-11",
        "date_founded_precision": "day",
        "domicile_country": {"iso_alpha3": "CYM", "name": "Cayman Islands"},
        "trading_status": {"key": "operating", "name": "Operating"},
        "registration_numbers": [
          {"reg_number": "MC-987321", "authority_type": null, "authority_name": null}
        ]
      },
      "display_name": "Sequoia Capital SEA Fund III",
      "description": "Southeast Asia-focused growth equity fund",
      "fund_managers": [
        {"uuid": "c3d4e5f6-a7b8-9012-cdef-123456789012", "display_name": "Sequoia Capital Southeast Asia"}
      ],
      "status": {"key": "fund_status_closed", "name": "Closed"},
      "vintage_year": 2021,
      "is_raising_now": false,
      "last_updated_at": "2025-11-15T08:22:31Z",
      "aggregate_updated_at": "2026-02-03T09:41:07.512034Z"
    },
    {
      "uuid": "d4e5f6a7-b8c9-0123-defa-234567890123",
      "legal_entity": {
        "uuid": "e5f6a7b8-c9d0-1234-efab-345678901234",
        "display_name": "Sequoia Capital SEA Fund IV",
        "year_founded": null,
        "date_founded": null,
        "date_founded_precision": null,
        "domicile_country": null,
        "trading_status": null,
        "registration_numbers": []
      },
      "display_name": "Sequoia Capital SEA Fund IV",
      "description": null,
      "fund_managers": [
        {"uuid": "c3d4e5f6-a7b8-9012-cdef-123456789012", "display_name": "Sequoia Capital Southeast Asia"}
      ],
      "status": {"key": "fund_status_raising", "name": "Raising"},
      "vintage_year": 2024,
      "is_raising_now": true,
      "last_updated_at": "2026-03-01T14:10:00Z",
      "aggregate_updated_at": "2026-03-04T11:05:52.203118Z"
    }
  ]
}

Rows use the same shape as GET /partners/funds/. The nested legal_entity here is the compact variant (no headquarters, no description) — fetch /funds/{uuid}/ for the full object.

Funds Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: display_name). Supported: display_name, vintage_year, term_years, created_at, updated_at, aggregate_updated_at, uuid. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

News

Returns paginated news articles linked to this capital allocator’s underlying legal entity.

Returns an empty list for allocators backed by a Person.

curl "https://api.altdmp.io/v3/partners/capital-allocators/f80754f5-16c7-4aac-9cab-4149285c7220/news/" \
  -H "Authorization: Bearer YOUR_TOKEN"
news = requests.get(
    f"{BASE}/capital-allocators/{uuid}/news/", headers=headers
).json()

Example response:

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "e7a3b2c1-...",
      "date": "2024-06-01",
      "headline": "Meridian Ventures Closes $200M Fund II",
      "body": "Meridian Ventures has held the final close of its second Southeast Asia-focused fund...",
      "source_url": "https://dealstreetasia.com/...",
      "type": {"key": "news_type_closing_fund", "name": "Closing of a Fund"}
    }
  ]
}

News Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -date). Supported: date, headline, body, source_url, type, uuid, created_at, updated_at. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

Funds

Last updated: 5 October 2026

Fund profiles represent individual fund vehicles — each managed by one or more capital allocators. This covers venture capital funds, private equity funds, and other structured vehicles.

Endpoints

MethodEndpointAccess
GET/v3/partners/funds/Subscription required
POST/v3/partners/funds/Subscription required
GET/v3/partners/funds/{uuid}/Subscription required
GET/v3/partners/funds/{uuid}/performance/Subscription required
POST/v3/partners/funds/performance/Subscription required
GET/v3/partners/funds/{uuid}/aum/Subscription required
GET/v3/partners/funds/{uuid}/commitments/Subscription required
GET/v3/partners/funds/{uuid}/investments/Subscription required
GET/v3/partners/funds/{uuid}/news/Subscription required

List Funds

curl "https://api.altdmp.io/v3/partners/funds/?search=Wavemaker" \
  -H "Authorization: Bearer YOUR_TOKEN"
import requests

BASE = "https://api.altdmp.io/v3/partners"
headers = {"Authorization": "Bearer YOUR_TOKEN"}

resp = requests.get(
    f"{BASE}/funds/",
    params={"search": "Wavemaker"},
    headers=headers,
)

Example response:

{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "73541611-0738-46fb-abbd-7e9dcf74b00c",
      "legal_entity": {
        "uuid": "c7cccf8b-93ff-4fbe-b979-a3372ea177f7",
        "display_name": "Wavemaker Pacific 1 Ltd",
        "year_founded": 2017,
        "date_founded": "2017-03-22",
        "date_founded_precision": "day",
        "domicile_country": {"iso_alpha3": "CYM", "name": "Cayman Islands"},
        "trading_status": {"key": "operating", "name": "Operating"},
        "registration_numbers": [
          {"reg_number": "MC-321654", "authority_type": null, "authority_name": null}
        ]
      },
      "display_name": "Wavemaker Pacific 1",
      "description": "Wavemaker Pacific 1 is a $66M early-stage fund focused on B2B and deep tech across Southeast Asia.",
      "fund_managers": [
        {"uuid": "f80754f5-16c7-4aac-9cab-4149285c7220", "display_name": "Wavemaker Partners"}
      ],
      "status": {"key": "fund_status_closed", "name": "Closed"},
      "vintage_year": 2017,
      "is_raising_now": false,
      "last_updated_at": "2025-10-16T07:33:30Z",
      "aggregate_updated_at": "2026-09-24T03:12:45.118204Z"
    }
  ]
}

The list legal_entity has no headquarters. It is the compact variant: uuid, display_name, year_founded, date_founded, date_founded_precision, domicile_country, trading_status, registration_numbers. Geography beyond domicile — including headquarters — only appears on the detail response, even though headquarters_country_iso_alpha3 is filterable on the list.

status, vintage_year, and every nested legal_entity sub-object are nullable. Many fund records in the data set are thinly populated, so expect null for status, vintage_year, domicile_country, and trading_status, and [] for fund_managers.

List Query Parameters

ParameterDescription
searchFull-text search on fund and fund manager names
orderingSort field. Prefix with - for descending. Supported: display_name, vintage_year, term_years, created_at, updated_at, aggregate_updated_at, uuid. Default display_name. An unsupported value returns 400 Bad Request listing the valid fields
limitResults per page
offsetPagination offset

Advanced Filter (POST)

Use POST with a JSON body for complex filters on fund attributes. The body accepts only the filters object; pass search, ordering, limit, and offset as query parameters. filters cannot go the other way — a value in ?filters= returns 400 Bad Request on both GET and POST.

Operators, body shapes, and the supported filter field list for this endpoint are on the Filtering page. Note that eq, ne, in, and nin are case-sensitive on every field here except structure, and that an unrecognized op returns 400 Bad Request.

# Cayman-domiciled funds targeting Southeast Asia at Series A or Series B
curl -X POST https://api.altdmp.io/v3/partners/funds/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq",  "field": "domicile_country_iso_alpha3",      "value": "CYM"},
        {"op": "in",  "field": "preferred_countries_iso_alpha3",   "value": ["SGP", "IDN", "MYS", "PHL", "THA", "VNM"]},
        {"op": "in",  "field": "preferred_allocation_types_names", "value": ["Series A", "Series B"]}
      ]
    }
  }'

# Funds currently raising, domiciled in Singapore
curl -X POST https://api.altdmp.io/v3/partners/funds/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq", "field": "domicile_country_iso_alpha3", "value": "SGP"},
        {"op": "eq", "field": "status_key",                 "value": "fund_status_raising"}
      ]
    }
  }'
# Cayman-domiciled funds targeting SEA at Series A/B
resp = requests.post(
    f"{BASE}/funds/",
    headers=headers,
    json={
        "filters": {
            "all": [
                {"op": "eq",  "field": "domicile_country_iso_alpha3",      "value": "CYM"},
                {"op": "in",  "field": "preferred_countries_iso_alpha3",   "value": ["SGP", "IDN", "MYS", "PHL", "THA", "VNM"]},
                {"op": "in",  "field": "preferred_allocation_types_names", "value": ["Series A", "Series B"]},
            ]
        }
    },
)

Common Filter Fields

FieldTypeExample
uuidstring"0f8b2c11-7d3e-4a6f-8c92-1b5a4e7d9c30" — the fund profile UUID; pair with in to re-fetch a known set
aggregate_updated_atdate-time string"2026-09-24T00:00:00Z": the change watermark. eq, gt, gte, lt, and lte only, with a UTC offset. See Incremental Sync
domicile_country_iso_alpha3string"CYM"
domicile_country_namestring"Cayman Islands"
headquarters_country_iso_alpha3string"SGP"
headquarters_country_namestring"Singapore"
status_keystring"fund_status_raising"
vintage_yearinteger2020
date_foundeddate string"2014-04-01"
preferred_countries_iso_alpha3string"SGP"
preferred_allocation_types_namesstring"Series A"

date_founded filters on the fund’s full founding date and supports gte, lte, range, and eq. The legal_entity object also exposes date_founded and date_founded_precision ("year", "month", or "day") alongside the retained year-only year_founded.


Get Fund Detail

# Wavemaker Pacific 1
curl "https://api.altdmp.io/v3/partners/funds/73541611-0738-46fb-abbd-7e9dcf74b00c/" \
  -H "Authorization: Bearer YOUR_TOKEN"
uuid = "73541611-0738-46fb-abbd-7e9dcf74b00c"
fund = requests.get(f"{BASE}/funds/{uuid}/", headers=headers).json()

The detail response returns everything from the list response — with the fuller legal_entity object — plus terms, fees, preference arrays, fund size, and eight classification flags.

{
  "uuid": "73541611-0738-46fb-abbd-7e9dcf74b00c",
  "legal_entity": {
    "uuid": "c7cccf8b-93ff-4fbe-b979-a3372ea177f7",
    "display_name": "Meridian SEA Fund II Ltd",
    "description": "Cayman-domiciled fund vehicle managed by Meridian Ventures.",
    "year_founded": 2022,
    "date_founded": "2022-01-18",
    "date_founded_precision": "day",
    "is_female_founder": null,
    "domicile_country": {"iso_alpha3": "CYM", "name": "Cayman Islands"},
    "headquarters": {
      "country_name": "Singapore",
      "country_iso_alpha3": "SGP",
      "state_name": null,
      "city_name": "Singapore"
    },
    "trading_status": {"key": "operating", "name": "Operating"},
    "registration_numbers": [
      {"reg_number": "MC-321654", "authority_type": null, "authority_name": null}
    ],
    "alternate_names": [
      {"name": "Meridian SEA II", "type": "Other", "type_key": "alt_name_other"}
    ],
    "website_url": null,
    "email": null,
    "phone": null,
    "founders": [],
    "directors": [{"uuid": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "name": "Jia Wei Tan"}],
    "auditors": [{"uuid": "ad9be164-4e19-4822-987e-54efe73d8974", "name": "Baker Tilly LSC"}]
  },
  "display_name": "Meridian SEA Fund II",
  "description": "Meridian SEA Fund II is a $200M Series A and Series B fund focused on B2B SaaS and FinTech across Southeast Asia.",
  "fund_managers": [
    {"uuid": "f80754f5-16c7-4aac-9cab-4149285c7220", "display_name": "Meridian Ventures Pte. Ltd."}
  ],
  "status": {"key": "fund_status_closed", "name": "Closed"},
  "vintage_year": 2022,
  "is_raising_now": false,
  "last_updated_at": "2025-10-16T07:33:30Z",
  "aggregate_updated_at": "2026-09-24T03:12:45.118204Z",
  "term_years": 10,
  "structure": "Limited Partnership",
  "currency": {"iso_code": "USD", "name": "US Dollar"},
  "target_close_date": "2022-06-30",
  "preferred_countries": [
    {"iso_alpha3": "SGP", "name": "Singapore"},
    {"iso_alpha3": "IDN", "name": "Indonesia"},
    {"iso_alpha3": "VNM", "name": "Vietnam"}
  ],
  "preferred_themes": [
    {"key": "themes_payments", "name": "Payments"},
    {"key": "themes_business_applications_saas", "name": "Business Applications / SaaS"}
  ],
  "preferred_horizontals": [{"key": "horizontals_marketplaces", "name": "Marketplaces"}],
  "preferred_business_models": [
    {"key": "business_models_b2b", "name": "B2B (Business-to-Business)"}
  ],
  "preferred_techs": [{"key": "techs_fintech", "name": "FinTech"}],
  "preferred_allocation_types": [{"key": "equity", "name": "Equity"}],
  "preferred_allocation_subtypes": [
    {"key": "venture_capital", "name": "Venture Capital (VC)"}
  ],
  "preferred_allocation_deal_types": [
    {"key": "series_a", "name": "Series A"},
    {"key": "series_b", "name": "Series B"}
  ],
  "preferred_industries": [
    {"code": "09", "description": "Information and Communication"}
  ],
  "preferred_other_preferences": null,
  "latest_fund_size": {
    "uuid": "da38f125-485d-45ca-b637-73a71f31c8c1",
    "aum_value_date": "2024-12-31",
    "aum_value": 200000000.00,
    "aum_value_usd": 200000000.00,
    "reporting_currency": {"iso_code": "USD", "name": "US Dollar"}
  },
  "is_first_time_fund": false,
  "is_foia_reportable": null,
  "is_captive_fund": null,
  "is_single_deal_fund": false,
  "is_continuation_fund": false,
  "is_hybrid_fund": null,
  "is_balanced_fund": null,
  "is_special_situation_fund": null,
  "management_fee_percentage": 2.00,
  "gp_commitment_percentage": 2.00,
  "hurdle_percentage": 8.00,
  "carry_percentage": 20.00
}

Fund Profile Fields

FieldTypeDescription
uuidstringFund profile UUID
legal_entityobject, nullableThe fund’s registered vehicle. See Data Model
display_namestringFund name
descriptionstring, nullableFund description
fund_managersarrayManaging capital allocator(s) — uuid + display_name. Use the uuid with /capital-allocators/{uuid}/. A manager whose record is unpublished or deleted is not listed
statusobject, nullableFund lifecycle status, e.g. {"key": "fund_status_closed", "name": "Closed"}
vintage_yearinteger, nullableYear the fund was established / first close
is_raising_nowboolean, nullableCurrently in fundraise
last_updated_atdate-time stringDeprecated, use aggregate_updated_at. The later of when the fund profile and its registered vehicle were last edited. See last_updated_at is deprecated
aggregate_updated_atdate-time string, nullableWhen the record, or anything shown on it, last changed. Filter and sort on it to sync incrementally; see Incremental Sync
term_yearsinteger, nullableFund term in years
structurestring, nullableFree-text legal structure, e.g. "Limited Partnership", "Commingled", "Closed-end", "Separately Managed", "Variable Capital Company". Not a {key, name} object and not drawn from a fixed enum
currencyobject, nullableThe fund’s denomination currency: iso_code + name
target_close_datedate string, nullableTarget final-close date
preferred_countries / preferred_themes / preferred_horizontals / preferred_business_models / preferred_techs / preferred_allocation_types / preferred_allocation_subtypes / preferred_allocation_deal_types / preferred_industriesarrayStated mandate. Empty arrays when nothing is declared
preferred_other_preferencesstring, nullableFree-text mandate notes
latest_fund_sizeobject, nullableMost recent AUM record: uuid, aum_value_date, aum_value, aum_value_usd, reporting_currency
is_first_time_fund / is_foia_reportable / is_captive_fund / is_single_deal_fund / is_continuation_fund / is_hybrid_fund / is_balanced_fund / is_special_situation_fundboolean, nullableClassification flags. null means unclassified — not false
management_fee_percentage / gp_commitment_percentage / hurdle_percentage / carry_percentagenumber, nullablePercentages, so 2.00 means 2% — not 0.02. See Field Types and Nulls

The list response returns only the first nine fields in this table; everything from term_years down is detail-only.

Real Funds for Testing

FundUUIDSizeVintage
Wavemaker Pacific 173541611-0738-46fb-abbd-7e9dcf74b00c$66M2017
Bain Capital Asia III50280c30-d626-44db-8aff-7cdf1eb323bb$3B2016

Fund Performance

Returns quarterly / annual performance metrics (IRR, DPI, TVPI). Each row is one reporting period.

curl "https://api.altdmp.io/v3/partners/funds/50280c30-d626-44db-8aff-7cdf1eb323bb/performance/" \
  -H "Authorization: Bearer YOUR_TOKEN"
performance = requests.get(
    f"{BASE}/funds/50280c30-d626-44db-8aff-7cdf1eb323bb/performance/",
    headers=headers,
).json()

Example performance row (Bain Capital Asia III, Q4 2024):

{
  "uuid": "876b7ca5-f4f3-4262-81bb-a33874b2e199",
  "fund_uuid": "50280c30-d626-44db-8aff-7cdf1eb323bb",
  "date": "2024-12-31",
  "year": 2024,
  "quarter": "Q4",
  "source": "Limited Partner",
  "source_type_key": "fund_per_source_type_lp",
  "source_capital_allocator_uuid": "f80754f5-16c7-4aac-9cab-4149285c7220",
  "currency": {"iso_code": "USD", "name": "US Dollar"},
  "source_url": null,
  "irr": 0.1800,
  "irr_pct": 18.00,
  "dpi": 1.3000,
  "rvpi": 0.8000,
  "net_multiple": 2.1000,
  "profit_usd": 540000000.00,
  "drawdowns_usd": 2800000000.00,
  "committed_capital_usd": 3000000000.00,
  "retained_earnings_usd": null,
  "dividends_usd": null,
  "net_assets_usd": null,
  "carry_unrealised_usd": null,
  "carry_realised_usd": null,
  "cashflow_usd": null,
  "share_redemption_usd": null,
  "distributions_usd": null,
  "created_at": "2026-06-23T04:25:42.261489Z",
  "last_updated_at": "2025-01-15T10:22:00Z"
}

Performance Fields

FieldTypeDescription
uuidstringPerformance record UUID
fund_uuidstring, nullableThe fund this row belongs to. Returned on the per-fund endpoint as well as the batch endpoint
datedate stringReporting-period end date
year / quarterinteger / string, nullableReporting period, e.g. 2024 and "Q4"
sourcestring, nullableData source display name — one of "Financial Statement", "Fund Manager", or "Limited Partner"
source_type_keystring, nullableMachine-readable source type — "fund_per_source_type_fs", "fund_per_source_type_fm", or "fund_per_source_type_lp"
source_capital_allocator_uuidstring, nullableThe allocator that reported the figures, when the source is an LP. null when that allocator’s record is unpublished or deleted; the performance record itself is still returned
currencyobject, nullableReporting currency of the as-reported performance data; the _usd fields are always USD-normalized regardless of this value
source_urlstring, nullableLink to the source document, when public
irrnumber, nullableInternal Rate of Return as a fraction — 0.1800 means 18%
irr_pctnumber, nullableThe same figure as a percentage — 18.00 means 18%. Always exactly irr × 100, and null whenever irr is
dpinumber, nullableDistributions to Paid-In multiple. A multiple, not a percentage: 1.3000 means 1.3×
rvpinumber, nullableResidual Value to Paid-In multiple
net_multiplenumber, nullableTotal Value to Paid-In (TVPI)
profit_usd / drawdowns_usd / committed_capital_usd / retained_earnings_usd / dividends_usd / net_assets_usd / carry_unrealised_usd / carry_realised_usd / cashflow_usd / share_redemption_usd / distributions_usdnumber, nullableUSD-normalized amounts
created_at / last_updated_atdate-time stringWhen the record was first created and last changed

irr did not change scale — it was a fraction before 2026-08-14 and still is. What changed is that it is now a JSON number rather than a quoted string, and irr_pct was added beside it. Read irr_pct if you want the percentage; there is no need to multiply by 100 yourself. dpi, rvpi, and net_multiple are multiples and get no _pct sibling — a dpi of 1.30 is 1.3×, not 130%.

Coverage is sparse beyond the headline metrics. Expect null for most of the cash-flow fields (retained_earnings_usd through distributions_usd) on any given row.

Query Parameters

ParameterDescription
orderingSort field. Supported values: date, irr, irr_pct, dpi, rvpi, net_multiple (prefix with - for descending, default: -date). irr and irr_pct sort identically. Anything else returns 400 — see Sorting
irr_min / irr_maxRange filter on IRR, as a fraction — irr_min=0.15 means 15%. There is no irr_pct_min; passing irr_min=15 returns an empty page rather than an error
dpi_min / dpi_maxRange filter on DPI
rvpi_min / rvpi_maxRange filter on RVPI
net_multiple_min / net_multiple_maxRange filter on net multiple
limitResults per page
offsetPagination offset

Rows where the filtered metric is null are excluded from every range filter above. Rows with no value in the sort field come last in either direction, so -irr puts records with no IRR at the end. Until September 2026 they came first under a descending sort.

Batch Performance via POST

Use POST /v3/partners/funds/performance/ (note: no {uuid} in the path) to pull performance records for many funds in one paginated request instead of calling the per-fund .../{uuid}/performance/ endpoint once per fund. The body must contain a non-empty fund_uuid in filter; up to 1,000 UUIDs are accepted per request. Each returned row carries a fund_uuid field so you can join it back to the fund it belongs to.

curl -X POST "https://api.altdmp.io/v3/partners/funds/performance/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "in", "field": "fund_uuid", "value": [
          "50280c30-d626-44db-8aff-7cdf1eb323bb",
          "3fa85f64-5717-4562-b3fc-2c963f66afa6"
        ]}
      ]
    }
  }'

Rows are ordered by most recent reporting period first and use the same shape as the per-fund performance endpoint, including the fund_uuid join field.


AUM History

Returns a time series of fund size records.

curl "https://api.altdmp.io/v3/partners/funds/73541611-0738-46fb-abbd-7e9dcf74b00c/aum/" \
  -H "Authorization: Bearer YOUR_TOKEN"
aum = requests.get(f"{BASE}/funds/{uuid}/aum/", headers=headers).json()

Example response:

{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "a088fa5b-a023-49dc-b1c5-a162bea5a055",
      "aum_value_date": "2024-12-31",
      "aum_value": 66000000.00,
      "aum_value_usd": 66000000.00,
      "reporting_currency": {"iso_code": "USD", "name": "US Dollar"}
    }
  ]
}

aum_value is the fund size in reporting_currency; aum_value_usd is the same figure normalized to USD. Both are numbers. The most recent record here is also surfaced as latest_fund_size on the fund detail response.

Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -aum_value_date). Supported: aum_value_date, aum_value, aum_value_usd, reporting_currency, uuid, created_at, updated_at. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

LP Commitments

Returns LP commitments into this fund — who invested and how much.

curl "https://api.altdmp.io/v3/partners/funds/50280c30-d626-44db-8aff-7cdf1eb323bb/commitments/" \
  -H "Authorization: Bearer YOUR_TOKEN"
commitments = requests.get(
    f"{BASE}/funds/50280c30-d626-44db-8aff-7cdf1eb323bb/commitments/",
    headers=headers,
).json()

Example commitment row (Bain Capital Asia III LP):

{
  "uuid": "449caf29-9825-453c-88ef-ddfffcbb6ca6",
  "date": "2016-08-15",
  "allocation_type_key": "allocation_type_commitment",
  "allocation_type_name": "Commitment",
  "allocation_subtype_key": "allocation_subtype_commitment",
  "allocation_subtype_name": "Commitment",
  "investment_amount_usd": 50000000.00,
  "transactions": [
    {
      "uuid": "af9bbee4-c09e-4d4d-be6a-124b3d4b034f",
      "investment_amount_usd": 50000000.00,
      "buyer": {
        "uuid": "dc8b6720-5e9e-4306-b5fc-4884c8f94e4b",
        "name": "GIC Private Limited",
        "type_key": "capital_allocator",
        "type": "CapitalAllocatorProfile"
      }
    }
  ]
}

Response Fields

FieldTypeDescription
uuidstringCommitment UUID
datedate stringCommitment date
allocation_type_key / allocation_type_namestring, nullableAllocation type
allocation_subtype_key / allocation_subtype_namestring, nullableAllocation subtype
investment_amount_usdnumber, nullableTotal committed amount in USD
transactionsarrayUnderlying transactions, one per committing LP. May be empty.
transactions[].uuidstringTransaction UUID
transactions[].investment_amount_usdnumber, nullableCommitted amount for this transaction in USD
transactions[].buyerobject, nullableThe committing party (LP): exactly uuid, name, type_key, and type — no registration numbers here, unlike the buyer/seller objects on deals. null when no buyer is recorded

transactions[].buyer.type_key tells you what kind of entity the LP is — one of capital_allocator, fund, capital_receiver, legal_entity, person, or shareholder_group — and uuid is its ID in that namespace. Join on type_key, not on the type label; see routing by type_key.

This is the fund-side view. The allocator-side feed at /capital-allocators/{uuid}/commitments/ covers the same relationship from the other direction but returns a different shape — a nested fund object and cash_value_transacted* amounts instead of transactions[] and investment_amount_usd.

A transaction whose LP (buyer) or seller is unpublished or deleted is left out of transactions, and a deal left with no visible transactions is left out of the results and count. A deal with no transactions, or with a transaction whose buyer is not recorded, stays. investment_amount_usd is the deal’s recorded figure and does not change when a transaction is hidden.

Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -date). Supported: date, investment_amount_usd, created_at, updated_at. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

Portfolio Investments

Returns aggregated investment data for companies this fund has invested in, including investments made through child entities (SPVs, etc.) in the ownership hierarchy.

curl "https://api.altdmp.io/v3/partners/funds/73541611-0738-46fb-abbd-7e9dcf74b00c/investments/" \
  -H "Authorization: Bearer YOUR_TOKEN"
investments = requests.get(f"{BASE}/funds/{uuid}/investments/", headers=headers).json()

Example response:

{
  "count": 14,
  "next": null,
  "previous": null,
  "results": [
    {
      "capital_receiver_uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
      "legal_entity_uuid": "fee788ac-cffe-46f0-9bb5-fedb62992bd8",
      "capital_receiver_name": "Shopback",
      "shares_issued": 1500000.0,
      "shares_bought_secondary": 0.0,
      "shares_sold": 0.0,
      "shares_currently_held": 1500000.0,
      "total_invested_usd": 7500000.00,
      "first_investment_date": "2019-04-30",
      "latest_investment_date": "2022-03-01"
    }
  ]
}

Response Fields

FieldTypeDescription
capital_receiver_uuidstringUUID of the capital receiver profile — use with /capital-receivers/{uuid}/
legal_entity_uuidstringUUID of the underlying legal entity
capital_receiver_namestringCompany name
shares_issuednumberShares acquired via primary transactions
shares_bought_secondarynumberShares acquired via secondary purchases
shares_soldnumberShares sold via secondary transactions
shares_currently_heldnumberCurrent net shareholding
total_invested_usdnumberTotal cash invested in USD (primary transactions only)
first_investment_datedateDate of first investment
latest_investment_datedateDate of most recent investment

Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -latest_investment_date). Supported: latest_investment_date, first_investment_date, capital_receiver_name, shares_issued, shares_bought_secondary, shares_sold, shares_currently_held, total_invested_usd. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

News

Returns paginated news articles linked to this fund.

curl "https://api.altdmp.io/v3/partners/funds/73541611-0738-46fb-abbd-7e9dcf74b00c/news/" \
  -H "Authorization: Bearer YOUR_TOKEN"
news = requests.get(f"{BASE}/funds/{uuid}/news/", headers=headers).json()

Example response:

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "b3c4d5e6-...",
      "date": "2022-06-30",
      "headline": "Wavemaker Pacific Closes $60M Fund at Final Close",
      "body": "Wavemaker Partners has announced the final close of Wavemaker Pacific 1...",
      "source_url": "https://dealstreetasia.com/...",
      "type": {"key": "news_type_closing_fund", "name": "Closing of a Fund"}
    }
  ]
}

News Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -date). Supported: date, headline, body, source_url, type, uuid, created_at, updated_at. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

Investors

Last updated: 5 October 2026

The Investors endpoint is a unified search across every entity that has been a buyer in a transaction — companies, individuals, and shareholder groups — in a single query. Use it when you want investment totals for an investor and don’t know which kind of entity it is.

This endpoint reports base entity types, not profiles. Every row’s investor_type is legal_entity, person, or shareholder_group — it never resolves a firm to its capital allocator or fund profile. Filtering ?investor_type=capital_allocator or ?investor_type=fund is accepted but always returns zero results. For allocator- and fund-level views, use Capital Allocators and Funds; for a company’s investors resolved to their profiles, use /capital-receivers/{uuid}/investors/.

Endpoints

MethodEndpointAccess
GET/v3/partners/investors/Subscription required
POST/v3/partners/investors/Subscription required

List Investors

Each result row represents one investor × allocation type combination. An investor with both equity and debt investments appears as two separate rows.

curl "https://api.altdmp.io/v3/partners/investors/?search=Wavemaker" \
  -H "Authorization: Bearer YOUR_TOKEN"
import requests

BASE = "https://api.altdmp.io/v3/partners"
headers = {"Authorization": "Bearer YOUR_TOKEN"}

resp = requests.get(
    f"{BASE}/investors/",
    params={"search": "Wavemaker"},
    headers=headers,
)

Example response:

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "name": "Wavemaker Group",
      "investor_type": "legal_entity",
      "uuid": "f80754f5-16c7-4aac-9cab-4149285c7220",
      "allocation_type_key": "equity",
      "allocation_type_name": "Equity",
      "total_invested_usd": 87500000.0,
      "no_of_invested_companies": 47,
      "investments_by_deal_type": {
        "seed": {
          "amount_invested_usd": 12000000.0,
          "no_of_invested_companies": 18
        },
        "series_a": {
          "amount_invested_usd": 35000000.0,
          "no_of_invested_companies": 19
        },
        "series_b": {
          "amount_invested_usd": 40500000.0,
          "no_of_invested_companies": 10
        }
      }
    }
  ]
}

Response Fields

FieldTypeDescription
uuidstringUUID of the investor entity
namestringInvestor display name
investor_typestringBase entity type — legal_entity, person, or shareholder_group only. This is a key, not a label: it is stable and safe to join and filter on, and there is no separate investor_type_key
allocation_type_keystring, nullableAsset class key: equity, debt, allocation_type_commitment, or allocation_type_other
allocation_type_namestring, nullableAsset class display name: Equity, Debt, Commitment, Other
total_invested_usdnumber, nullableTotal primary capital deployed (USD), within whatever country or startup scope the request applies
no_of_invested_companiesintegerDistinct companies invested in
investments_by_deal_typeobjectBreakdown keyed by deal-stage key, each value {amount_invested_usd, no_of_invested_companies}. Only stages the investor has actually invested in appear — the object is sparse, so read it with .get() rather than assuming a fixed key set

There are no investment dates on this endpoint. Unlike the per-company /capital-receivers/{uuid}/investors/ rows, an investor row here carries no first_investment_date or latest_investment_date. Use invested_on_from / invested_on_to to filter by date, or call the per-company endpoint when you need the actual dates.

The allocation_type_key values are not uniformly prefixed: equity and debt are bare, while commitment and other carry an allocation_type_ prefix. Match on the exact strings above, or read them from reference data.


Query Parameters

ParameterDescription
searchFull-text search on investor name
investor_typeFilter by base entity type: legal_entity, person, or shareholder_group. capital_allocator and fund are accepted but match nothing
invested_in_stageFilter by deal-stage key (e.g. seed, series_a) — the same keys as investments_by_deal_type
invested_on_fromInvestments on or after this date (YYYY-MM-DD)
invested_on_toInvestments on or before this date (YYYY-MM-DD)
capital_receiver_headquarters_country_iso_alpha3Scope to investors in portfolio companies headquartered in these countries (comma-separated ISO alpha-3, e.g. SGP,MYS)
capital_receiver_domicile_country_iso_alpha3Scope to investors in portfolio companies domiciled in these countries (comma-separated ISO alpha-3)
capital_receiver_is_startuptrue or false (any case). Scope to investments in startups, or in companies that are not startups. See below
orderingSort field, prefix with - for descending: name (the default), investor_type, allocation_type_key, allocation_type_name, total_invested_usd, no_of_invested_companies. Ties break by name, so pages never skip or repeat a row. Takes one field at a time: anything else, including a comma-separated list, returns 400 Bad Request listing these fields
limitResults per page
offsetPagination offset

Filtering Examples

# All equity investors who made a Seed investment after 2022
curl "https://api.altdmp.io/v3/partners/investors/?invested_in_stage=seed&invested_on_from=2022-01-01" \
  -H "Authorization: Bearer YOUR_TOKEN"

# All investor types matching "Temasek"
curl "https://api.altdmp.io/v3/partners/investors/?search=Temasek" \
  -H "Authorization: Bearer YOUR_TOKEN"
# Seed investors after 2022
resp = requests.get(
    f"{BASE}/investors/",
    params={"invested_in_stage": "seed", "invested_on_from": "2022-01-01"},
    headers=headers,
)

# Search for Temasek
resp = requests.get(f"{BASE}/investors/", params={"search": "Temasek"}, headers=headers)

Advanced Filter (POST)

Supports the same fields as the GET query parameters, passed as a filters.all array in the request body. The body accepts only the filters object; pass search, ordering, limit, and offset as query parameters — sending them in the body returns 400 Bad Request.

This endpoint’s filter body is narrower than the other list endpoints. It supports a flat filters.all list of the field/operator pairs below and rejects anything else with 400 Bad Request and a message naming what it can accept. In particular any and not are not available here — combine your conditions in all, or issue separate requests.

filters must be a JSON object. A list or scalar — {"filters": ["any"]} — returns 400 Bad Request rather than applying no filter; omit filters, or send null or {}, to get the unfiltered list. Each entry in all must be an object, and the request body itself must be a JSON object rather than an array.

A ?filters= query parameter is ignored here, not rejected. Elsewhere it returns 400 Bad Request; on this endpoint the value is dropped and you get the complete unfiltered list with a 200. Always send the filter in the request body — and do not treat a 200 from this endpoint as confirmation that a query-string filter was applied.

Supported field and operator combinations:

FieldOperators
investor_typeeq
nameeq, contains
searcheq, contains
invested_in_stageeq
invested_on_fromeq, gte
invested_on_toeq, lte
capital_receiver_headquarters_country_iso_alpha3eq, in
capital_receiver_domicile_country_iso_alpha3eq, in
capital_receiver_is_startupeq, with a JSON boolean

name matches as a substring under both eq and contains. {"op": "eq", "field": "name", "value": "Wavemaker"} returns every investor whose name contains “Wavemaker”, not just an exact match — pass the full name to narrow to one. A list value is rejected, so filter one name per request.

Investor Filter Fields

FieldTypeExample
investor_typestring"legal_entity", "person", or "shareholder_group" — exact match
namestring"Temasek" — substring match under eq and contains
invested_in_stagestring"seed", "series_a"
invested_on_fromdate string"2020-01-01"
invested_on_todate string"2024-12-31"
capital_receiver_headquarters_country_iso_alpha3list["SGP", "MYS"]
capital_receiver_domicile_country_iso_alpha3list["SGP"]
capital_receiver_is_startupbooleantrue

The two capital_receiver_*_country_iso_alpha3 filters scope the investor list to those who have transacted in portfolio companies headquartered in — or domiciled in — the given countries. In a POST body use op: in with a list (or a single scalar) value. When both filters are supplied together they AND — an investor must satisfy both constraints.

Startup scope

capital_receiver_is_startup scopes by the company invested in, using the startup rule shared with the other startup filters:

An investor in both kinds of company appears under each value, with different totals. investments_by_deal_type is not narrowed by this filter, just as it is not narrowed by the country filters, so it can include companies the totals leave out.

# Investors in startups headquartered in Singapore or Malaysia
curl "https://api.altdmp.io/v3/partners/investors/?capital_receiver_headquarters_country_iso_alpha3=SGP,MYS&capital_receiver_is_startup=true" \
  -H "Authorization: Bearer YOUR_TOKEN"

On GET the value must be true or false in any case; anything else, an empty value included, returns 400. In a POST body it must be a JSON boolean — the string "true" returns 400.

# Company investors whose name contains "Wavemaker"
curl -X POST https://api.altdmp.io/v3/partners/investors/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq", "field": "investor_type", "value": "legal_entity"},
        {"op": "contains", "field": "name", "value": "Wavemaker"}
      ]
    }
  }'

This returns every investor whose name contains “Wavemaker”. To target one, pass its full name; to cover several, issue one request each.

resp = requests.post(
    f"{BASE}/investors/",
    headers=headers,
    json={
        "filters": {
            "all": [
                {"op": "eq", "field": "investor_type", "value": "legal_entity"},
                {"op": "contains", "field": "name", "value": "Wavemaker"},
            ]
        }
    },
)

What uuid identifies depends on investor_type.

  • person — a Person UUID. Pass it straight to /people/{uuid}/.
  • legal_entity — a Legal Entity UUID, not a profile UUID. It returns 404 on /capital-allocators/{uuid}/, /funds/{uuid}/, and /capital-receivers/{uuid}/, and there is no /legal-entities/{uuid}/ endpoint in the Partner API. To reach a profile, search that entity by name or registration number on the relevant profile list endpoint and match on legal_entity.uuid.
  • shareholder_group — a grouping construct with no detail endpoint. name, uuid, and the investment totals on this row are all the API exposes.

People

Last updated: 5 October 2026

The People resource covers founders, directors, employees, and other individuals linked to companies or funds.

Endpoints

MethodEndpointAccess
GET/v3/partners/people/Subscription required
POST/v3/partners/people/Subscription required
GET/v3/partners/people/{uuid}/Subscription required
GET/v3/partners/people/{uuid}/roles/Subscription required
POST/v3/partners/people/roles/Subscription required
GET/v3/partners/people/{uuid}/investments/Subscription required
GET/v3/partners/people/{uuid}/news/Subscription required

List People

# All founders at Singapore companies
curl "https://api.altdmp.io/v3/partners/people/?role_type_key=person_association_founder" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Directors named "Chan"
curl "https://api.altdmp.io/v3/partners/people/?role_type_key=person_association_director&search=Chan" \
  -H "Authorization: Bearer YOUR_TOKEN"
import requests

BASE = "https://api.altdmp.io/v3/partners"
headers = {"Authorization": "Bearer YOUR_TOKEN"}

# All founders
founders = requests.get(
    f"{BASE}/people/",
    params={"role_type_key": "person_association_founder"},
    headers=headers,
).json()

# Directors named Chan
directors = requests.get(
    f"{BASE}/people/",
    params={"role_type_key": "person_association_director", "search": "Chan"},
    headers=headers,
).json()

Example response:

{
  "count": 3,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "f2eac778-a47b-497e-a4c6-ddd71335a404",
      "display_name": "Shanru Lai",
      "given_name": "Shanru",
      "family_name": "Lai",
      "location_country_name": "Singapore",
      "email": "shanru@shopback.com",
      "linkedin_url": "https://linkedin.com/in/shanrulai",
      "last_updated_at": "2025-10-16T07:33:30Z",
      "aggregate_updated_at": "2026-09-24T03:12:45.118204Z"
    }
  ]
}

List Query Parameters

ParameterDescription
searchName search across given and family name. Multiple words are matched independently, so full names like Shanru Lai match regardless of word order.
role_type_keyFilter by role: person_association_founder, person_association_director, person_association_employee
domicile_country_iso_alpha3Filter by the domicile-country ISO alpha-3 code of an associated organization. Repeatable or comma-separated (e.g. SGP,USA).
headquarters_country_iso_alpha3Filter by the headquarters-country ISO alpha-3 code of an associated organization. Repeatable or comma-separated.
organization_is_startuptrue or false (any case). Filter to people with a role at a startup, or with a role that is not at one. See Startup filter
orderingSort field: family_name, given_name, date_of_birth, created_at, updated_at, aggregate_updated_at, or uuid, with a - prefix for descending. Default family_name, then given_name. aggregate_updated_at sorts people with no value last in either direction. Comma-separated lists such as aggregate_updated_at,uuid are accepted. Anything else returns 400 Bad Request listing these fields
limitResults per page
offsetPagination offset

The role and country parameters match only visible roles: a role that is deleted or unpublished, or whose organization is unpublished or deleted, never makes a person match. A deleted headquarters location reads as no headquarters. A person with no visible roles still appears in an unfiltered list.

Startup filter

organization_is_startup scopes people by whether they hold a role at a startup:

The split is by role, so a person with roles on both sides matches both. Only companies can be startups, so with true a person linked only to an investor or a service provider drops out.

When you send organization_is_startup, every role and country parameter must hold on the same role. So role_type_key=person_association_director&headquarters_country_iso_alpha3=SGP,MYS&organization_is_startup=true returns directors of startups headquartered in Singapore or Malaysia, not directors of any company who also hold some other role at a startup. Without it, each parameter can match a different role of the same person, as before.

# Directors of startups headquartered in Singapore or Malaysia
curl "https://api.altdmp.io/v3/partners/people/?role_type_key=person_association_director&headquarters_country_iso_alpha3=SGP,MYS&organization_is_startup=true" \
  -H "Authorization: Bearer YOUR_TOKEN"

The value must be true or false, in any case. Anything else — an empty value, or the parameter sent twice — returns 400 Bad Request. It works on GET and POST /people/ and on POST /people/roles/; the detail routes ignore it.


Advanced Filter (POST)

Use POST with a JSON body to filter people by name or location. The body accepts only the filters object; pass search, role_type_key, domicile_country_iso_alpha3, headquarters_country_iso_alpha3, organization_is_startup, ordering, limit, and offset as query parameters. filters cannot go the other way — a value in ?filters= returns 400 Bad Request on both GET and POST.

Operators, body shapes, and the supported filter field list for this endpoint are on the Filtering page. Note that eq, ne, in, and nin match given_name, family_name, and the two location_country_* fields regardless of case, and that an unrecognized op returns 400 Bad Request. A person’s location that has been deleted reads as no location: location_country_name is null and the location filters do not match through it.

People Filter Fields

FieldTypeExample
uuidstring"7c1f9e02-3b4d-4a58-9f21-6d0e5b8c3a41" — the person UUID; pair with in to re-fetch a known set
given_namestring"Henry"
family_namestring"Chan"
location_country_namestring"Singapore"
location_country_iso_alpha3string"SGP"
date_of_birthdate string"1985-04-12"
aggregate_updated_atdate-time string"2026-09-24T00:00:00Z": the change watermark. eq, gt, gte, lt, and lte only, with a UTC offset. See Incremental Sync

These fields are the whole filter surface. Any other name returns 400 Bad Request naming the field — including four undocumented names that used to be accepted and then ignored: nationality_name, nationality_iso_alpha3, is_employee, and associated_ca_types. Nationality and employment status are not filterable on this endpoint, and a filter naming one has never narrowed a result set. See Filtering → Supported Filter Fields.

role_type_key, role_type_name, and organization_is_startup are query parameters, not filter-body fields. Sending any of them inside filters returns 400 Bad Request. Role filtering is combined with the body filters, so pass the role on the query string and the rest in the body — as below.

# Founders based in Singapore — role on the query string, location in the body
curl -X POST "https://api.altdmp.io/v3/partners/people/?role_type_key=person_association_founder" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "eq", "field": "location_country_iso_alpha3", "value": "SGP"}
      ]
    }
  }'
resp = requests.post(
    f"{BASE}/people/",
    headers=headers,
    params={"role_type_key": "person_association_founder"},
    json={
        "filters": {
            "all": [
                {"op": "eq", "field": "location_country_iso_alpha3", "value": "SGP"},
            ]
        }
    },
)

Get Person Detail

# ShopBack co-founder Shanru Lai
curl "https://api.altdmp.io/v3/partners/people/f2eac778-a47b-497e-a4c6-ddd71335a404/" \
  -H "Authorization: Bearer YOUR_TOKEN"
uuid = "f2eac778-a47b-497e-a4c6-ddd71335a404"
person = requests.get(f"{BASE}/people/{uuid}/", headers=headers).json()

Example response:

{
  "uuid": "f2eac778-a47b-497e-a4c6-ddd71335a404",
  "display_name": "Shanru Lai",
  "given_name": "Shanru",
  "family_name": "Lai",
  "location_country": {"iso_alpha3": "SGP", "name": "Singapore"},
  "nationality": {"iso_alpha3": "SGP", "name": "Singapore"},
  "date_of_birth": null,
  "biography": null,
  "email": null,
  "linkedin_url": "https://www.linkedin.com/in/shanrulai",
  "last_updated_at": "2026-07-14T12:22:57.051103Z",
  "aggregate_updated_at": "2026-09-02T04:18:39.640211Z"
}
FieldTypeDescription
uuidstringPerson UUID
display_namestringFull name as displayed
given_name / family_namestring, nullableName parts
location_countryobject, nullableWhere the person is based — {iso_alpha3, name}. Note the list endpoint returns a flat location_country_name string instead of this object
nationalityobject, nullable{iso_alpha3, name}
date_of_birthdate string, nullableRarely populated
biographystring, nullableFree-text biography
email / linkedin_urlstring, nullableContact details
last_updated_atdate-time stringDeprecated, use aggregate_updated_at. When the person’s own record was last edited. See last_updated_at is deprecated
aggregate_updated_atdate-time string, nullableWhen the record, or anything shown on it, last changed. Filter and sort on it to sync incrementally; see Incremental Sync

The list and detail responses differ on two fields: the list returns location_country_name (a flat string) and no nationality, date_of_birth, or biography; the detail returns location_country as an object plus the three extra fields.


Roles

Returns all organizational roles held by a person across their career.

curl "https://api.altdmp.io/v3/partners/people/f2eac778-a47b-497e-a4c6-ddd71335a404/roles/" \
  -H "Authorization: Bearer YOUR_TOKEN"
roles = requests.get(f"{BASE}/people/{uuid}/roles/", headers=headers).json()

Example role row:

{
  "uuid": "f243d7ca-8386-436c-bb22-2c5c9c19f177",
  "person_uuid": "f2eac778-a47b-497e-a4c6-ddd71335a404",
  "role_type": {
    "key": "person_association_founder",
    "name": "Founder"
  },
  "organization": {
    "uuid": "fee788ac-cffe-46f0-9bb5-fedb62992bd8",
    "display_name": "Shopback",
    "profile_type_key": "legal_entity",
    "profile_type": "legalentity",
    "registration_numbers": [
      {"reg_number": "201411189G", "authority_type": "UEN"}
    ]
  },
  "title": "Co-founder & CEO",
  "start_date": "2014-01-01",
  "end_date": null,
  "is_current": true,
  "email": null,
  "linkedin_url": null
}

Response Fields

FieldTypeDescription
uuidstringRole UUID
person_uuidstringThe person holding the role. Returned on the per-person endpoint as well as the batch endpoint
role_typeobject, nullable{key, name} — see Role Type Keys
organizationobject, nullableThe organization: uuid, display_name, profile_type_key, profile_type, plus type-dependent extras. See below
titlestring, nullableJob title as recorded
start_date / end_datedate string, nullableRole dates. Both are commonly null
is_currentbooleanDerived from end_date being null
email / linkedin_urlstring, nullableRole-scoped contact details

The organization object

organization always carries uuid, display_name, profile_type_key, and profile_type, and gains extra keys depending on what the role is attached to. Branch on profile_type_key — it is the same vocabulary used by type_key everywhere else in the API, whereas the profile_type label is spelled differently here than on capital allocator profiles and will change in a future release.

profile_type_keyprofile_typeExtra keys
legal_entitylegalentityregistration_numbers — a list of {reg_number, authority_type}
capital_allocatorcapitalallocatorprofilelegal_entity — {uuid, display_name, registration_numbers} for the allocator’s backing company, when it has one
service_providerserviceproviderprofilelegal_entity — same shape as above
capital_receivercapitalreceiverprofilenone
fundfundprofilenone
nullout_of_scopenone. uuid is null and display_name is the raw company name

Most roles return legal_entity. Note the last row: out_of_scope means the organization isn’t tracked on the platform, so it is not a kind of entity and gets no key — a null profile_type_key tells you there is nothing to join to.

organization.registration_numbers entries have no authority_name. They carry only reg_number and authority_type — narrower than the registration_numbers returned everywhere else in the API.

Roles Query Parameters

ParameterDescription
role_type_key / role_type_nameFilter to specific role types
domicile_country_iso_alpha3Filter by the domicile-country ISO alpha-3 code of the associated organization. Repeatable or comma-separated.
headquarters_country_iso_alpha3Filter by the headquarters-country ISO alpha-3 code of the associated organization. Repeatable or comma-separated.

Roles come back by end date, then start date, then role type, with a fixed tiebreak so pages never skip or repeat a row. The order is fixed: this route takes no ordering, and any value returns 400. GET /people/{uuid}/roles/ does not take organization_is_startup; the batch endpoint does.

Only visible roles are returned. A role that is deleted or unpublished, or whose organization is unpublished, deleted, or no longer exists, is left out. A role at an organization that isn’t tracked on the platform (out_of_scope above) is returned as normal.

Role Type Keys

These are the only three role types. The name column is the exact value returned in role_type.name.

KeyName
person_association_founderFounder
person_association_directorDirector
person_association_employeeEmployee

end_date: null means the person is currently in the role.

Batch Roles via POST

Use POST /v3/partners/people/roles/ to fetch roles for many people in one paginated request instead of calling the per-person GET .../{uuid}/roles/ endpoint once per person. The body must contain a non-empty person_uuid in filter; up to 1,000 UUIDs are accepted per request. Each returned row carries a person_uuid field so you can join it back to the person it belongs to.

curl -X POST "https://api.altdmp.io/v3/partners/people/roles/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "all": [
        {"op": "in", "field": "person_uuid", "value": [
          "f2eac778-a47b-497e-a4c6-ddd71335a404",
          "a1b2c3d4-0000-0000-0000-000000000002"
        ]}
      ]
    }
  }'

The role_type_key, role_type_name, domicile_country_iso_alpha3, and headquarters_country_iso_alpha3 query parameters also apply, letting you narrow a batch to, say, only current directors. So does organization_is_startup: true returns the roles at startup companies and false every other role, so the two together return every role. A person who is unpublished or deleted returns no rows, the same 200 as a UUID that matches nobody. Response rows use exactly the same shape as the per-person roles endpoint — person_uuid is present on both.


Personal Investments

Returns direct investments made by this person.

curl "https://api.altdmp.io/v3/partners/people/f2eac778-a47b-497e-a4c6-ddd71335a404/investments/" \
  -H "Authorization: Bearer YOUR_TOKEN"
investments = requests.get(f"{BASE}/people/{uuid}/investments/", headers=headers).json()

Example response:

{
  "count": 3,
  "next": null,
  "previous": null,
  "results": [
    {
      "capital_receiver_uuid": "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67",
      "legal_entity_uuid": "fee788ac-cffe-46f0-9bb5-fedb62992bd8",
      "capital_receiver_name": "Shopback",
      "shares_issued": 500000.0,
      "shares_bought_secondary": 0.0,
      "shares_sold": 0.0,
      "shares_currently_held": 500000.0,
      "total_invested_usd": 250000.00,
      "first_investment_date": "2014-01-01",
      "latest_investment_date": "2014-01-01"
    }
  ]
}

Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -latest_investment_date). Supported: latest_investment_date, first_investment_date, capital_receiver_name, shares_issued, shares_bought_secondary, shares_sold, shares_currently_held, total_invested_usd. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

News

Returns paginated news articles linked to this person.

curl "https://api.altdmp.io/v3/partners/people/f2eac778-a47b-497e-a4c6-ddd71335a404/news/" \
  -H "Authorization: Bearer YOUR_TOKEN"
news = requests.get(f"{BASE}/people/{uuid}/news/", headers=headers).json()

Example response:

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "a1b2c3d4-...",
      "date": "2023-03-10",
      "headline": "ShopBack Co-founder Shanru Lai Steps Down as CEO",
      "body": "Shanru Lai, co-founder of ShopBack, has transitioned out of the CEO role...",
      "source_url": "https://techinasia.com/...",
      "type": {"key": "news_type_rebranding", "name": "Rebranding"}
    }
  ]
}

News Query Parameters

ParameterDescription
orderingSort field. Prefix with - for descending (default: -date). Supported: date, headline, body, source_url, type, uuid, created_at, updated_at. Anything else returns 400 — see Sorting
limitResults per page
offsetPagination offset

Service Providers

Last updated: 28 September 2026

Service providers are professional-services firms linked to companies — most commonly auditors, but also legal counsel, fund administrators, placement agents, and more.

Endpoints

MethodEndpointAccess
GET/v3/partners/service-providers/Subscription required
POST/v3/partners/service-providers/Subscription required
GET/v3/partners/service-providers/{uuid}/Subscription required

List Service Providers

# All auditors
curl "https://api.altdmp.io/v3/partners/service-providers/?service_type=service_provider_type_audit" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Find Ernst & Young
curl "https://api.altdmp.io/v3/partners/service-providers/?search=Ernst+%26+Young" \
  -H "Authorization: Bearer YOUR_TOKEN"
import requests

BASE = "https://api.altdmp.io/v3/partners"
headers = {"Authorization": "Bearer YOUR_TOKEN"}

# All auditors
auditors = requests.get(
    f"{BASE}/service-providers/",
    params={"service_type": "service_provider_type_audit"},
    headers=headers,
).json()

# Find EY
ey = requests.get(
    f"{BASE}/service-providers/",
    params={"search": "Ernst & Young"},
    headers=headers,
).json()

Example response:

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "a3f1b2c4-5d6e-7f80-9102-a3b4c5d6e7f8",
      "display_name": "Ernst & Young LLP",
      "legal_entity": {
        "uuid": "d9e2f3a4-5b6c-7d8e-9f01-a2b3c4d5e6f7",
        "display_name": "Ernst & Young LLP",
        "year_founded": 1989,
        "date_founded": "1989-01-01",
        "date_founded_precision": "year",
        "domicile_country": {"iso_alpha3": "SGP", "name": "Singapore"},
        "trading_status": {"key": "operating", "name": "Operating"},
        "registration_numbers": [
          {
            "reg_number": "T08LL0018E",
            "authority_type": "UEN",
            "authority_name": "Accounting and Corporate Regulatory Authority"
          }
        ]
      },
      "service_types": [
        {"key": "service_provider_type_audit", "name": "Audit"}
      ],
      "last_updated_at": "2025-10-16T07:33:30Z",
      "aggregate_updated_at": "2026-09-24T03:12:45.118204Z"
    }
  ]
}

The list legal_entity has no headquarters. It is the compact variant — uuid, display_name, year_founded, date_founded, date_founded_precision, domicile_country, trading_status, registration_numbers. Fetch the detail response for headquarters, contact details, and people.

Service provider records are often thinly populated: expect null for year_founded, date_founded, domicile_country, and trading_status, and [] for registration_numbers.

Query Parameters

ParameterDescription
searchSubstring match on firm name. The whole value is matched as one string, so Ernst Young does not match “Ernst & Young LLP” — pass a single distinctive word, or the name as it is written
service_typeFilter by service type key (see table below)
orderingSort field: display_name, created_at, updated_at, aggregate_updated_at, or uuid, with a - prefix for descending. Default display_name. Anything else returns 400 Bad Request
limitResults per page
offsetPagination offset

POST to this path for an advanced filter body. Supported filter fields are uuid, display_name, description, legal_entity_uuid, service_type_key, service_type_name, and aggregate_updated_at (see Incremental Sync) — this is the only list endpoint that lets you look a record up by the legal entity behind it. Any other field name returns 400 Bad Request naming it. See the Filtering page for the grammar and operators.

Two things changed here in September 2026. ordering is validated against the fields above, and legal_entity__display_name — the only sort value it used to accept — now returns 400; send display_name instead. And ?search= is a single whole-string match on the firm name; it previously also matched each word separately, so a multi-word search that worked before may now return nothing. See Filtering → Supported Filter Fields.

Service Type Keys

The name column is the exact value returned in service_types[].name.

KeyName
service_provider_type_auditAudit
service_provider_type_taxTax
service_provider_type_complianceCompliance
service_provider_type_law_firmLaw Firm
service_provider_type_investment_bankInvestment Bank
service_provider_type_merchant_bankMerchant Bank
service_provider_type_commercial_bankCommercial Bank
service_provider_type_placement_agentPlacement Agent
service_provider_type_fund_administratorFund Administrator
service_provider_type_lenderLender
service_provider_type_valuation_firmValuation Firm
service_provider_type_management_consultantManagement Consultant
service_provider_type_financing_advisoryFinancing Advisory
service_provider_type_lp_consultantLP Consultant
service_provider_type_business_intermediaryBusiness Intermediary
service_provider_type_bus_dev_companyBusiness Development Company
service_provider_type_crowdfunding_platformCrowdfunding Platform
service_provider_type_insurance_providerInsurance Provider
service_provider_type_recruiting_firmRecruiting Firm
service_provider_type_software_providerSoftware Provider

Fetch the authoritative list at runtime with GET /reference-data/?type=enums&enum_categories=service_types rather than hard-coding it.


Get Service Provider Detail

curl "https://api.altdmp.io/v3/partners/service-providers/{uuid}/" \
  -H "Authorization: Bearer YOUR_TOKEN"
uuid = "..."
provider = requests.get(f"{BASE}/service-providers/{uuid}/", headers=headers).json()

Example response (Ernst & Young LLP):

{
  "uuid": "a3f1b2c4-5d6e-7f80-9102-a3b4c5d6e7f8",
  "display_name": "Ernst & Young LLP",
  "legal_entity": {
    "uuid": "d9e2f3a4-5b6c-7d8e-9f01-a2b3c4d5e6f7",
    "display_name": "Ernst & Young LLP",
    "description": "Ernst & Young LLP is a professional services firm providing audit, tax, and advisory services.",
    "year_founded": 1989,
    "date_founded": "1989-01-01",
    "date_founded_precision": "year",
    "is_female_founder": null,
    "domicile_country": {"iso_alpha3": "SGP", "name": "Singapore"},
    "headquarters": {
      "country_name": "Singapore",
      "country_iso_alpha3": "SGP",
      "state_name": null,
      "city_name": "Singapore"
    },
    "trading_status": {"key": "operating", "name": "Operating"},
    "registration_numbers": [
      {
        "reg_number": "T08LL0018E",
        "authority_type": "UEN",
        "authority_name": "Accounting and Corporate Regulatory Authority"
      }
    ],
    "alternate_names": [
      {"name": "EY Singapore", "type": "Other", "type_key": "alt_name_other"}
    ],
    "website_url": "https://www.ey.com/en_sg",
    "email": null,
    "phone": null,
    "founders": [],
    "directors": [],
    "auditors": []
  },
  "service_types": [
    {"key": "service_provider_type_audit", "name": "Audit"}
  ],
  "last_updated_at": "2025-10-16T07:33:30Z",
  "aggregate_updated_at": "2026-09-24T03:12:45.118204Z",
  "description": null,
  "public_notes": null
}
FieldTypeDescription
uuidstringService provider profile UUID
display_namestringFirm name
legal_entityobject, nullableThe underlying registered company, in the full variant — adds description, headquarters, alternate_names, website_url, email, phone, founders, directors, and auditors over the list variant
service_typesarray{key, name} per service offered — see Service Type Keys
last_updated_atdate-time stringDeprecated, use aggregate_updated_at. The later of when the profile and its legal entity were last edited. See last_updated_at is deprecated
aggregate_updated_atdate-time string, nullableWhen the record, or anything shown on it, last changed. Filter and sort on it to sync incrementally; see Incremental Sync
descriptionstring, nullableProfile-level description. Distinct from legal_entity.description, and usually null
public_notesstring, nullableFree-text notes

Reference Data

Last updated: 5 October 2026

The reference data endpoint exposes the full set of taxonomies and lookup lists used across the Partner API. Use it to build filter dropdowns, validate field values, and understand what keys are valid.

Cache this response. Reference data changes infrequently. Fetch it once at startup and cache it locally rather than calling it on every user interaction.

Endpoint

MethodEndpointAccess
GET/v3/partners/reference-data/Subscription required

Fetch All Reference Data

curl "https://api.altdmp.io/v3/partners/reference-data/" \
  -H "Authorization: Bearer YOUR_TOKEN"
import requests

BASE = "https://api.altdmp.io/v3/partners"
headers = {"Authorization": "Bearer YOUR_TOKEN"}

ref = requests.get(f"{BASE}/reference-data/", headers=headers).json()

Fetch Specific Sections

Use the type parameter to request only the data you need:

# Only enums and countries
curl "https://api.altdmp.io/v3/partners/reference-data/?type=enums,countries" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Only specific enum categories
curl "https://api.altdmp.io/v3/partners/reference-data/?type=enums&enum_categories=allocation_types,themes" \
  -H "Authorization: Bearer YOUR_TOKEN"
# Fetch only enums
enums = requests.get(
    f"{BASE}/reference-data/",
    params={"type": "enums"},
    headers=headers,
).json()

# Fetch specific categories
resp = requests.get(
    f"{BASE}/reference-data/",
    params={"type": "enums", "enum_categories": "allocation_types,themes"},
    headers=headers,
).json()

type Parameter Values

ValueReturns
enumsAll enumerated choice values grouped by category
countriesAll countries with iso_alpha3 and name
citiesCities with name, state_name, and country_iso_alpha3
industriesIndustry codes with code and description
business_sic_codesBusiness SIC codes with code, description, and country_iso_alpha3

Omit type to fetch all sections at once.

Each list comes back in a fixed order. This endpoint takes no ordering, and any value returns 400.


Enum Categories

When type=enums is requested, use enum_categories to narrow to specific groups:

The Count column is the number of values in each category, so you can tell an abridged sample below from a complete list.

Category KeyCountValues (samples for the large categories)
allocation_types4Commitment, Debt, Equity, Other
allocation_subtypes9Commitment, Debt Transactions, Other, Venture Capital (VC), Private Equity (PE), Mergers & Acquisitions (M&A), Distressed Transactions, Liquidity Events, Other Equity
allocation_deal_types75Pre-Seed, Seed, Series A – Series K+, Buyout / LBO, Growth / Expansion, IPO, Secondary Transaction, Convertible Debt, Term Loan, Bridge Loan, Commitment, Establishment, ESOP, …
capital_allocator_types25Venture Capital Firm, Private Equity Firm, Family Office - Single, Family Office - Multi, Family Office - Unknown, Sovereign Wealth Fund, Pension Fund (Manager) - Corporate / Other / Public / Union, Endowment Fund Manager, Fund of Funds Manager, Fund Manager, Corporate Investor, Banking Institution, Insurance Company, Investment Firm, Development Finance Institution (DFI), Wealth / Asset Manager, …
capital_allocator_allocates_from3Balance Sheet, Fund, Unknown
person_role_types3Director, Employee, Founder
service_types20Audit, Tax, Compliance, Law Firm, Investment Bank, Merchant Bank, Commercial Bank, Placement Agent, Fund Administrator, Lender, Valuation Firm, Management Consultant, Financing Advisory, LP Consultant, Business Intermediary, Business Development Company, Crowdfunding Platform, Insurance Provider, Recruiting Firm, Software Provider
preferred_fund_types75The same vocabulary as allocation_deal_types — the two keys return an identical list (e.g. venture_general_other / “Venture - General / Other”). Request either one to validate the preferred_fund_types values on capital allocator and fund profiles
fund_statuses14Announced, Closed, Raising, Evergreen, First Close, Second Close, Third Close, Fourth Close, Open, Open - With first close, Upcoming, Estimated, Liquidated, Unknown
trading_statuses3Operating, Inactive, Closed
deal_provenances3Filed with Regulator, Filed via FOIA, Reported
fund_performance_sources3Financial Statement, Fund Manager, Limited Partner
business_models3B2B (Business-to-Business), B2C (Business-to-Consumer), P2P (Peer-to-Peer)
techs61AgriTech, AI / GenAI / MLTech, BioTech, CleanTech, DeepTech, EduTech, EnergyTech, FinTech, GovTech, HealthTech, HRTech, InsurTech, LegalTech, MarTech, PropTech, RetailTech, TransportTech, …
themes40AI / GenAI / ML, Business Applications / SaaS, Buy Now Pay Later (BNPL), Climate / Green / Clean, Cybersecurity, Data Management & Analytics, Digital / Neo Banking, Digital Health Solutions, Payments, Quantum Computing, Smart City, Web 3 / Blockchain / Distributed Ledger, Wellness, …
horizontals12AI / GenAI / ML, AR / VR / XR / Spatial Computing, Collaboration & Productivity Suites, Connectivity & Communications, Core Compute / Edge Computing / Cloud Compute, Customer Stack, Data Foundations & Engineering Platforms, Data Management & Analytics, Digital Identity & Access Management, IoT (Internet of Things), Marketplaces, Web 3 / Blockchain / Distributed Ledger

News-article types are not in this endpoint. The type object on news rows uses news_type_* keys (e.g. news_type_funding, news_type_acquisition) that no enum_categories value exposes. Read type.name from the news rows themselves.


Example Response Structure

A full response (no type parameter) has five top-level keys. Arrays are truncated here — in reality each holds every value on the platform.

{
  "enums": {
    "allocation_types": [
      {"key": "equity", "name": "Equity"},
      {"key": "debt", "name": "Debt"},
      {"key": "allocation_type_commitment", "name": "Commitment"},
      {"key": "allocation_type_other", "name": "Other"}
    ],
    "allocation_subtypes": [
      {"key": "venture_capital", "name": "Venture Capital (VC)"},
      {"key": "private_equity", "name": "Private Equity (PE)"}
    ],
    "allocation_deal_types": [
      {"key": "preseed", "name": "Pre-Seed"},
      {"key": "seed", "name": "Seed"},
      {"key": "series_a", "name": "Series A"}
    ],
    "themes": [
      {"key": "themes_ai_genai_ml", "name": "AI / GenAI / ML"},
      {"key": "themes_payments", "name": "Payments"}
    ],
    "horizontals": [{"key": "horizontals_marketplaces", "name": "Marketplaces"}],
    "business_models": [
      {"key": "business_models_b2b", "name": "B2B (Business-to-Business)"}
    ],
    "techs": [{"key": "techs_fintech", "name": "FinTech"}],
    "capital_allocator_types": [
      {"key": "ca_type_venture_capital_firm", "name": "Venture Capital Firm"}
    ],
    "capital_allocator_allocates_from": [
      {"key": "allocates_from_fund", "name": "Fund"}
    ],
    "person_role_types": [
      {"key": "person_association_founder", "name": "Founder"}
    ],
    "service_types": [{"key": "service_provider_type_audit", "name": "Audit"}],
    "preferred_fund_types": [
      {"key": "preseed", "name": "Pre-Seed"},
      {"key": "venture_general_other", "name": "Venture - General / Other"}
    ],
    "trading_statuses": [{"key": "operating", "name": "Operating"}],
    "fund_statuses": [{"key": "fund_status_closed", "name": "Closed"}],
    "deal_provenances": [
      {"key": "deal_txn_provenance_filed_regulator", "name": "Filed with Regulator"}
    ],
    "fund_performance_sources": [
      {"key": "fund_per_source_type_lp", "name": "Limited Partner"}
    ]
  },
  "countries": [
    {"iso_alpha3": "SGP", "name": "Singapore"},
    {"iso_alpha3": "IDN", "name": "Indonesia"}
  ],
  "cities": [
    {"name": "Singapore", "state_name": null, "country_iso_alpha3": "SGP"}
  ],
  "industries": [
    {"code": "0602", "description": "Retail Trade (Except Vehicles)"}
  ],
  "business_sic_codes": [
    {"code": "47910", "description": "Retail sale via internet", "country_iso_alpha3": "SGP"}
  ]
}

Enum keys are not consistently prefixed. Some categories prefix every key (themes_*, fund_status_*, service_provider_type_*), others don’t (equity, debt, seed, operating), and a few are mixed — allocation_types returns bare equity and debt alongside prefixed allocation_type_commitment and allocation_type_other. Never construct a key from a display name; always read it from this endpoint.

enums values are {key, name} pairs, countries are {iso_alpha3, name}, cities are {name, state_name, country_iso_alpha3}, and both industries and business_sic_codes are {code, description} (with business_sic_codes adding country_iso_alpha3). When a type parameter is supplied, only the requested top-level keys appear.


Usage Pattern

# Load reference data once at startup and cache
ref = requests.get(f"{BASE}/reference-data/?type=enums,countries", headers=headers).json()

# Build a lookup: key -> display name for deal types
deal_type_map = {
    item["key"]: item["name"]
    for item in ref["enums"]["allocation_deal_types"]
}

# Use it to label API results
for deal in deals["results"]:
    label = deal_type_map.get(deal["allocation_deal_type_key"], deal["allocation_deal_type_key"])
    print(f"{deal['date']}: {label}")

Changelog

Last updated: 9 October 2026

Most recent changes first.

2026-10-09

Migration guide maps v2 funding rounds field by field

Docs only, no API change. The v2 → v3 migration guide now maps each field of v2 fundings[] (from GET /api/v2/companies/{id}/financials) to its field on GET /v3/partners/capital-receivers/{uuid}/deals/. The per-round total_shares_allocated is no_shares_issued. average_share_price_paid is now listed as a gap.

2026-10-07

Company investors no longer merge a person and a company

Changes results on GET /v3/partners/capital-receivers/{uuid}/investors/ for a small number of companies. When a person and a company had both invested, the endpoint could return them as one investor: one had no row, and its shares were counted on another row, either the other investor’s or that investor’s parent company or fund manager. Each investor now has its own row with only its own shares.

On an affected company:

The response shape is unchanged. See Capital Receivers → Investors.

2026-10-06

Industry code filters follow the industry tree

Changes results for any filter on industries_codes (capital receivers) or preferred_industries_codes (capital allocators and funds). These fields used to match codes flat. Now:

ne, ncontains, and nin are the exact complement of eq, contains, and in. Re-check saved queries that filter on industry. See Filtering → Industry Codes.

2026-10-05

Every route now rejects an ordering it cannot apply

Breaking for any request that sends an ordering a route does not use. Each of these got 200 before and now gets 400 Bad Request:

Every accepted value sorts exactly as before. Each endpoint page lists its values, and Filtering → Sorting has the shared rules.

Company investors count filed transactions only

GET /v3/partners/capital-receivers/{uuid}/investors/ now leaves out Reported transactions, as /captable/ already did, so the two endpoints agree on who holds shares. On companies with Reported transactions you will see fewer rows, and current_share_holding_percentage changes:

See Capital Receivers → Investors.

Person roles at a service provider show the company name

On GET /v3/partners/people/{uuid}/roles/ and POST /v3/partners/people/roles/, organization.display_name for a role at a service provider was the internal profile label, such as "Acme Service Provider Profile 123". It is now the provider company’s display_name. A role at a capital allocator that has no name now returns "" instead of a similar label. If you stripped the suffix yourself, you can stop.

2026-09-30

Deal rows return no_shares_bought and no_shares_sold

Every deal row from GET / POST /v3/partners/capital-receivers/{uuid}/deals/ and the batch POST /v3/partners/capital-receivers/deals/ now carries two share counts. You no longer need to add up transactions[].number_of_shares over the Secondary transactions yourself.

FieldValue
no_shares_boughtTotal shares bought in the deal’s Secondary transactions that name a buyer. null when the deal has none.
no_shares_soldTotal shares sold in the deal’s Secondary transactions that name a seller. null when the deal has none.

Both were already accepted as filter fields and ordering keys, and a filter or sort on either now matches the value shown on the row. Like every other share figure on the row, they leave out any transaction whose buyer or seller is unpublished or deleted.

This is an additive change; nothing existing on the row changed. See Deal Fields.

People roles: v2 field names mapped, and a role type removed

The migration guide now shows where the v2 people role fields went in v3. They are renamed or nested, not missing. Key Field Mappings → People Roles lists them:

v2 fieldv3 field
job_titletitle
role_type_keyrole_type.key
role_type_namerole_type.name

role_type_key and role_type_name are still accepted as filters, but they are no longer returned as response fields. Read the nested role_type object instead.

The People list parameters no longer list person_association_advisor as a role_type_key value. That role type does not exist: the only three are person_association_founder, person_association_director, and person_association_employee, as Role Type Keys already said. The API’s behavior has not changed.

2026-09-28

aggregate_updated_at: pull only what changed

Capital receivers, capital allocators, funds, people, and service providers now carry aggregate_updated_at, on both the list and detail responses. It moves when the record or anything shown on it changes (financials, transactions, the cap table, classifications, news, and the rest), including the record’s own edits, so it is the one field to poll for an incremental sync. Incremental Sync has what moves it, what does not, and a worked example.

WhatWhere
aggregate_updated_at filter, with eq, gt, gte, lt, lte and an offset-qualified ISO 8601 date-timeFilter body on all five POST list endpoints
ordering=aggregate_updated_at / -aggregate_updated_atAll five list endpoints, and /capital-allocators/{uuid}/funds/
ordering=uuid / -uuidThe same routes, as the tiebreak for keyset paging

Three things are worth knowing before you build on it:

Any other operator on the field, or a value without a UTC offset, returns 400.

last_updated_at is deprecated

last_updated_at keeps exactly its current value and behavior, and is marked deprecated in the OpenAPI schema. It covers only the profile and its registered entity, cannot be filtered, and sorts only on capital receivers, where the sort is slow. Move to aggregate_updated_at. last_updated_at will be removed in a future release, and the date will be announced here in advance.

2026-09-25

Startup filters on four endpoints

You can now scope companies, investment firms, investors, and people to startups. A company is a startup when it has ever raised venture capital, or when its funding status is Unfunded startup; Filtering → Startup filters has the full rule.

EndpointNew filterForm
POST /v3/partners/capital-receivers/is_startupFilter body, JSON boolean, eq / ne
POST /v3/partners/capital-allocators/has_startup_investmentsFilter body, JSON boolean, eq / ne
GET / POST /v3/partners/investors/capital_receiver_is_startupQuery string on GET, filters.all with a JSON boolean on POST
GET / POST /v3/partners/people/, POST /v3/partners/people/roles/organization_is_startupQuery string, true / false

Leaving any of them out returns exactly what the endpoint returned before. Two behaviors are worth knowing before you build on them:

The per-company deals endpoint now rejects unsupported filter fields

POST /v3/partners/capital-receivers/{uuid}/deals/ accepts exactly the Capital Receiver Deal Filter Fields — the same list as the batch POST /capital-receivers/deals/ — and returns 400 naming anything else. It was the last endpoint that did not: an unlisted name was matched against whatever it happened to resolve to, and transaction_label was accepted and ignored.

What to check. A deals filter built from the documented fields is unaffected. A request sending transaction_label, or any undocumented name, now returns 400 instead of a 200 that may have been wider than it looked. Use deal_label or self_declared_label in place of transaction_label.

Every 400 from this endpoint now uses the standard body — {"status_code": 400, "detail": ["..."]}. Previously a rejected filter here, such as a range with one bound, returned detail as a single string with no status_code. If you parse that string, switch to reading detail[0].

The allow-list check on every endpoint also now covers a field named inside a field-keyed condition, and conditions nested under one.

ordering is validated on every sub-resource route

The AUM, commitments, news, fund performance, investments, and allocator funds routes now check ordering against a published list and return 400 for anything else. Each route’s accepted values are on its endpoint page, and Filtering → Sorting has the shared rules.

Previously these routes differed: several returned 500 for an unknown field, fund performance and fund commitments silently fell back to -date, and the three investments pages and /capital-allocators/{uuid}/funds/ ignored ordering altogether. Three orders change for requests that already worked:

The capital receivers list has no name default: without ordering, companies come back in the order they were added to the platform. Send ordering=display_name for A to Z. The people list, /investors/, and both deals endpoints still ignore an unrecognized value.

Paging never skips or repeats a row

Every paginated route now ends its sort order with a unique key, so rows that tie on the sort field keep one position across requests. Previously, rows with the same sort value — common on the batch endpoints and on routes with no ordering — could swap places between one request and the next, so one page could leave a row out and the next repeat another. If you de-duplicate by UUID while paging to cope with this, you no longer need to.

Unpublished and deleted records are hidden everywhere

Every route now leaves out records that are unpublished or deleted, and anything reached only through one. Most of this is invisible to a well-behaved client. The figures that move are listed here; Unpublished and Deleted Records has the full rule.

Tracked actual_* fields count secondary buys

actual_count_of_investments and the actual_* lists on capital allocator profiles now count secondary purchases alongside primary ones. Firms with secondary-market activity show higher counts and longer lists, with no underlying data changed, and results sorted by those counts shift accordingly. A transaction with no recorded company no longer inflates actual_count_of_investments on the detail response. See Profile Fields.

Share counts count equity transactions only

no_shares_issued, no_shares_bought, and no_shares_sold on deals, and the shares_* fields on the investments pages, no longer count shares recorded on debt transactions such as a conversion or refinancing leg. A company that carried shares on a debt leg shows lower share counts, and a filter on those fields can return different rows.

More malformed filters return 400 instead of 500

2026-09-16

limit and offset are now validated, and the page-size contract is published

Every list endpoint takes limit between 1 and 1,000 (default 20) and offset from 0 upward (default 0). Those numbers are now declared in the OpenAPI schema on every paginated operation, so a generated client can read them rather than discover them.

A value outside that range is refused instead of being replaced with a default. ?limit=0, ?limit=-5, ?limit=abc, ?limit=10.5, ?limit=1e9 and the equivalents for offset return 400 Bad Request, keyed by the parameter at fault:

{
  "limit": ["Must be a positive integer. Omit `limit` to use the default of 20; values above 1000 are reduced to 1000."],
  "status_code": 400
}

Previously each of those returned 200 — a bad limit served the default 20 rows and a bad offset served the first page, so a caller who miscalculated an offset got page one with nothing to indicate it.

A limit above 1,000 is still accepted and reduced to 1,000, which has not changed. The envelope reports it honestly: count stays the full total and next carries the applied limit, so paging to exhaustion cannot read a prefix as the whole set. An empty value — ?limit= — still means “not specified” and applies the default. Only plain digits are accepted, so 1_0 and +10 are refused rather than reinterpreted.

What to check. If you send a computed limit or offset, make sure it cannot reach the endpoint as 0, a negative number, a decimal, or an empty-but-present non-numeric value. Those requests now fail loudly. See Pagination.

eq, ne, in, and nin now match case-insensitively on far more fields

Case-insensitive matching for the equality operators previously reached only fields stored directly on the record — in practice given_name and family_name on people and structure on funds. It now covers the single-valued text fields across every partner list endpoint, including geography, status, and classification fields on capital receivers, capital allocators, funds, service providers, people roles, and deals. {"op": "eq", "field": "domicile_country_iso_alpha3", "value": "sgp"} and "SGP" now return the same rows.

Filtering → Case sensitivity lists the fields per endpoint.

The multi-value classification fields are deliberately unchanged — every *_names / *_keys field, such as themes_names or preferred_countries_names, still matches exactly as written. A record can carry several of those values, and folding case there would change what ne and nin mean on them: {"op": "ne", "field": "themes_names", "value": "Fintech"} returns records with no Fintech theme, and that meaning is worth more than case tolerance.

What to check. A filter that passes the casing the API returns is unaffected. A filter that relies on a case mismatch to exclude rows — an eq on "sgp" that was returning nothing — now matches.

Capital allocator monetary filters

cheque_size_avg, cheque_size_min, cheque_size_max, dry_powder, median_valuation, current_allocation, and target_allocation filter on POST /v3/partners/capital-allocators/ using the same client-facing names as the response and ordering. Values are whole US dollars and take the numeric operators; an allocator that has not stated a figure is null and is excluded from a comparison in both directions. See Capital Allocator Filter Fields for a worked example.

Rate limit guidance expanded

Authentication → Rate Limits now documents what the published 320-per-minute rate actually covers, rather than just stating the number. No limit changed; this is the same contract described in more detail.

What is newly written down:

preferred_fund_types reference data returns values

GET /partners/reference-data/?type=enums&enum_categories=preferred_fund_types returns the full 75-value list, which is the same vocabulary allocation_deal_types returns — the two keys serve one shared category. It previously returned an empty array, and Reference Data → Enum Categories told you to validate against allocation_deal_types instead; either key now works. Use it to validate preferred_fund_types_names / preferred_fund_types_keys filters and the preferred_fund_types values on capital allocator and fund profiles.

2026-09-09

The five list endpoints filter on their documented fields, and nothing else

This is the breaking change on this page, and it should be a no-op for anyone filtering on documented fields. POST to /capital-receivers/, /capital-allocators/, /funds/, /people/, or /service-providers/ used to resolve an undocumented field name against internal storage instead of rejecting it. A name that happened to match something there filtered on it and returned 200 — including names written as relation paths with __ in them, which reached data the API does not publish.

Each of the five now filters on the supported filter field list for its endpoint and returns 400 for anything else:

{
  "status_code": 400,
  "detail": ["This endpoint does not filter on 'middle_name'. Supported filter fields: 'date_of_birth', 'family_name', 'given_name', 'location_country_iso_alpha3', 'location_country_name', 'uuid'."]
}

The check reads the whole filter body, so a name nested under all, any, or not is rejected, and so is the field-keyed form — including the scalar shorthand {"middle_name": "Ann"}, which was being applied as an equality match rather than ignored.

Six undocumented names are among what is now rejected: funding_status and has_vc_transactions on capital receivers, and nationality_name, nationality_iso_alpha3, is_employee, and associated_ca_types on people. These were accepted and then ignored, which Fields Accepted but Not Applied documented with the intent to reject them — that has now landed. None of them ever narrowed a result set, so no working filter changed. Only transaction_label on the per-company deals endpoint is left in that section.

Every documented field filters exactly as before. financials_latest_financial_year_end on capital receivers keeps working, with the same shape restrictions. uuid is now published as a filter field on all five endpoints — the profile UUID, or the person UUID on people — so a uuid filter that worked before continues to.

What to check. A filter body built from the documented lists needs no change. If one of yours carries a name that is not in its endpoint’s list, that request returns 400 now instead of 200 — worth a look for two shapes in particular: a __ in a field name, and the six names above. In both cases the condition was never narrowing anything, so removing it changes nothing about the rows you get.

POST and GET /v3/partners/service-providers/ accepted only legal_entity__display_name as a sort value and ignored any other ordering it did not recognize. The endpoint now takes the same client-facing names as the other lists — display_name, created_at, updated_at, default display_name — and returns 400 listing them for anything else, including the old legal_entity__display_name. This is the last of the four profile lists to publish its sort contract; see Service Provider Filter Fields.

?search= on this endpoint matched the whole string and, separately, each whitespace-separated word. It is now a single whole-string match on the firm name, consistent with capital receivers, capital allocators, and funds. ?search=Ernst+%26+Young still finds “Ernst & Young LLP”; ?search=Ernst+Young no longer does. If you build a search term by joining words, pass one distinctive word instead.

2026-09-03

Batch endpoints reject what they used to ignore

This is the breaking change on this page. The seven batch endpoints accepted any filter condition you sent and threw away the ones they did not apply. Asking for “news for these 500 companies, but only the ones since January” returned every news row for all 500, with a 200 and nothing marking the discarded condition. A misspelled field name behaved identically to a correct one — capital_receiver_uui and capital_receiver_uuid produced the same response.

Each endpoint now declares the fields it applies and returns 400 for anything else:

EndpointFields accepted
POST /v3/partners/capital-receivers/deals/capital_receiver_uuid (optional) and the deal filter fields, except transaction_label
POST /v3/partners/capital-receivers/financials/capital_receiver_uuid, financial_year_end, uuid
POST /v3/partners/people/roles/person_uuid, role_type_key, role_type_name
POST /v3/partners/capital-receivers/news/capital_receiver_uuid only
POST /v3/partners/capital-receivers/deal-share-types/capital_receiver_uuid only
POST /v3/partners/capital-allocators/aum/capital_allocator_uuid only
POST /v3/partners/funds/performance/fund_uuid only
{
  "status_code": 400,
  "detail": ["This endpoint does not filter on 'date'. Supported filter fields: 'capital_receiver_uuid'."]
}

The check reads the whole filter body, so an unsupported field nested under any or not, or written in the field-keyed form, no longer slips past.

Nothing that used to be applied stopped being applied. financials/ and people/roles/ keep every filter their published field lists carry, and batch deals/ gains a great deal, covered in the next two entries. Only conditions that were being silently dropped now error.

Check any request that sends conditions beyond the parent UUID on the four endpoints marked “only”. Those return 400 now. Drop the condition and filter client-side, or call the per-entity endpoint where the fields are honored.

A limit in the request body now returns 400 instead of doing nothing

limit, offset, and ordering are query parameters. Sent in the POST body they were ignored, so a caller who paginated that way received the default page size with a 200 and could reasonably conclude that was the whole result set.

{
  "status_code": 400,
  "detail": ["Unrecognized field(s) in request body: limit. Only filters may be sent in the body; pass limit, offset and ordering as query parameters."]
}

Every POST endpoint in the Partner API now rejects an unknown body key. The list endpoints already did; the change reaches the seven batch endpoints and POST /v3/partners/capital-receivers/{uuid}/deals/. Only filters belongs in the body.

Batch deals: filter by date, query the whole market, and sort

Of the seven batch endpoints, this is the one that gained a real filter surface rather than just stricter rejection. A date window used to mean pulling every round for a company and discarding the ones you did not want.

Filter on deal date. date takes eq, ne, in, nin, gt, gte, lt, lte, range, isnull, and notnull, with ISO date values:

{"filters": {"all": [
  {"op": "in", "field": "capital_receiver_uuid", "value": ["c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67"]},
  {"op": "range", "field": "date", "value": ["2025-01-01", "2025-06-30"]}
]}}

capital_receiver_uuid is now optional. A date filter on its own returns deals across every company on the platform — the “what closed recently” query, with no company list to page first:

{"filters": {"all": [{"op": "gte", "field": "date", "value": "2025-01-01"}]}}

A market-wide query must carry a date lower bound (eq, in, gt, gte, or range) so it stays bounded. lt and lte are not enough on their own, and a request with neither UUIDs nor a lower bound returns 400.

Results are ordered by deal date, most recent first, and honor ?ordering=date / ?ordering=-date. A stable secondary sort keeps a large paginated pull from repeating or skipping rows between pages. Both deals endpoints accept more sort keys than date — see the entry below.

The other deal filter fields apply here too — the same set as the per-company endpoint.

On top of the rejections in the entry above, three more things that used to pass silently now return 400 here: a substring operator on date, more than 1,000 UUIDs or a malformed one anywhere in the filter tree, and a node carrying two of all / any / not. That last one is worth checking for: a body that narrowed to one company through a top-level any was returning every company with a 200. transaction_label is the one deal field this endpoint does not accept.

See Batch Deals via POST and Filtering → Batch Deals.

Deals: a single round-size field, provenance on the row, and key-based filtering

Deal rows carry two new fields, and the size filters now match the numbers you can actually see in the response.

total_deal_size_usd is the round size regardless of where the figure came from. It is populated on every deal that has a size, which makes it the one to sort and filter on for “biggest rounds”. The existing deal_size_usd and reported_deal_size_usd split that same figure by provenance — at most one of them is ever populated, so filtering or sorting on either quietly leaves out every deal it does not cover.

provenance now appears on the deal row and on each nested transaction, as {key, name}. It was already filterable and sortable but never returned, so there was no way to tell from a row which of the two split size fields applied to it.

The size filters were matching the wrong number. deal_size_usd and reported_deal_size_usd filtered and sorted against a raw column rather than the value rendered in the field of the same name. Two consequences: deals whose amount sits on their transactions read as empty and never matched, and a deal_size_usd filter returned deals that render null in that very field. Both now use the same figure you see in the response, so a size filter or sort will return a different set of deals than it did before — a more correct one.

Filter classification and provenance by key. allocation_type_key, allocation_subtype_key, deal_transaction_type_key, and provenance_key sit alongside the existing display-name fields. A value copied straight from a deal response or from Reference Data now matches. The display-name fields are case-sensitive, so this is the more reliable way to filter.

deal_size_usd is now sortable. It was filterable but not orderable, so ?ordering=-deal_size_usd did nothing and returned rows in the default order. deal_transaction_size_usd continues to work and covers the same rows as total_deal_size_usd.

See Deal Fields and Capital Receiver Deal Filter Fields, which now lists the accepted ordering keys.

An incomplete filter condition now returns 400

A condition one key short of working — no op, or no field — used to contribute nothing to the query. The request returned 200 with every row the remaining conditions allowed, and nothing marked the dropped condition:

{"filters": {"all": [{"field": "display_name", "value": "Acme"}]}}

That now returns 400 naming the keys it received. The field-keyed form ({"display_name": {"value": "Acme"}}) behaves the same way, as does a field given as a number or an empty string.

A field given as a JSON array or object is still not handled cleanly — see Rejected bodies for which endpoints return 500 rather than 400 for it. The request is malformed either way; pass the field name as a string.

If a saved filter has been quietly returning more rows than it should, this is a likely cause, and it now fails loudly instead. This applies to every Partner API list endpoint.

A misspelled filter operator now returns 400 instead of quietly matching

An operator the API does not recognize is rejected, and the error names every operator the field accepts:

{
  "status_code": 400,
  "detail": ["Operator 'like' is not supported for 'display_name'. Supported operators: contains, endswith, eq, gt, gte, in, isnull, lt, lte, ncontains, ne, nin, notnull, range, startswith. Also accepted: exact, icontains."]
}

Previously it was treated as eq and returned 200, so {"op": "like", …} looked like a working substring filter while actually matching on exact equality. A filter that has been returning plausible results with a misspelled operator will now fail outright. Check the op in every saved filter against the operator table before your next run.

exact and icontains are also now accepted as aliases for eq and contains.

isnull and notnull mean “no value recorded”

On a text field, a blank value now counts as no value recorded: isnull returns it, notnull does not. The two are exact complements — for any field their counts sum to the unfiltered total.

notnull on a text field returns fewer rows than before. It used to count a blank as a value, so on some fields it returned every row and read as a no-op. If you have been using it that way, you will see the result set shrink.

Some fields now match eq regardless of case

eq, ne, in, and nin are case-insensitive on these fields:

EndpointFields
GET/POST /v3/partners/people/given_name, family_name
GET/POST /v3/partners/funds/structure

{"op": "eq", "field": "family_name", "value": "smid"} and "Smid" now return the same rows. Everywhere else — including every field on /v3/partners/capital-receivers/ — these operators still require exact casing. The safe practice has not changed: match the casing the API returns. See Filtering → Case sensitivity.

This one direction only ever widens a result set, so it cannot make an existing filter return less. A filter relying on a case mismatch to exclude something on one of these fields will stop excluding it.

A 429 now tells you which limit you hit

Rate-limited responses carry a structured body instead of a bare message. Alongside the usual status_code and detail, three keys name the limit:

{
  "status_code": 429,
  "detail": "Too many requests. Please slow down and retry.",
  "scope": "partner.standard",
  "dimension": "user",
  "retry_after_seconds": 60
}

scope names the class of request the limit applies to — partner.standard for Partner API traffic. dimension is what the limit was counted against: user for your credentials, customer for your organization, or ip for your network address. retry_after_seconds mirrors the Retry-After header.

Four headers accompany the response: X-RateLimit-Limit and X-RateLimit-Remaining always, plus Retry-After and X-RateLimit-Reset when a retry time can be determined.

X-RateLimit-Reset is a duration in seconds, not a Unix timestamp, despite what the header name usually means elsewhere. It carries the same value as Retry-After. Treating it as an epoch second produces a retry time in 1970, which shows up as a client that retries immediately and stays limited.

The limit itself is unchanged, as is the retry guidance — honor Retry-After, back off on repeated 429s. See Authentication → Rate Limits.

Fund performance no longer returns retired funds

POST /v3/partners/funds/performance/ and GET /v3/partners/funds/{uuid}/performance/ were returning performance rows belonging to funds that have since been retired from the platform. They no longer do.

If you pull performance for a fixed list of fund UUIDs, some of those UUIDs may now return no rows. Nothing about the request or the row shape changed.

2026-08-25

?search= no longer matches description text

?search= now means the same thing on every Partner API list endpoint: it matches what an entity is called, not the prose written about it. description is no longer searched on any of them.

EndpointWhat ?search= matches
GET /v3/partners/capital-receivers/Company name, alternate names, registration number
GET /v3/partners/capital-allocators/The allocator’s own display_name, and the linked legal entity’s display_name
GET /v3/partners/funds/The fund’s display_name, and its fund manager’s name
GET /v3/partners/service-providers/The firm’s display_name

Nothing else about ?search= changed — same parameter, same response shape, same pagination.

Expect fewer rows on all four. A term that appeared mostly in description text loses most of its matches: ?search=tech on capital receivers returned 39,438 rows and now returns 14,229. The request still succeeds, and nothing in the response marks the narrowing, so a saved search whose row count you track will simply drop. Re-check any term you match on that is a topic or a descriptor rather than a name.

To keep matching description text, filter on it explicitly instead. description is a supported filter field on all four endpoints, and contains is case-insensitive:

{"filters": {"description": {"op": "contains", "value": "tech"}}}

Send it as a POST to the same list endpoint. See Filtering.

Two documentation fixes went with this. The capital receivers and capital allocators pages both claimed description coverage and no longer do. The funds page described ?search= as covering the fund’s name alone — it has always also matched the fund manager’s name, and now says so. The service providers page was already accurate.

2026-08-20

Financial-statement file URLs — lifetime and expiry behavior documented

The financial_statements_audited[] and financial_statements_extracted[] arrays on capital receiver detail are the only place the Partner API returns files. Their url values were documented as signed and expiring, without saying for how long or what an expired link does. The lifetime is now stated: one hour from the response, with the Expires parameter in the URL carrying the exact deadline as a Unix timestamp. No API behavior changed — this was already true.

Also newly documented:

Migration guide listed the financial-statement arrays under the wrong entity

Migrating from v2 to v3 listed financial_statements_audited[] and financial_statements_extracted[] in the Capital Providers → Capital Allocators field mapping. Both are returned by capital receiver detail only; GET /v3/partners/capital-allocators/{uuid}/ has never returned them. The rows have moved to the Companies → Capital Receivers mapping, where they belong. If you read that table while porting an allocator integration, the fields were never available on that endpoint.

2026-08-19

New Filtering page — operators, body shapes, and the complete field lists

Filter documentation was spread across the introduction, the migration guide, and repeated on each endpoint page, and several parts of the grammar were written down nowhere. Filtering is now the single reference: every operator with its exact matching behavior, both accepted body shapes, the supported filter field list for each endpoint, and the batch-endpoint rules. The endpoint pages keep their own field tables and link here for the grammar. No API behavior changed — all of this was already true.

Behavior that was previously undocumented, worth checking your integration against:

Newly documented, nothing changed:

Data model — profiles per entity, and why counterparty UUIDs move

Data Model described bi-residency only across roles. It now covers how many profiles one root record can carry, and the two ways a counterparty uuid changes underneath you. No API behavior changed.

Field-location corrections worth checking your integration against:

New guidance:

2026-08-17

A filters query parameter now returns 400 instead of being silently ignored

filters has always been a request-body parameter. A value passed as ?filters=… was discarded before it was parsed, so the request succeeded with 200 OK and returned the complete unfiltered result set, with nothing in the response indicating the filter had been dropped. A caller filtering for one country got every country and no error.

It now returns 400 Bad Request:

{
  "filters": "`filters` must be sent in the JSON request body on this endpoint, not as a query-string parameter.",
  "status_code": 400
}

Note the shape: the message is keyed by filters, not under detail. See Error Responses → 400 response bodies.

Where it applies. The capital receivers, capital allocators, funds, people, and service providers lists, on GET as well as POST. The batch POST endpoints are unchanged in effect: they read their required UUID in filter from the body, so a query-string filters leaves the body empty and already returned 400. /v3/partners/investors/ is the one endpoint that still ignores a query-string filters rather than rejecting it — it validates its filter body separately. Do not rely on that.

What to check. If a request of yours has been returning more rows than your filter should allow, this is the likely cause, and you will now see the 400 instead. Move the filter into the request body and use POST. A bare ?filters= with no value is unaffected — it expresses no filter, and is still accepted.

Same principle as the strict filter-body rules in the 2026-08-07 entry: a filter you believe restricts rows must never silently widen them.

filters removed from the OpenAPI schema as a query parameter

The published schema advertised filters as a query parameter on 38 Partner API operations, none of which honored it. Several were GET-only sub-resources — /capital-allocators/{uuid}/funds/, /{uuid}/news/, /{uuid}/aum/ — which accept no request body at all, so the parameter documented filtering there was no way to perform. It has been removed everywhere.

If you generate a client from the schema, the filters query argument will disappear from these operations on your next regeneration. Nothing that worked before stops working: the parameter never had an effect. Pass the filter in the POST body instead, as the request examples in the schema already show.

Long URLs no longer fail opaquely

The request-line limit is now 8190 bytes across all environments; it was previously lower, and differed between them. A URL over the limit is still rejected before the API runs — returning 400 as HTML rather than JSON — but the ceiling is now consistent and roughly double what it was. See Error Responses → Very long URLs.

2026-08-14

⚠️ Breaking: all numeric values are now JSON numbers

This is a breaking change to every endpoint. Amounts, percentages, multiples, and rates that were previously returned as quoted strings are now returned as JSON numbers. There is no dual-serving period and no opt-in header — the change lands in one release. If your integration reads any of these fields as text, update it before this release reaches you.

FieldBeforeAfter
aum_value_usd"200000000.00"200000000.00
irr"0.1800"0.1800
percentage_held_absolute"18.50"18.50

What breaks. Anything that treats these values as strings: a JSON-schema or generated client that types them as string, string comparison (v === "0.00"), string methods on the value (v.replace, v.trim, v.startsWith), or a strongly-typed deserializer that will now reject a number where it expected text. Code that already coerced (float(v), Number(v), Decimal(str(v))) keeps working unchanged.

No value changed. Only the JSON type did. Every figure is the same figure, at the same precision.

Every affected field, across all endpoints:

EndpointFields
Fund detailmanagement_fee_percentage, gp_commitment_percentage, hurdle_percentage, carry_percentage, latest_fund_size.aum_value, latest_fund_size.aum_value_usd
Fund performanceirr, dpi, rvpi, net_multiple, and all eleven _usd cash-flow amounts
Fund / capital allocator AUMaum_value, aum_value_usd
Fund LP commitmentsinvestment_amount_usd, transactions[].investment_amount_usd
Capital allocator detaillatest_aum.aum_value, latest_aum.aum_value_usd
Capital allocator LP commitmentscash_value_transacted, cash_value_transacted_usd
Capital receiver detailThe whole funding object (total_funding_usd, latest_valuation_usd, latest_investment_amount, and the six equity/debt breakdowns), plus financials[] operating_revenue_usd, earnings_before_tax_usd, liabilities_usd, operating_revenue_growth_yoy_pct
Financials (capital receiver and capital allocator)Every monetary line item and every annual_*_yoy_growth_pct
Cap tables (managed)percentage_held_absolute, percentage_held_aggregate, holding_value_usd, and the same keys inside aggregations
Cap tables (snapshot)percentage_held
Dealstransactions[].price_per_share_usd

Decimal places are not contractual. A value carries the precision it is stored at, which varies by field — 18.50, 0.1800, 40.0000, and 374.0000000000 are all things you will see. Read the value, not the digits; JSON.parse discards trailing zeros anyway. If you need exact decimal arithmetic, parse into a decimal type rather than a float (Python: json.loads(body, parse_float=decimal.Decimal)).

Only responses are affected. Nothing about request syntax changed: POST filter bodies, query-parameter filters, and ordering all behave exactly as before.

See Introduction → Field Types and Nulls.

New irr_pct on fund performance

GET /v3/partners/funds/{uuid}/performance/ and the batch POST /v3/partners/funds/performance/ now return irr_pct alongside irr. irr is a fraction (0.1800); irr_pct is that value × 100 (18.00). It is null whenever irr is.

irr itself did not change scale — it was a fraction before and still is. irr_pct exists so the scale is stated in the field name rather than inferred, and so you do not have to multiply.

ordering now accepts irr_pct as well as irr; the two sort identically. Note that this endpoint silently falls back to -date for an unrecognized ordering value rather than returning 400 — so a sort that appears to do nothing is worth checking against the supported list.

dpi, rvpi, and net_multiple are multiples, not percentages, and get no _pct sibling. A dpi of 1.30 means 1.3×.

See Funds → Fund Performance.

Fund performance range filters were always available

irr_min / irr_max, dpi_min / dpi_max, rvpi_min / rvpi_max, and net_multiple_min / net_multiple_max work on /funds/{uuid}/performance/ and are now documented. The Migration Guide previously listed them as unavailable in v3, which was wrong.

irr_min and irr_max filter on the fraction, matching the irr response field — irr_min=0.15 means 15%. There is no irr_pct_min, and irr_min=15 returns an empty page rather than an error. No API behavior changed here; only the documentation.

2026-08-07

New type_key and profile_type_key — the fields to join on

Every object that references an entity without knowing its kind in advance now returns a stable snake_case key alongside the existing display value. Nothing was removed; these are additions.

ObjectNew fieldAlongside
Deal transactions[].buyer / .sellertype_keytype
Cap-table results[].shareholder (managed and snapshot)type_keytype
Fund LP commitment transactions[].buyertype_keytype
Capital allocator profile (list and detail)profile_type_keyprofile_type
Person role organizationprofile_type_keyprofile_type

Both draw from one vocabulary: capital_allocator, capital_receiver, fund, legal_entity, person, shareholder_group, service_provider, plus other and alt on cap tables, and null where the row references no entity at all.

Join, look up, filter, and group on the _key fields. The display halves — type and profile_type — spell the same concept two different ways today ("CapitalAllocatorProfile" versus "capitalallocatorprofile"), and a future release will change what they present to readable labels like "Capital Allocator". Neither field is being removed, but anything keyed on the display string will re-partition when that lands. Code keyed on type_key / profile_type_key is unaffected.

The same rule already applied to the older pairs (allocation_type_key / allocation_type_name, key / name inside nested choice objects) and is now written down in Introduction → Keys and Display Values, with per-namespace routing in Data Model → Telling Which Namespace a UUID Belongs To.

Every response example now shows the complete shape

Response examples across all endpoint pages were abridged, and several had fields at the wrong nesting level or with the wrong type. Each one has been regenerated against the live v3 API so it shows every field the endpoint returns. No API behavior changed — this is a documentation correction throughout.

Field-level corrections worth checking your integration against:

Filter-syntax corrections:

New guidance:

2026-08-04

Error responses now fully documented

Authentication → Error Responses now describes what an error body actually contains, instead of just listing statuses. No API behavior changed — these responses were always returned, they were simply not written down.

2026-07-20

Validated ordering on the funds and capital-allocators lists

The ordering query parameter on the funds list (GET/POST /v3/partners/funds/) and the capital-allocators list (GET/POST /v3/partners/capital-allocators/) is now validated against a fixed set of fields per endpoint, matching the behavior already in place for capital receivers. An unsupported or aliased field now returns 400 Bad Request listing the valid fields, rather than a server error.

2026-07-15

Batch deal-share-types endpoint

A new POST /v3/partners/capital-receivers/deal-share-types/ endpoint returns distinct deal-share-type rows across many companies in one paginated request, filtered by a non-empty capital_receiver_uuid in filter (up to 1,000 UUIDs). Each row is a unique (capital_receiver_uuid, deal_uuid, share_class, transaction_type) combination — built for ETL consumers that would otherwise call the per-company deals endpoint once per company. See Capital Receivers → Batch Deal Share Types via POST.

2026-07-14

Founding date exposed as a full date

Partner legal_entity objects now include date_founded (ISO date string, nullable) and date_founded_precision ("year", "month", or "day", nullable) on every entity type — capital receivers, capital allocators, funds, and service providers (list and detail). The integer year_founded field is retained unchanged for backward compatibility.

Capital receivers and funds also accept date_founded as a POST advanced-filter field (gte / lte / range / eq), enabling full-date founding-date queries alongside the existing year-only year_founded filter. The capital-receivers list now orders by the underlying full date; ?ordering=year_founded behavior is unchanged. See Capital Receivers → Common Filter Fields.

Investors — filter by portfolio-company country

GET/POST /v3/partners/investors/ now accept capital_receiver_headquarters_country_iso_alpha3 and capital_receiver_domicile_country_iso_alpha3, scoping results to investors who have transacted in portfolio companies headquartered in — or domiciled in — the specified countries. Both accept comma-separated ISO alpha-3 codes on GET, or an op: in list in a POST body. Supplying both filters ANDs them. See Investors → Query Parameters.

Known-zero funding amounts now return null

Funding buckets that compute to a known zero (filed_equity_usd, reported_equity_usd, reported_and_filed_equity_usd, filed_debt_usd, reported_debt_usd, reported_and_filed_debt_usd, total_funding_usd) now return null rather than a zero amount. This reverses the earlier distinction between unknown and known-zero for these fields: a null amount now means either. Companies whose funding rolls up to zero sort to the end of funding-based orderings in both directions, and are excluded from numeric range filters (gte/lte, total_funding_min/total_funding_max), since null never satisfies a numeric comparison. See Capital Receivers → The funding object.

2026-07-10

Growth-% fields capped at ±1000%

Every year-over-year growth-percentage field returned by the Partner API now returns null when the computed change exceeds +1000% or falls below −1000% (values at exactly ±1000% are retained). This affects the annual_*_yoy_growth_pct fields on capital-receiver and capital-allocator financials, and operating_revenue_growth_yoy_pct on the capital-receivers list. Out-of-bound values are treated as null for sorting and filtering as well, so they no longer sort as large numbers or pass numeric range filters. See Capital Receivers → Financials.

Batch POST endpoints for news, AUM, and fund performance

Three more POST endpoints extend the batch-export pattern to child records that previously had only per-parent GET routes. Each takes a non-empty in filter on the parent UUID, accepts up to 1,000 UUIDs per request, is paginated, and adds the parent UUID to every returned row so results can be joined back to their parent.

A missing or empty UUID filter, more than 1,000 UUIDs, or a malformed UUID returns 400 Bad Request. Access matches the existing per-record endpoints.

Capital allocators list — country filter performance

The capital-allocators list (GET /v3/partners/capital-allocators/) no longer times out when filtering by country. Results are unchanged; only response time improves.

2026-07-09

Sort and filter capital receivers by latest investment date

The capital-receivers list now accepts latest_investment_date — the date of a company’s most recent active deal — as both an ordering key and an advanced POST filter field. Sort with ?ordering=-latest_investment_date or filter with a gte comparison to pull only recently-funded companies (the v3 equivalent of the v2 date_of_last_round sort). It is computed only when you filter or sort on it, so plain list requests are unaffected, and companies with no deals sort last in both directions. See Capital Receivers → Advanced Filter (POST).

2026-07-07

Batch POST endpoints for bulk export

Three new POST endpoints let you fetch child records for many parents in a single request, replacing one-GET-per-record loops for large exports. Each takes a non-empty in filter on the parent UUID, accepts up to 1,000 UUIDs per request, and adds the parent UUID to every returned row so results can be joined back to their parent.

A missing or empty UUID filter, more than 1,000 UUIDs, or a malformed UUID returns 400 Bad Request. Access matches the existing per-record endpoints (both Atlas and Allocate).

2026-07-01

⚠️ Breaking: capital receiver funding fields renamed and expanded

This is a breaking change. Field names were removed and renamed with no backward-compatible aliases. Requests that read, filter, or sort by the old names will break — filtering or ordering by a removed name now returns 400 Bad Request, and responses no longer contain the old keys. Update your integration before this release reaches you.

The funding object on the capital-receiver detail response has been standardized (a hard cutover — the old field names are gone, with no aliases):

The total_funding_amount_usd filter and ordering key was renamed to total_funding_usd to match the response field; the old name now returns 400 Bad Request. latest_valuation_usd is unchanged. Filtering, ordering, and the response body now use one identical set of names.

Semantics also changed: null now means unknown while a zero amount means known-zero; public-listed companies return null for all funding and valuation amounts (but keep funding_status); and funding totals now count all primary funding regardless of deal subtype or type. See Capital Receivers → The funding object.

Per-plan endpoint access documented

Added a Plans & Access page listing every endpoint and whether it’s included with the Atlas or Allocate plan. Notable differences: Funds and capital-allocator LP commitments are Atlas-only, while the capital-receivers list/search endpoint is Allocate-only. Custom plans are defined by their own agreements and are not covered by the matrix.

2026-06-29

Rate limits documented

The API is rate limited to 320 requests per rolling 1-minute window. Exceeding the limit returns 429 Too Many Requests with a Retry-After header indicating how long to wait before retrying. See Authentication → Rate Limits for guidance on backoff and retries.

2026-06-19

POST filter body is now strict

On every list endpoint that accepts a POST advanced filter (capital receivers, capital allocators, funds, investors, people, service providers), the request body now accepts only the filters object. search, ordering, limit, and offset must be passed as query parameters — sending them in the body now returns 400 Bad Request instead of being silently ignored.

# Pagination and sorting go on the query string; only filters in the body
curl -X POST "https://api.altdmp.io/v3/partners/capital-receivers/?ordering=-latest_valuation_usd&limit=100" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"all": [{"op": "eq", "field": "headquarters_country_iso_alpha3", "value": "SGP"}]}}'

Validated ordering

The ordering parameter is now validated against a per-endpoint list of supported fields. An unsupported value returns 400 Bad Request with the list of valid fields, rather than a server error or being silently ignored.

People — country filters

GET /v3/partners/people/ and GET /v3/partners/people/{uuid}/roles/ now accept domicile_country_iso_alpha3 and headquarters_country_iso_alpha3 query parameters, filtering on an associated organization’s country. Both are repeatable or comma-separated (e.g. SGP,USA).

The search parameter on people now matches each word independently across given and family name, so a full name such as Shanru Lai matches regardless of word order.

Fund LP commitments — committing parties

GET /v3/partners/funds/{uuid}/commitments/ now returns a transactions[] array on each commitment, exposing the underlying transactions and the committing party (the LP) as transactions[].buyer. The previous top-level transaction_currency, buyer, and seller fields are not part of this endpoint’s response.

Capital receivers — search and registration number

GET /v3/partners/capital-receivers/ search now also matches alternate names, description, and registration number. A registration_number query parameter is available to filter directly by company registration number. is_raising_now is a POST filter field only — not a GET query parameter.