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 Type | What It Represents | Example |
|---|---|---|
| Capital Receivers | Companies and startups that have raised funding | ShopBack |
| Capital Allocators | Investors (VC firms, family offices, PE funds) that deploy capital | Wavemaker Group |
| Funds | Individual fund vehicles managed by a capital allocator | Wavemaker Pacific 1, Bain Capital Asia III |
| People | Founders, directors, and other individuals linked to companies | Shanru Lai (ShopBack co-founder) |
| Investors | Investment totals for any entity that has been a buyer in a transaction, by base entity type | Any company, person, or shareholder group that has invested |
| Service Providers | Auditors and professional-services firms | Ernst & Young LLP |
| Legal Entities | Underlying registered companies — returned as a nested object on profile detail responses | Any registered business |
| Reference Data | Taxonomies and lookup lists for filter fields | Countries, 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:
GET— query-string filters (search,ordering,limit,offset)POST— JSON body for advanced range queries, multi-value filters, and boolean logic
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.
filtersgoes in the body, never on the query string. The two directions are not interchangeable:search,ordering,limit, andoffsetare query parameters only, andfiltersis a body parameter only. A request that puts a value in?filters=returns400 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-stringfiltersand 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:
| Parameter | Default | Accepted values | Behavior outside that range |
|---|---|---|---|
limit | 20 | Whole number from 1 to 1000 | Above 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 |
offset | 0 | Whole number from 0 upward | A 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:
- A clamped page is not a silent truncation. When
limit=5000is reduced to 1,000,countstill reports the full total andnextis returned carrying the applied limit, so a client that pages to exhaustion cannot mistake the first 1,000 rows for the whole result set. - An empty value is treated as “not specified”.
?limit=&offset=applies the defaults rather than returning400— a query string assembled from an unset variable still works. - Pages never overlap or leave gaps. Every paginated route ends its sort order with a unique key, so rows that tie on the sort field keep one position across requests and paging with
limitandoffsetnever skips or repeats one. This holds with or withoutordering. See Filtering → Sorting.
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/previouswhen the company has no cap table on file. Readresultsrather than branching oncount.
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:
- A company, investment firm, fund, person, or service provider that is unpublished or deleted is left out of every list, batch response, and
count, and its detail route and every sub-route return404. A batch request naming its UUID gets no rows, the same as an unknown UUID. - A transaction whose buyer or seller is hidden is left out of deals, cap tables, commitments, investor totals, and investments pages, and of every figure built from them — share counts, deal sizes, prices, and investment dates.
- A role that is itself unpublished or deleted, or that sits at a hidden organization, is left out of the people routes and never makes a person match a role or country filter.
- Filters never match through a hidden record. A deleted headquarters location reads as no headquarters — it matches no country, and
isnullmatches it — andfund_manager_nameconsiders only visible managers. On/capital-receivers/, deleted registration numbers and alternate names are not matched by theregistration_numberfilter orsearch. - Contact details, websites, auditors, founders, and directors that are unpublished are not shown; the next visible one is, or
null.
Two consequences worth designing for:
- Percentages can sum to less than 100%. A cap table or a company’s investor list keeps every visible holder’s percentage of the whole company, and leaves out the hidden ones — see Cap Table.
- A deal can be returned with fewer transactions than it has. The deal stays, even when none of its transactions are visible.
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/.
- Operators. The field accepts
eq,gt,gte,lt, andlte. Any other operator returns400. - Values. Send an ISO 8601 date-time with a UTC offset, such as
2026-09-24T00:00:00Zor2026-09-24T08:00:00+08:00. A date alone, or a time with no offset, returns400. - Sorting.
ordering=aggregate_updated_ator-aggregate_updated_at. Records with no value yet sort last in either direction, on every one of the five lists.
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:
| Record | Also 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 allocator | The 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 |
| Fund | The fund’s registered entity and everything that moves it; its preferred_* preferences; performance records; AUM; contact details and files on the fund’s profile |
| Person | Their roles at organizations, registration numbers, contact details, news about them, and transactions in which they bought or sold |
| Service provider | Its 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 asstring), 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 shape | Scale | 18.75% is written | Example fields |
|---|---|---|---|
Bare name — irr | Fraction | 0.1875 | irr |
_pct / _percentage / percentage in the name | Fraction × 100 | 18.75 | irr_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:
- Multiples —
dpi,rvpi,net_multiple. Adpiof1.80means 1.8×, not 180%. - Conversion factors — an exchange
rateof1.3450is a multiplier between two currencies. There is no percentage reading of it.
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 key | Alongside the label |
|---|---|
type_key | type |
profile_type_key | profile_type |
allocation_type_key | allocation_type_name |
allocation_subtype_key | allocation_subtype_name |
allocation_deal_type_key | allocation_deal_type_name |
latest_investment_stage_key | latest_investment_stage_name |
deal_type_key | deal_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_keyis 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", …). Onlegal_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 — readtype_keyin the context of the object holding it.
typeandprofile_typewill 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_keyandprofile_type_keyare unaffected. If you join on the keys, this change is invisible to you. If you join ontypeorprofile_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.
| Type | How it works | What’s available |
|---|---|---|
managed | Cap 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. |
snapshot | Cap 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
| Goal | How |
|---|---|
| Company profile, funding history, cap table | GET /capital-receivers/{uuid}/ → /deals/ → /captable/ |
| Current shareholders for a company | GET /capital-receivers/{uuid}/captable/ |
| Total invested per investor with deal-stage breakdown | GET /capital-receivers/{uuid}/investors/ (managed cap table only) |
| Find VC firms active in SEA at Seed stage | POST /capital-allocators/ filtering preferred_allocation_deal_types_keys, then compare against each firm’s actual_allocation_deal_types |
| Startups, their investors, and their people | is_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 history | GET /funds/{uuid}/performance/ + /aum/ + /commitments/ |
| Look up a founder or director by name | GET /people/?role_type_key=person_association_founder&search=Henry+Chan |
| Registration number lookup | GET /capital-receivers/?search=<name> — registration numbers are in the legal_entity object on the detail response |
| Build filter dropdowns in a UI | GET /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.
Legal Entity vs. Profile
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 Type | Endpoint | Represents |
|---|---|---|
| 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_typefield of"legalentity"or"person"to indicate which. Fields that resolve through the legal entity — geography, cap table, financials — returnnullfor 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 type | Root record | How many per root |
|---|---|---|
| Capital Receiver | Legal Entity | Exactly one |
| Fund | Legal Entity | Exactly one |
| Capital Allocator | Legal Entity or Person | One per preferred allocation type — more than one is normal |
| Service Provider | Legal Entity | Not 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.
The Nested Legal Entity Object
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:
| UUID | Where it appears | Identifies | Use it with |
|---|---|---|---|
Profile uuid | Top level of every list and detail response | The profile (capital receiver, allocator, fund, service provider, or person) | The matching profile endpoint, e.g. /capital-receivers/{uuid}/ |
legal_entity.uuid | Nested inside the legal_entity object on detail responses | The underlying registered company | Reference key — links profiles that share the same legal entity |
legal_entity_uuid | Flat field on investment rows — the /investments/ sub-resource on capital allocators, funds, and people | The underlying registered company (same value as legal_entity.uuid) | Reference key to correlate rows belonging to the same company |
capital_receiver_uuid | Flat 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_key | Buyer / seller / shareholder / organization objects | Whatever type_key says — see above | The 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 value | uuid identifies | Use it with |
|---|---|---|
capital_allocator | A capital allocator profile | /capital-allocators/{uuid}/ |
capital_receiver | A capital receiver profile | /capital-receivers/{uuid}/ |
fund | A fund profile | /funds/{uuid}/ |
service_provider | A service provider profile | /service-providers/{uuid}/ |
person | A person | /people/{uuid}/ |
legal_entity | The underlying registered company | Correlation key only — there is no legal-entity endpoint |
shareholder_group | A grouping of shareholders | Correlation key only |
other, alt, null | Nothing — an aggregate or name-only row | Nothing 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 examplecapital_receiver_uuid, notlegal_entity_uuid. Thelegal_entity_uuidis 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
| Response | Root UUID |
|---|---|
Profile detail — /capital-receivers/{uuid}/, /capital-allocators/{uuid}/, /funds/{uuid}/, /service-providers/{uuid}/ | Nested legal_entity.uuid |
/investments/ on capital allocators, funds, and people | Flat legal_entity_uuid |
/investors/ — the top-level list | The row uuid already is the root: investor_type is legal_entity, person, or shareholder_group, never a profile |
| Snapshot cap table | shareholder.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 organization | None — 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_keyisother,alt,shareholder_group, ornullon 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 Forbiddenresponse 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:
- Page size. One request for 1,000 rows and one request for 20 rows each count as one. This is the single largest lever you have — see the checklist below.
- Method. A
POSTcarrying afiltersbody is a read on this API and counts exactly as aGETdoes. - Response size or query cost. There is no weighting by how much work a request does.
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:
dimension | Counted against | When it binds |
|---|---|---|
user | The credentials the request authenticated with | The normal case — this is the published 320/minute rate |
customer | Your organization, across every set of credentials it holds | Several users, services, or workers running in parallel under one organization |
ip | The network address the request came from | Traffic 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
}
| Field | Meaning |
|---|---|
scope | Names the class of request the limit applies to — partner.standard for Partner API traffic |
dimension | What the limit was counted against — user for your credentials, customer for your organization, or ip for your network address |
retry_after_seconds | Seconds 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:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | The limit that was hit |
X-RateLimit-Remaining | Requests left in the window that refused the request |
Retry-After | Seconds to wait before retrying |
X-RateLimit-Reset | Also 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-Resetis a duration, not a Unix timestamp. The name usually means the latter, and treating60as 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:
- Retrying immediately. A retry sent before the window frees is refused again and, on the shared ceilings, deepens the hole. Always wait.
- Retrying in lockstep. When several workers hit the limit at once and all wait exactly
Retry-After, they resume simultaneously and are refused together. Add a small random delay, as above. - Treating
429as fatal. A job that aborts on the first429and is then restarted from the beginning re-requests everything it already had, which costs more quota than waiting would have.
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 a429.
Staying within the limits
In rough order of how much they buy you:
- Use the largest page size you can.
limit=1000is the maximum on list endpoints. One request for 1,000 rows counts the same against the limit as one request for 20 — a 50× difference in rows per request, for the same quota. - Use the batch endpoints instead of per-record loops. Deals, financials, news, AUM, and deal share types all have batch
POSTforms that take a list of UUIDs and return one paginated response. Fetching 500 companies’ deals one call at a time costs 500 requests; the batch form costs a handful. See Batch Endpoints. - Pull incrementally. Filter each list on
aggregate_updated_atfrom just before your last sync and page by keyset, rather than re-fetching the full set each run. See Incremental Sync. - Cache what does not change. Reference Data — enum values, countries, industries — changes rarely. Fetch it once per run at most, not once per record.
- Bound your concurrency. Parallel workers share the organization ceiling, so more of them past a point produces
429s rather than throughput. Start with a small fixed pool and raise it only if you are not seeing429s. - Spread out bulk work. Page through large result sets steadily rather than issuing requests in tight bursts. A steady 5 requests per second stays comfortably under a 320-per-minute rate; 320 sent in the first two seconds of a minute does not.
- Do not poll for changes you can schedule. Re-running a full sync every few minutes spends most of its quota confirming nothing changed.
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."
}
| Status | detail | Meaning |
|---|---|---|
400 Bad Request | varies — see 400 response bodies | Malformed 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 Unauthorized | Authentication 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 Forbidden | You 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 Found | The 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 Found | The 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 Requests | Too 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 Error | An 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:
- 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.
- Endpoint entitlement. Each endpoint is individually gated by your plan. If your plan doesn’t include an endpoint, calling it returns
403 Forbiddeneven 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:
| Area | Atlas | Allocate |
|---|---|---|
| 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
| Method | Endpoint | Atlas | Allocate |
|---|---|---|---|
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
| Method | Endpoint | Atlas | Allocate |
|---|---|---|---|
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
| Method | Endpoint | Atlas | Allocate |
|---|---|---|---|
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
| Method | Endpoint | Atlas | Allocate |
|---|---|---|---|
GET / POST | /v3/partners/investors/ | ✓ | ✓ |
People
| Method | Endpoint | Atlas | Allocate |
|---|---|---|---|
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
| Method | Endpoint | Atlas | Allocate |
|---|---|---|---|
GET / POST | /v3/partners/service-providers/ | ✓ | ✓ |
GET | /v3/partners/service-providers/{uuid}/ | ✓ | ✓ |
Reference Data
| Method | Endpoint | Atlas | Allocate |
|---|---|---|---|
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
| Area | v2 | v3 |
|---|---|---|
| Base URL | https://api.alternatives.pe/api/v2/ | https://api.altdmp.io/v3/partners/ |
| Authentication | OAuth2 client credentials → /api/v2/oauth/token | API key → POST /v3/token/issue/ (via PropelAuth) |
| Identifiers | Integer IDs ("id": 1234) | UUIDs ("uuid": "c16a0ffd-…") |
| Pagination envelope | {"data": {"data": [...], "total_records": N, ...}} | {"count": N, "next": "…", "previous": "…", "results": [...]} |
| Max page size | 100 | 1000 (default 20) |
| Ordering | order_by + order_direction | Single ordering param with - prefix for descending |
| Filtering | Query-string only | Query-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 |
| Classification | Sector / theme driven | Five dimensions: themes, techs, business_models, industries, horizontals |
| Money fields | Mixed currency for fund performance fields; company financials already USD | USD-normalized, _usd suffix (explicit in field names for both) |
Breaking Semantic Changes
These are not syntactic renames — they change meaning and require deliberate handling:
- Investors split by allocation type. An investor with both equity and debt allocations now appears as two separate records in API responses — one per allocation type.
- Sectors deprecated.
v3.industriesis a different classification system, not a rename ofv2.sectors. Do not treat industry codes as mapped equivalents of sector IDs. - Theme taxonomy reworked.
v3.themesare not 1:1 withv2.themes. Treat the five-dimension classification as a new framework to adopt explicitly. date_incorporatedmaps todate_founded. The nestedlegal_entityexposesdate_founded(full ISO date) with adate_founded_precisionflag ("year"/"month"/"day"), plus a year-onlyyear_foundedfor convenience.nameis nested.v2.namemaps tov3.legal_entity.display_name, not a top-level field.headquatersspelling fixed. v2 had a typo; v3 usesheadquarters.
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
POSTbody accepts only thefiltersobject.search,ordering,limit, andoffsetare always query parameters — sending them in the body returns400 Bad Request.
There is no
?filters=query parameter. Coming from v2 it is a natural guess, and it returns400 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§ors=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
| Category | Operators |
|---|---|
| Equality | eq, ne |
| Set membership | in, nin |
| String | contains, ncontains, startswith, endswith |
| Numeric / date | gt, gte, lt, lte, range |
| Blank | isnull, notnull — “no value recorded”, which on a text field includes a blank value |
| Logical | all (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 param | v3 equivalent |
|---|---|
query | search query param |
countries | POST headquarters_country_iso_alpha3 or domicile_country_iso_alpha3 |
sectors | Deprecated — industries_codes is available but uses a different classification. A code matches itself and every code beneath it; see Industry Codes |
themes | POST themes_names or themes_keys (taxonomy overhauled) |
investment_stage | POST latest_investment_stage_name or latest_investment_stage_key |
valuation_min / valuation_max | POST latest_valuation_usd with gte / lte |
revenue_min / revenue_max | POST latest_operating_revenue_usd with gte / lte |
revenue_growth_min / revenue_growth_max | POST operating_revenue_growth_yoy_pct with gte / lte |
total_funding_min / total_funding_max | total_funding_min / total_funding_max query params, or POST total_funding_usd with gte / lte |
status | POST trading_status_name |
female_founder | POST is_female_founder |
iso_code | POST domicile_country_iso_alpha3 |
order_by + order_direction | ordering (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:
| Dimension | Description |
|---|---|
themes | Thematic groupings (e.g., FinTech, AI, ClimateTech). Taxonomy was overhauled — not 1:1 with v2 themes. |
techs | Underlying technology focus. |
business_models | How the business makes money (Marketplace, SaaS, etc.). |
industries | Different classification system from v2 sectors. Uses string codes ("08", "10") instead of integer IDs. |
horizontals | Cross-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.
Response Structure: Nested Legal Entity
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 Endpoint | v3 Equivalent | Notes |
|---|---|---|
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}/uen | GET /v3/partners/capital-receivers/?registration_number={uen} | UEN lookup via query param |
GET /api/v2/companies/{id}/financials | Multiple v3 endpoints | See 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 source | v3 equivalent |
|---|---|
| Basic funding block | GET /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 |
| Shareholders | GET /v3/partners/capital-receivers/{uuid}/investors/ |
| Cap table | GET /v3/partners/capital-receivers/{uuid}/captable/ |
| Multi-year financials | GET /v3/partners/capital-receivers/{uuid}/ (financials[] array on detail) |
Capital Providers → Capital Allocators
| v2 Endpoint | v3 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 Endpoint | v3 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 Endpoint | v3 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=X | GET /v3/partners/people/?search=X |
Auditors → Service Providers
| v2 Endpoint | v3 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)
| Endpoint | What 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 Field | v3 Field | Notes |
|---|---|---|
id | uuid | Changed to UUID |
name | legal_entity.display_name | Nested |
uen | legal_entity.registration_numbers[].reg_number | Nested; each item includes authority_type and authority_name |
description | legal_entity.description | Nested |
url / website | legal_entity.website_url | Renamed |
database | legal_entity.domicile_country.iso_alpha3 | Renamed to domicile |
headquaters | legal_entity.headquarters.country_iso_alpha3 | Spelling fixed; nested object |
date_incorporated | legal_entity.date_founded | Full date, with date_founded_precision; year_founded also available |
investment_stage | funding.latest_investment_stage_name | Derived from most recent active deal |
total_equity_funding | funding.reported_and_filed_equity_usd | Detail only; USD. Total equity is also broken out into filed_equity_usd / reported_equity_usd; funding.total_funding_usd adds debt |
last_valuation | funding.latest_valuation_usd | Available on list and detail; v2 values were already USD at import — v3 makes it explicit |
size_of_last_round | funding.latest_investment_amount | Detail only |
date_of_last_round | funding.latest_investment_date | — |
revenue | latest_financials.operating_revenue_usd | Latest snapshot on list; historical array on detail; v2 values were already USD at import — v3 makes it explicit |
financial_year_end | latest_financials.financial_year_end | — |
revenue_growth | latest_financials.operating_revenue_growth_yoy_pct | YoY percentage |
ebit | latest_financials.earnings_before_tax_usd | Renamed; v2 values were already USD at import — v3 makes it explicit |
liabilities | latest_financials.liabilities_usd | v2 values were already USD at import — v3 makes it explicit |
status | legal_entity.trading_status.name | v3 trading status enum; do not preserve v2 semantics |
company_raising | is_raising_now | Boolean (was nullable) |
female_founder | legal_entity.is_female_founder | Boolean (was 0/1) |
sectors | (deprecated) | Do not treat industries as a direct replacement |
themes | themes[] | 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_at | aggregate_updated_at | ISO 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[] field | v3 deal field | Notes |
|---|---|---|
label | self_declared_label | Round name as the company describes it |
series | allocation_deal_type_name / allocation_deal_type_key | Normalized round stage, e.g. "Series F" / "series_f" |
investment_quarter | investment_quarter | Now a label such as "Q2 2024", not an integer |
first_investment_date / last_investment_date | first_investment_date / last_investment_date | First and last transaction dates within the round |
share_class_id | share_class | Share class names, comma-separated; the raw integer ID is not exposed |
total_funding | total_deal_size_usd | USD. deal_size_usd / reported_deal_size_usd split the same figure by provenance |
post_money_valuation | post_money_valuation_usd | USD |
max_share_price_paid | price_per_share_usd | Highest per-share price across the round’s transactions |
total_shares_allocated | no_shares_issued | Shares 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 Field | v3 Field | Notes |
|---|---|---|
investor_id | uuid | |
name | display_name | |
type | types[].name | |
country | legal_entity.domicile_country.name | |
aum | latest_aum.aum_value_usd | |
stages | preferred_allocation_deal_types[].name |
Fund Performance
| v2 Field | v3 Field | Notes |
|---|---|---|
irr | irr | v3 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 |
tvpi | tvpi | Total Value to Paid-In |
dpi | dpi | Distributions to Paid-In |
rvpi | rvpi | Residual Value to Paid-In |
net_irr | net_irr | |
gross_irr | gross_irr | |
as_of_date | as_of_date | Performance date |
currency | currency.iso_code | v2 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 |
provenance | provenance.name | Data source |
Commitment Deals
| v2 Field | v3 Field | Notes |
|---|---|---|
id | uuid | Changed to UUID |
fund_id | Parent URL | Use /funds/{uuid}/commitments/ |
fund_name | Parent resource | Get from fund detail |
lp_id | Different endpoint | Use /capital-allocators/{uuid}/commitments/ |
lp_name | Via allocator detail | |
commitment_amount | investment_amount_usd | |
commitment_date | date | |
vintage | fund.vintage_year | From fund |
currency | transaction_currency.iso_code | |
provenance | provenance.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 Field | v3 Field | Notes |
|---|---|---|
job_title | title | Renamed |
role_type_key | role_type.key | Nested |
role_type_name | role_type.name | Nested |
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
| Method | Endpoint | Purpose | Key params / body |
|---|---|---|---|
GET | /v3/partners/capital-receivers/ | List | search, ordering, registration_number, limit, offset |
POST | /v3/partners/capital-receivers/ | Filter | JSON filters, search, ordering, limit, offset |
GET | /v3/partners/capital-receivers/{uuid}/ | Detail | — |
GET | /v3/partners/capital-receivers/{uuid}/financials/ | Detailed financials | limit, offset |
GET | /v3/partners/capital-receivers/{uuid}/investors/ | Investors in this entity | limit, offset |
GET | /v3/partners/capital-receivers/{uuid}/deals/ | Funding rounds + transactions | search, ordering, limit, offset |
POST | /v3/partners/capital-receivers/{uuid}/deals/ | Filter funding rounds | JSON filters, search, ordering, limit, offset |
GET | /v3/partners/capital-receivers/{uuid}/news/ | News articles | ordering, limit, offset |
Capital Allocators
| Method | Endpoint | Purpose | Key params / body |
|---|---|---|---|
GET | /v3/partners/capital-allocators/ | List | search, ordering, limit, offset |
POST | /v3/partners/capital-allocators/ | Filter | JSON filters, search, ordering, limit, offset |
GET | /v3/partners/capital-allocators/{uuid}/ | Detail | — |
GET | /v3/partners/capital-allocators/{uuid}/commitments/ | Commitments by allocator | ordering, limit, offset |
GET | /v3/partners/capital-allocators/{uuid}/aum/ | Allocator AUM history | ordering, limit, offset |
GET | /v3/partners/capital-allocators/{uuid}/investments/ | Investments by allocator | limit, offset |
GET | /v3/partners/capital-allocators/{uuid}/financials/ | Historical financial statements | limit, offset |
GET | /v3/partners/capital-allocators/{uuid}/captable/ | Cap table for allocator’s legal entity | limit, offset |
GET | /v3/partners/capital-allocators/{uuid}/funds/ | Fund profiles managed by allocator | limit, offset |
GET | /v3/partners/capital-allocators/{uuid}/news/ | News articles | ordering, limit, offset |
Funds
| Method | Endpoint | Purpose | Key params / body |
|---|---|---|---|
GET | /v3/partners/funds/ | List | search, ordering, limit, offset |
POST | /v3/partners/funds/ | Filter | JSON filters, search, ordering, limit, offset |
GET | /v3/partners/funds/{uuid}/ | Detail | — |
GET | /v3/partners/funds/{uuid}/performance/ | Performance history | ordering, limit, offset |
GET | /v3/partners/funds/{uuid}/aum/ | AUM / size history | ordering, limit, offset |
GET | /v3/partners/funds/{uuid}/commitments/ | Commitments to fund | ordering, limit, offset |
GET | /v3/partners/funds/{uuid}/investments/ | Investments by fund | limit, offset |
GET | /v3/partners/funds/{uuid}/news/ | News articles | ordering, limit, offset |
People
| Method | Endpoint | Purpose | Key params / body |
|---|---|---|---|
GET | /v3/partners/people/ | List | search, role_type_key, role_type_name, ordering, limit, offset |
POST | /v3/partners/people/ | Filter | JSON filters, search, role_type_key, role_type_name, ordering, limit, offset |
GET | /v3/partners/people/{uuid}/ | Detail | — |
GET | /v3/partners/people/{uuid}/roles/ | Org roles | role_type_key, role_type_name, limit, offset |
GET | /v3/partners/people/{uuid}/investments/ | Investments by person | limit, offset |
GET | /v3/partners/people/{uuid}/news/ | News articles | ordering, limit, offset |
Investors
| Method | Endpoint | Purpose | Key params / body |
|---|---|---|---|
GET | /v3/partners/investors/ | Discover investors | investor_type, search, invested_in_stage, invested_on_from, invested_on_to, ordering, limit, offset |
POST | /v3/partners/investors/ | Filter discovery | JSON filters with investor_type and/or name/search, plus pagination |
Service Providers
| Method | Endpoint | Purpose | Key params / body |
|---|---|---|---|
GET | /v3/partners/service-providers/ | List | service_type, search, ordering, limit, offset |
POST | /v3/partners/service-providers/ | Filter | JSON filters, service_type, search, ordering, limit, offset |
GET | /v3/partners/service-providers/{uuid}/ | Detail | — |
Reference Data
| Method | Endpoint | Purpose | Key params / body |
|---|---|---|---|
GET | /v3/partners/reference-data/ | Enums, countries, cities, industries, SIC codes | type, enum_categories |
Coverage Gap Summary
v2 fields, filters, and endpoints that have no direct v3 equivalent.
Fields Not in v3
| Category | v2 field(s) | Severity | Workaround |
|---|---|---|---|
| Companies | additional_ids | Low | Not migrated. |
| Companies | size_of_last_round, date_of_last_round | Medium | Available on CR detail as funding.latest_investment_amount and funding.latest_investment_date. |
| Companies | date_incorporated (full date) | Low | Available in v3 as legal_entity.date_founded (with date_founded_precision); year_founded also retained. |
| Companies | liquidation_details | Low | Deprecated; no v3 replacement. |
| Company Financials | fundings[] pre-money valuation | Medium | Per-round detail available via /capital-receivers/{uuid}/deals/. pre_money_valuation is not exposed. |
| Company Financials | fundings[].average_share_price_paid | Low | Not exposed. deals[].price_per_share_usd carries the highest price in the round, not the average. |
| Company Financials | additional_fundings[] (news-sourced rounds) | Medium | Not in Partner API. |
| Company Financials | revenue[] multi-year history | Medium | CR detail has a condensed financials[]. For the full statement history use /capital-receivers/{uuid}/financials/. |
| Company Financials | shareholders[].value_of_investment_at_last_round_valuation | Medium | Not in Partner API. |
| Company Financials | per_share_class_summary[] (raw share_class_id) | Low | Largely covered by deals[].transactions[]. Raw integer share_class_id is unavailable. |
| Investors | investor_uen on list | Low | Use /capital-receivers/{uuid}/investors/ registration_numbers[].reg_number. |
| Investors | investment_date on list | Low | Use first_investment_date / latest_investment_date on /capital-receivers/{uuid}/investors/. |
| Investors | Per-company stage amounts on /investments/ | Medium | Only on investor list as investments_by_deal_type; not per-company on /investments/. |
| Investors | Valuation calculations (value_of_investment_at_last_round_valuation*) | Medium | Not in Partner API. |
| Investors | Share-class amounts (amount_invested_ordinary, _preference) | Low | Not in Partner API. |
| Investors | max_price_per_share | Low | Not in Partner API. |
| Fund list | Inline performance metrics (irr, dpi, rvpi, net_multiple) | Medium | Separate call to /funds/{uuid}/performance/. |
| Fund list | size on list | Medium | Separate call to /funds/{uuid}/aum/. |
| Fund Performance | source_name, capital_provider_source_acting_as | Low | Not exposed. |
| Fund Performance | report_path, reporting_period | Low | Use year + quarter; report path is internal. |
| Commitment Deals | size (deal size) | Low | Not in Partner API. |
Filters Not in v3
| Category | v2 filter(s) | Workaround |
|---|---|---|
| Companies | total_funding_min/max (server-side aggregate) | Not available as a server-side filter. |
| Investors | sectors, themes, invested_in_stage, invested_on_from/to | Not available in Partner API; filter client-side or via other endpoints. |
| Investors | order_by + order_direction | Not available; no server-side ordering on investor results. |
| Funds | net_irr_min/max, net_multiple_min/max, dpi_min/max, rvpi_min/max | Not available; performance is separate from fund list. |
| Funds | last_report_quarter | Not available. |
| Commitment Deals | fund_type, query | Filter funds first, then query per-fund commitments. |
v2 Endpoints Fully Deprecated
| v2 endpoint | Reason / replacement |
|---|---|
GET /api/v2/companies/{uen}/uen/financials | Use ?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 call | v3 equivalent |
|---|---|
POST /api/v2/oauth/token | POST /v3/token/issue/ |
GET /api/v2/companies?query=X&countries=SGP | POST /v3/partners/capital-receivers/ with search=X and {"filters":{"all":[{"op":"eq","field":"headquarters_country_iso_alpha3","value":"SGP"}]}} |
GET /api/v2/companies/123 | GET /v3/partners/capital-receivers/{uuid}/ |
GET /api/v2/companies/201935876D/uen | GET /v3/partners/capital-receivers/?registration_number=201935876D |
GET /api/v2/companies/123/financials | GET /v3/partners/capital-receivers/{uuid}/ (funding block + financials[]) |
GET /api/v2/investors?query=X | GET /v3/partners/investors/?search=X |
GET /api/v2/investors/4 | GET /v3/partners/investors/ → find UUID → /capital-allocators/{uuid}/investments/ |
GET /api/v2/directors?query=X | GET /v3/partners/people/?role_type_key=person_association_director&search=X |
GET /api/v2/directors/4 | GET /v3/partners/people/{uuid}/ |
GET /api/v2/founders?query=X | GET /v3/partners/people/?role_type_key=person_association_founder&search=X |
GET /api/v2/founders/2 | GET /v3/partners/people/{uuid}/ |
GET /api/v2/auditors?query=X | GET /v3/partners/service-providers/?service_type=service_provider_type_audit&search=X |
GET /api/v2/auditors/2 | GET /v3/partners/service-providers/{uuid}/ |
GET /api/v2/capital-providers?category=fund-manager | GET /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=2020 | POST /v3/partners/funds/ with {"filters":{"all":[{"op":"gte","field":"vintage_year","value":2020}]}} |
GET /api/v2/funds/477 | GET /v3/partners/funds/{uuid}/ |
GET /api/v2/fund-performances/?fund_id=508 | GET /v3/partners/funds/{fund-uuid}/performance/ |
GET /api/v2/commitment-deals/?fund_id=764 | GET /v3/partners/funds/{fund-uuid}/commitments/ |
GET /api/v2/commitment-deals/?limited_partner_id=3521 | GET /v3/partners/capital-allocators/{allocator-uuid}/commitments/ |
GET /api/v2/people/?first_name=Arjun | GET /v3/partners/people/?search=Arjun |
Migration Checklist
- Update base URL from
https://api.alternatives.pe/api/v2/tohttps://api.altdmp.io/v3/partners/ - Replace
client_id/client_secretauth with the API-key flow onPOST /v3/token/issue/ - Confirm your subscription gives you access to the v3 endpoints you need (403 = contact support)
- Replace integer IDs with UUIDs in all API calls and persisted references
- Update pagination: parse
count,next,previous,resultsinstead of the v2data.data[]envelope - Convert
order_by/order_directionto singleorderingparameter (-prefix for descending) - Move advanced filters from query parameters to POST
filtersJSON bodies — afiltersquery parameter returns400 - Remove
sectorsusage and remap onto the v3 classification framework (themes,techs,business_models,industries,horizontals) - Update company lookups to
/capital-receivers/; look up by UEN via?registration_number= - Update capital-provider lookups to
/capital-allocators/; readprofile_typefrom the response - Replace auditor lookups with
/service-providers/?service_type=service_provider_type_audit - Consolidate director/founder calls to
/people/?role_type_key=person_association_director|founder - Move person
job_titlesreads to/people/{uuid}/roles/(or the batchPOST /people/roles/, up to 1,000 person UUIDs per request); readtitleinstead ofjob_title, androle_type.key/role_type.nameinstead ofrole_type_key/role_type_name - Handle the nested
legal_entityobject in profile responses - Update field names per the mapping tables (
first_name→given_name,last_name→family_name, etc.) - Update fund consumers: fetch performance via
/funds/{uuid}/performance/and AUM via/funds/{uuid}/aum/ - Migrate commitment-deal logic to the two nested commitments endpoints (per fund vs. per allocator)
- Adopt
/v3/partners/investors/and per-profile/investments/endpoints; expect investor records to split by allocation type - Integrate
GET /v3/partners/reference-data/to bootstrap filter dropdowns - Stop applying client-side FX — money fields are USD-normalized with
_usdsuffix - Smoke-test all endpoints in a non-prod environment before switching production traffic
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 send | Result |
|---|---|
filters in a POST body | Applied |
filters in ?filters=… | 400 Bad Request on both GET and POST |
?filters= with no value | Accepted — expresses no filter |
search / ordering / limit / offset in the body | 400 Bad Request. Only filters may be sent in the body |
search / ordering / limit / offset on the query string | Applied, 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-stringfiltersinstead 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"}]}}
all— AND. Takes an array.any— OR. Takes an array.not— NOT. Takes a single condition object, or an array, which is negated as a whole (the same as wrapping it inallfirst).
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, andcaptable_is_managedare computed only when the endpoint sees the field inside afieldkey — which the field-keyed form does not produce.{"filters": {"latest_valuation_usd": {"op": "gte", "value": 50000000}}}returns400 Bad Request. Use the logical form for these fields:{"filters": {"all": [{"op": "gte", "field": "latest_valuation_usd", "value": 50000000}]}}
financials_latest_financial_year_endhas 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:
| Shorthand | Equivalent 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
| Body | Result |
|---|---|
{"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 expected | 400 |
{"op": "range", "field": "…", "value": [1]} — range without exactly two values | 400 |
{"all": [{"field": "display_name", "value": "Acme"}]} — a condition missing op | 400 |
{"display_name": {"value": "Acme"}} — the same omission in the field-keyed form | 400 |
{"op": "eq", "field": 7, "value": "Acme"} — a field that is a number or an empty string | 400 |
{"op": "eq", "field": "is_female_founder", "value": "yes"} — a value that cannot be read as the field’s type | 400, 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 nofield— contributed nothing to the query, so the request returned200and every row the other conditions allowed. It now returns400naming 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.
fieldmust 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"}returns500rather than a400; every other endpoint returns400. Either way the request is malformed — pass the field name as a string. Do not treat the500as 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
| Operator | Meaning | value shape | Case-sensitive |
|---|---|---|---|
eq | Equals | scalar | Usually — depends on the field |
ne | Does not equal | scalar | Usually — depends on the field |
in | Equals any of | array | Usually — depends on the field |
nin | Equals none of | array | Usually — depends on the field |
contains | Contains substring | string | No |
ncontains | Does not contain substring | string | No |
startswith | Starts with | string | No |
endswith | Ends with | string | No |
gt / gte | Greater than / or equal | number or date string | n/a |
lt / lte | Less than / or equal | number or date string | n/a |
range | Between two bounds, inclusive | array of exactly 2, low then high | n/a |
isnull | No value recorded | true | n/a |
notnull | A value is recorded | true | n/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."] }(
containsis the substring operator — there is nolike.) This changed in September 2026. Previously an unrecognized operator was silently treated aseqand returned200, so a typo inopread 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.
| Operators | Behavior |
|---|---|
contains, ncontains, startswith, endswith | Case-insensitive on every field |
eq, ne, in, nin | Case-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:
| Endpoint | Fields |
|---|---|
/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_typetakes the display name"Equity",allocation_type_keytakes 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.
- A code matches itself and everything beneath it.
{"op": "eq", "field": "industries_codes", "value": "16"}returns records tagged16,1601, or any16xxxxcode. - A code never matches its parents.
160101returns records tagged160101. It does not return a record tagged only16or1601, because that record has not been classified down to hospitals. - Sending every child of a node is the same as sending the node. All nine
1601xxleaves in oneinlist are treated as1601and, because1601is the only group under16, as16— so records tagged only16come back too. The same applies toeq,contains, orinconditions on the field grouped in oneany. A partial set of children never widens to the parent. When you mean the whole branch, send the parent code. - Matching is on whole codes.
0101never matches030101, which only contains it. eq,contains, andinfollow these rules, andne,ncontains, andninare their exact complement.containson a code is not a substring match.- Avoid
startswithandendswithon these fields. They compare each code as plain text and ignore the tree:startswith"1601"misses records tagged only16, whicheq"1601"returns, andendswith"0101"matches codes across every sector. - Only industries shown on the record’s response count toward a match.
{"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:
eqandinmatched only the exact code, andcontainswas a raw substring match, so"0101"also matched030101in another sector. Results move in both directions. A parent code now returns records tagged anywhere beneath it, so expect more rows; acontainsthat 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 return400, 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, andtransaction_labelwas accepted and ignored. Both now return400. It accepts exactly the Capital Receiver Deal Filter Fields, the same list as the batchPOST /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 responsefundingobject 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_startupselects startups, or everything else. A company is a startup when it has ever raised venture capital, or when its funding status isUnfunded startup.{"op": "eq", "field": "is_startup", "value": true}returns startups,falsereturns every other company, and leaving the field out returns every company, as before. It works underall,any, andnot, 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
eqorne—"true",1,null, or any other operator returns400. 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:
| Endpoint | Field or parameter | Where it goes |
|---|---|---|
POST /v3/partners/capital-receivers/ | is_startup | Filter body |
POST /v3/partners/capital-allocators/ | has_startup_investments | Filter body |
GET / POST /v3/partners/investors/ | capital_receiver_is_startup | Query string on GET, filters.all on POST |
GET / POST /v3/partners/people/, POST /v3/partners/people/roles/ | organization_is_startup | Query string |
A company is a startup when either is true:
- It has ever raised venture capital — a deal with the Venture Capital subtype, or a primary transaction with that subtype. Venture capital is counted only where Alternatives manages the company’s cap table, so a company whose cap table comes from an external source can qualify only through the second rule.
- Its funding status is
Unfunded startup: tagged Pre Venture Backed, founded 10 or fewer years ago (or with no known founding date), not listed, not a shell vehicle or a wholly owned or controlled subsidiary, and with no private equity, venture capital, venture debt, or convertible debt.
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_endis narrower than the rest, and it fails silently. It resolves through a separate lookup, which only recognizes a leaf carrying an explicitfieldkey, directly underall, with one ofeq,gt,gte,lt,lte. Everything else returns200with the condition dropped or misapplied:
Shape What 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, 200The leaf inside notInverted — returns exactly the rows you asked to exclude, 200The leaf inside anyANDed — narrower than the anyyou wroteUse only
eq/gt/gte/lt/lte, only directly underall, and never insidenotorany. For a two-sided window, passgteandlteas two conditions in the sameallrather thanrange.
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
orderinguse — pass1000000, not"1,000,000"or"1m". Use the numeric operators (gt,gte,lt,lte,range,eq,ne);containsand the other substring operators do not apply to them.cheque_size_min/cheque_size_avg/cheque_size_maxare three independent values, so a band around a firm’s typical cheque is a filter oncheque_size_avgrather than arangeacross the min and max fields. As with every numeric field, an allocator that has not stated the figure isnulland 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_investmentsselects firms that have invested in a startup.truereturns firms whereGET /capital-allocators/{uuid}/investments/has at least one row for a startup;falsereturns 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 witheqorne:{"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 againstactual_allocation_deal_typeson the response. Geography resolves through the allocator’s linked legal entity, so allocators backed by aPersonreturnnulland 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, againstpreferred_allocation_type_*singular on capital allocators. The classification flags are nullable —nullmeans undeclared, notfalse.
fund_manager_nameasks about the fund’s visible managers, one condition at a time. A fund can have several managers. Eachfund_manager_namecondition asks “does some manager match?” — or forne,nin,ncontains, andnot, “does no manager match?” — and managers that are unpublished or deleted are never considered. Two consequences:
allof twofund_manager_nameconditions 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.isnullmatches a fund with no visible manager, andnotnullone with at least one.searchon 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, andorganization_is_startupgo on the query string and combine with the body filters. Sending any of them insidefiltersreturns400 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_namereplaced a relation-path value in September 2026. This endpoint previously accepted onlylegal_entity__display_name, and ignored anything it did not recognize.display_nameis now the name to send; the old value returns400.
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 field | Matches |
|---|---|
total_deal_size_usd | Every deal with a size, whatever its provenance |
deal_size_usd | Only deals whose provenance is not Reported |
reported_deal_size_usd | Only deals whose provenance is Reported |
deal_transaction_size_usd | Same 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:
transaction_labelon the per-companyPOST /v3/partners/capital-receivers/{uuid}/deals/. Filter deals withdeal_labelorself_declared_labelinstead.funding_statusandhas_vc_transactionson capital receivers. Both are returned on thefundingobject, so that test can be applied to the rows you get back — and for the most common reason to want them, useis_startup.nationality_name,nationality_iso_alpha3,is_employee, andassociated_ca_typeson people. Nationality and employment status are not part of the people endpoint.
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:
| Endpoint | Parent UUID in filter | Other fields accepted |
|---|---|---|
POST /capital-receivers/deals/ | capital_receiver_uuid — optional, see Batch Deals | The full deal filter field set |
POST /capital-receivers/financials/ | capital_receiver_uuid — required | financial_year_end, uuid |
POST /people/roles/ | person_uuid — required | role_type_key, role_type_name |
POST /capital-receivers/news/ | capital_receiver_uuid — required | None |
POST /capital-receivers/deal-share-types/ | capital_receiver_uuid — required | None |
POST /capital-allocators/aum/ | capital_allocator_uuid — required | None |
POST /funds/performance/ | fund_uuid — required | None |
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/:
- The
infilter is required. Missing or empty returns400 Bad Request— these endpoints never return unscoped results. - The
inleaf must sit directly inside a root-levelall, exactly as shown above. The other body shapes this page documents are not recognized here:{"any": [...]}, a nested{"all": [{"all": [...]}]}, the field-keyed{"capital_receiver_uuid": {"op": "in", "value": [...]}}, and the list shorthand{"capital_receiver_uuid": [...]}each return400 Bad Requestsaying the IN filter is missing, even though the UUIDs are right there in the body.
And on all of them, batch deals included:
Maximum 1,000 UUIDs per request. More returns
400, as does any value that is not a valid UUID.Every returned row carries the parent UUID field, so you can join it back to the entity it belongs to.
Pagination is by
limit/offseton the query string, as everywhere else. Alimitin the body returns400— onlyfiltersmay be sent there.POST /capital-receivers/{uuid}/deals/behaves the same way:{ "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."] }A filter field the endpoint does not accept returns
400, and the message names it and lists what is supported. This includes a misspelled parent UUID field —capital_receiver_uuino longer behaves likecapital_receiver_uuid:{ "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 nesting an unsupported field under
anyornot, or writing it in the field-keyed form, does not get it past.
Both of these used to pass silently, and that is the change most likely to break an existing integration. A condition beyond the required
inwas accepted and dropped: asking for “news for these 500 companies, but only since January” returned every news row for all 500, with a200and nothing marking the discarded condition. A bodylimitwas 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 returning400— 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 body | Result |
|---|---|
{"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 substring operator on
date—contains,startswith,endswithand their negations.{"op": "contains", "field": "date", "value": "2025"}used to return a plausible-looking set that was not the query you meant. Use the comparison operators. - A non-ISO date, a
rangethat is not exactly two ISO dates, or anin/ninondatethat is not a list of ISO dates. - More than 1,000
capital_receiver_uuidvalues, or a malformed one, wherever in the filter tree they sit — nesting them inside ananyno longer evades the cap. - A node carrying two of
all/any/not, or one of them beside anop/fieldcondition. Only the first was ever applied, so a body narrowing to one company via a top-levelanyreturned every company with a200. Nest them instead:{"all": [<condition>, {"any": [<condition>]}]}.
A
dateunderanyornotfilters, 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-levelalland 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:
- Rows with no value sort last, in either direction.
-irrputs funds with no IRR at the end, not the start. The people list is the exception: onlyaggregate_updated_atsorts this way there. - An empty
?ordering=applies the route’s default, the same as leaving the parameter out. - Ties keep one fixed order. Every route breaks ties with a unique final key, so paging with
limitandoffsetnever skips or repeats a row, even when many rows share the sort value, as long as the rows do not change while you page. When you sort by a value that can change mid-pull, such asaggregate_updated_at, page by keyset instead; see Incremental Sync.
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
500for an unknown value, two silently fell back to-date, the investments pages ignoredorderingaltogether, 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-daterather than first, and on fund performance a descending sort now putsnullvalues 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:
| Cause | Effect |
|---|---|
financials_latest_financial_year_end outside its supported shapes | Condition dropped, or inverted under not |
Wrong casing under eq / ne / in / nin on a case-sensitive field | Zero 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
| Method | Endpoint | Access |
|---|---|---|
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, andregistration_numbersare all nested underlegal_entity— the capital receiver profile itself carries onlyuuid,description, the classification arrays,captable_source,is_raising_now,last_updated_at,aggregate_updated_at,funding, andlatest_financials. See Data Model.
fundingandlatest_financialsarenullwhen the company has no deals or no filed financials respectively.
List Query Parameters
| Parameter | Type | Description |
|---|---|---|
search | string | Matches company name, alternate names, and registration number |
registration_number | string | Filter by company registration number |
ordering | string | Field 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 |
limit | integer | Results per page (default: 20) |
offset | integer | Pagination offset |
total_funding_min | number | Minimum total equity raised (USD) |
total_funding_max | number | Maximum total equity raised (USD) |
is_raising_nowis a filter field on the advancedPOSTfilter, not aGETquery 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:
filtersmust be an object whose top level isall(AND),any(OR), ornot(NOT) — wrap even a single condition inall. Blocks nest to any depth.eq,ne,in, andninmatch case-insensitively on this endpoint’s single-valued text fields —display_name,description, the geography fields,trading_status_*, andlatest_investment_stage_*— and case-sensitively on the multi-valuethemes_*,horizontals_*,techs_*, andbusiness_models_*fields; the substring operators are case-insensitive everywhere, and an unrecognizedopreturns400 Bad Request. Use theall/any/notform 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
| Field | Type | Example |
|---|---|---|
uuid | string | "c16a0ffd-4dbb-4f7b-a9ca-a3a47f93be67" — the capital receiver profile UUID; pair with in to re-fetch a known set |
headquarters_country_iso_alpha3 | string | "SGP" |
domicile_country_iso_alpha3 | string | "SGP" |
latest_investment_stage_name | string | "Series A" |
latest_investment_stage_key | string | "series_a" |
latest_investment_date | date string | "2022-12-13" |
latest_valuation_usd | number | 50000000 |
latest_operating_revenue_usd | number | 10000000 |
operating_revenue_growth_yoy_pct | number | 15.0 |
total_funding_usd | number | 5000000 |
financials_latest_financial_year_end | date string | "2023-01-01" |
captable_is_managed | boolean | true |
is_female_founder | boolean | true |
is_raising_now | boolean | false |
is_startup | boolean | true — filter only, not returned on rows; see below |
year_founded | integer | 2018 |
date_founded | date string | "2014-04-01" |
aggregate_updated_at | date-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_foundedfilters on the full founding date and supportsgte,lte,range, andeq. The response also exposesdate_founded_precision("year","month", or"day") so you can tell how exact adate_foundedvalue is.year_founded(year only) is retained for backward compatibility; sorting with?ordering=year_foundedis 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, andtotal_funding_usdare computed on demand — only when they appear in the filter body or inordering— so a plain list request pays nothing for them.
latest_investment_dateis 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 v2date_of_last_roundsort. Companies with no deals sort last in both directions.
These field names match the response
fundingobject, so you read, filter, and sort by the same key (e.g.?ordering=-total_funding_usd).
funding_statusandhas_vc_transactionsare response fields, not filter fields. Both are on thefundingobject, and neither has ever narrowed a result set — a filter body naming one used to be accepted and ignored, and returns400 Bad Requestnaming 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:truereturns companies with a managed cap table (full transaction-level data available via/captable/and/investors/);falsereturns snapshot-only companies.null(no filter) includes both. Companies with no cap table at all are excluded from botheq:trueandeq:falsequeries.
is_startup:truereturns startups — companies that have ever raised venture capital, or whose funding status isUnfunded startup— andfalsereturns every other company. The value must be a JSON boolean witheqorne. The classification is updated periodically, so a newly funded company can take a while to appear undertrue. 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
| Field | Type | Description |
|---|---|---|
financials | array | Condensed 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_audited | array | Audited financial-statement documents: year, date_of_file, and a signed, expiring url — see the note below. |
financial_statements_extracted | array | Machine-extracted statement documents, same shape as above. |
funding_status | string | One 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_exits | boolean, nullable | Whether the company has any deal of that kind on record. |
The
urlvalues are signed and expire one hour after the response. Every request mints a fresh signature, and theExpiresparameter 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 Forbiddenwith 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
Authorizationheader 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:
| Field | Description |
|---|---|
filed_equity_usd | Equity from official filings (or, for some domiciles, paid-up capital) |
reported_equity_usd | Equity from reported (e.g. announced) rounds not yet reflected in filings |
reported_and_filed_equity_usd | filed_equity_usd + reported_equity_usd |
filed_debt_usd / reported_debt_usd / reported_and_filed_debt_usd | The equivalent breakdown for debt |
total_funding_usd | reported_and_filed_equity_usd + reported_and_filed_debt_usd |
latest_valuation_usd / latest_valuation_date | Most recent known valuation and its date |
latest_investment_amount / latest_investment_date / latest_investment_stage_name / latest_investment_stage_key | Most recent funding round summary |
nullfunding amounts. A funding amount isnullwhen it is unknown or known to net to zero — these fields never return0.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 thetotal_funding_min/total_funding_maxfilters), since anullvalue never satisfies a numeric comparison.
Public-listed companies. Companies classified as public no longer return funding or valuation — every amount in the
fundingobject (andlatest_valuation_usd) isnull, since a public company has a market capitalization rather than private funding.funding_statusis 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
}
}
| Field | Type | Notes |
|---|---|---|
shareholder | object | uuid, 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_* | integer | Share counts. |
total_invested_usd_* | number, nullable | Cash invested in USD. |
percentage_held_* / holding_value_usd | number, nullable | percentage_held_* is a percentage — 18.50 means 18.5% of the company. |
is_held_in_treasury | boolean | Shares held in treasury by the company itself. |
child_entities_count / child_entities | integer / array | Controlled 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
countandaggregations, but every remaining row keeps its percentage of the whole company. Soaggregations.percentage_held_aggregatecan read below100.00—61.80where hidden holders held the rest. A visible holder’s own figures are unchanged, including shares bought from a hidden seller, soshares_bought_secondary_aggregatecan exceedshares_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}. Readresultsrather than assumingcountis present.
/investors/returnsHTTP 400for 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
}
}
]
}
| Field | Type | Notes |
|---|---|---|
uuid / name / investor_type | string | The 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_numbers | array | Present for entity-backed investors: reg_number, authority_type, authority_name. |
registration_number | string, nullable | Present instead of registration_numbers for person investors — a single identifier rather than a list. |
shares_* / current_share_holding_percentage / amount_invested_usd | number, nullable | current_share_holding_percentage is a percentage — 12.5 means 12.5%. |
investment_by_stage_amount_usd | object | Always 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) orregistration_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_percentageis 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, socurrent_share_holding_percentageacross 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
| Field | Type | Description |
|---|---|---|
uuid | string | The deal’s own UUID — stable across requests, so use it to deduplicate or to key deals in your own store. |
capital_receiver_uuid | string, nullable | The company the deal belongs to. Present on the per-company endpoint too, not only the batch endpoint. |
date | date string | Deal date. |
investment_quarter | string, nullable | Calendar quarter of date, e.g. "Q4 2022". |
first_investment_date / last_investment_date | date string, nullable | First and last transaction dates within the round; both fall back to date when the round has a single dated event. |
self_declared_label | string, nullable | The round name as the company describes it. Can differ from allocation_deal_type_name. |
allocation_deal_type_name / allocation_deal_type_key | string, nullable | Normalized round stage, e.g. "Series F" / "series_f". |
allocation_type_name / allocation_type_key | string, nullable | Asset class, e.g. "Equity" / "equity". |
allocation_subtype_name / allocation_subtype_key | string, nullable | Sub-classification, e.g. "Venture Capital (VC)" / "venture_capital". |
share_class | string, nullable | All 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_issued | integer, nullable | Shares 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_bought | integer, nullable | Total shares bought in the deal’s Secondary transactions that name a buyer. null when the deal has none. |
no_shares_sold | integer, nullable | Total shares sold in the deal’s Secondary transactions that name a seller. null when the deal has none. |
provenance | object, nullable | Where 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_usd | number, nullable | Round 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_usd | number, nullable | Round size in USD when the deal’s provenance is not Reported (i.e. it is filed or otherwise sourced). |
reported_deal_size_usd | number, nullable | Round 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_usd | number, nullable | Post-money valuation in USD. |
price_per_share_usd | number, nullable | Highest per-share price across the round’s transactions, falling back to the deal-level price when no transaction records one. |
description | string, nullable | Body of the linked news article, falling back to the deal’s public notes. |
headline | string, nullable | Headline of the linked news article, falling back to the deal’s public notes. |
count_of_transactions | integer | Number of transactions in transactions. |
transactions | array | Individual 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_usdis 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_usdstarts at the largest filed round and never surfaces aReportedone, because those arenullin that field. Usetotal_deal_size_usdfor “biggest rounds”, and the split pair only when the source of the number matters to you.
Transaction Fields
| Field | Type | Description |
|---|---|---|
uuid | string | Transaction UUID. |
date | date string | Transaction date — can differ from the deal’s date. |
nature_of_transaction | string, nullable | e.g. "Primary", "Secondary". |
transaction_type | string, nullable | Transaction-level stage label, e.g. "Series F". |
allocation_type_key / allocation_type_name / allocation_subtype_key / allocation_subtype_name | string, nullable | Transaction-level classification, which can differ from the deal-level values. |
share_class | string, nullable | The single share class for this transaction. |
provenance | object, nullable | Where this transaction’s information came from, as {key, name}. Can differ from the deal-level provenance. |
number_of_shares | integer | Shares transacted. |
transaction_size_usd | number, nullable | Cash plus non-cash consideration in USD. null when it nets to zero. |
price_per_share_usd | number, nullable | Price 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 / seller | object, nullable | The counterparties. null when the side is not recorded. |
Buyer and Seller Objects
| Field | Type | Description |
|---|---|---|
uuid | string, nullable | Identifier of the counterparty, in whichever namespace type_key names. |
name | string, nullable | Display name. |
type_key | string, nullable | What kind of thing uuid is — join on this. One of capital_allocator, capital_receiver, fund, legal_entity, person, or shareholder_group. |
type | string, nullable | Human-readable label for the same thing. Presentation only — do not join on it. |
registration_numbers | array | Present for entity counterparties: a list of {reg_number, authority_type, authority_name}. |
registration_number | string, nullable | Present instead for person counterparties — a single string. |
allocation_type | string | Added 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_key | Use 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_entity | No detail endpoint. Use it as a correlation key to group rows for the same registered company |
shareholder_group | No detail endpoint. name and uuid are all that is exposed |
Read
type_key, nottype.typecurrently 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 bothtransactions[]amounts.transactions[].price_per_share_usdwas 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.
| Field | Type | Example |
|---|---|---|
date | date string | "2020-01-01" |
deal_label | string | "Series A" |
self_declared_label | string | "Series A" |
allocation_type | string | "Equity" |
allocation_subtype | string | "Venture Capital (VC)" |
deal_transaction_type | string | "Seed", "Series A" |
deal_size_usd | number | 5000000 |
post_money_valuation_usd | number | 50000000 |
no_shares_issued | integer | 1000000 |
no_shares_bought | integer | 500000 |
no_shares_sold | integer | 100000 |
# 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
- More than 1,000
capital_receiver_uuidvalues, or a value that is not a valid UUID, returns400 Bad Request— counted wherever the values sit in the filter tree. - A request with no
capital_receiver_uuidand no lower-boundeddatereturns400 Bad Request; this endpoint never returns an unbounded result set. - A field this endpoint does not support returns
400, and the message lists the ones it does. The per-company endpoint enforces the same list. - A filter node carrying two of
all/any/not, or one of them beside a condition, returns400. Nest them instead. - Only
filtersmay be sent in the body. Alimit,offset, ororderingkey there returns400 Bad Request; pass them as query parameters.
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".
- A missing or empty
capital_receiver_uuidinfilter returns400 Bad Request(this endpoint never returns unscoped results). - More than 1,000 UUIDs, or a value that is not a valid UUID, returns
400 Bad Request. - No other filter condition is accepted. Any other field — including a misspelled
capital_receiver_uuid— returns400 Bad Requestnaming it and listing what is supported. Filter these rows client-side, or use the per-entity endpoint. - Only
filtersmay be sent in the body. Alimit,offset, ororderingkey there returns400 Bad Request; passlimitandoffsetas query parameters. This endpoint returns rows in a fixed order and takes noordering, so sending it on the query string returns400too.
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_pctfields here, andoperating_revenue_growth_yoy_pcton the list endpoint) returnsnullwhen the computed change exceeds +1000% or falls below −1000%. Values at exactly ±1000% are retained. This suppresses meaningless swings off a near-zero base. Anulltherefore 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 oforderingon these fields, sincenullnever 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.
- A missing or empty
capital_receiver_uuidinfilter returns400 Bad Request(this endpoint never returns unscoped results). - More than 1,000 UUIDs, or a value that is not a valid UUID, returns
400 Bad Request. - Beyond
capital_receiver_uuid, this endpoint acceptsfinancial_year_endanduuid— the fields listed under Batch Financials Filter Fields. Any other field, including a misspelled one, returns400 Bad Requestnaming it and listing what is supported. - Only
filtersmay be sent in the body. Alimit,offset, ororderingkey there returns400 Bad Request; passlimitandoffsetas query parameters. This endpoint returns rows in a fixed order and takes noordering, so sending it on the query string returns400too.
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
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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"}
}
]
}
- A missing or empty
capital_receiver_uuidinfilter returns400 Bad Request(this endpoint never returns unscoped results). - More than 1,000 UUIDs, or a value that is not a valid UUID, returns
400 Bad Request. - No other filter condition is accepted. Any other field — including a misspelled
capital_receiver_uuid— returns400 Bad Requestnaming it and listing what is supported. Filter these rows client-side, or use the per-entity endpoint. - Only
filtersmay be sent in the body. Alimit,offset, ororderingkey there returns400 Bad Request; passlimitandoffsetas query parameters. This endpoint returns rows in a fixed order and takes noordering, so sending it on the query string returns400too.
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
| Method | Endpoint | Access |
|---|---|---|
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 owndisplay_nameand the linked legal entity’sdisplay_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
uuidvalues and the same underlyinglegal_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_keytells you which oflegal_entity/personis populated:"legal_entity"fills thelegal_entityobject and leavespersonasnull;"person"does the reverse and returns{"uuid", "given_name", "family_name", "display_name", "location_country"}underperson. Branch onprofile_type_key, not on theprofile_typelabel. Thelegal_entityobject 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 commonlynull—nullmeans “not stated”, which is not the same asfalse.
List Query Parameters
| Parameter | Description |
|---|---|
search | Full-text search (see above) |
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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, andninmatch case-insensitively on this endpoint’s single-valued text fields —display_name,description, the fourdomicile_*/headquarters_*country fields, andpreferred_allocation_type_name/preferred_allocation_type_key— and case-sensitively on the multi-valuepreferred_*andtypes_*fields. An unrecognizedopreturns400 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
| Field | Type | Example |
|---|---|---|
uuid | string | "3d9a5f74-2c18-4b6e-9a03-8e7f1c2d4b56" — the capital allocator profile UUID; pair with in to re-fetch a known set |
aggregate_updated_at | date-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_alpha3 | string | "SGP" |
domicile_country_name | string | "Singapore" |
headquarters_country_iso_alpha3 | string | "SGP" |
headquarters_country_name | string | "Singapore" |
preferred_allocation_deal_types_names | string | "Seed" |
preferred_allocation_deal_types_keys | string | "seed" |
types_names / types_keys | string | "Venture Capital Firm" / "ca_type_venture_capital_firm" |
preferred_allocation_type_name / preferred_allocation_type_key | string | "Equity" / "equity" |
preferred_allocation_subtypes_names / preferred_allocation_subtypes_keys | string | "Venture Capital (VC)" / "venture_capital" |
preferred_countries_names / preferred_countries_iso_alpha3 | string | "Singapore" / "SGP" |
preferred_themes_names / preferred_themes_keys | string | "Payments" / "themes_payments" |
preferred_horizontals_names / preferred_horizontals_keys | string | "Marketplaces" / "horizontals_marketplaces" |
preferred_business_models_names / preferred_business_models_keys | string | "B2B (Business-to-Business)" / "business_models_b2b" |
preferred_techs_names / preferred_techs_keys | string | "FinTech" / "techs_fintech" |
preferred_fund_types_names / preferred_fund_types_keys | string | "Venture - General / Other" / "venture_general_other" |
preferred_industries_codes | string | "09" — matches the code and every code beneath it; see Industry Codes |
cheque_size_avg / cheque_size_min / cheque_size_max | number | 3000000 |
dry_powder / median_valuation / current_allocation / target_allocation | number | 85000000 |
stated_count_of_investments | integer | 38 |
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 | boolean | true |
display_name / description | string | "Meridian" |
has_startup_investments | boolean | true — filter only, not returned on rows; see below |
has_startup_investmentsreturns firms whose investments include at least one startup withtrue, and every other firm — including firms with no investments — withfalse. 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 witheqorne. 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
_namesor_keys—preferred_allocation_deal_types_names, notpreferred_allocation_deal_type_name. Singular forms return400 Bad Request. The one exception is the single-valuedpreferred_allocation_type, which takespreferred_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 aPerson(rather than a legal entity) returnnullfor 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
| Field | Type | Description |
|---|---|---|
uuid | string | Capital allocator profile UUID |
display_name | string | Firm or individual name |
profile_type_key | string | "legal_entity" or "person" — branch on this. Tells you which of the legal_entity / person objects is populated; the other is null |
profile_type | string | Human-readable label for the same thing, currently "legalentity" / "person". Presentation only — do not branch on it, and see Keys and Display Values |
legal_entity | object, nullable | Underlying registered company. See Data Model |
person | object, nullable | uuid, given_name, family_name, display_name, location_country — for person-backed allocators |
types | array | Firm-type classification (e.g. Venture Capital Firm, Family Office) |
preferred_allocation_type | object, nullable | The 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_types | array | Stated preferred deal stages |
preferred_allocation_subtypes | array | Stated preferred allocation subtypes |
preferred_countries | array | Target geographies (iso_alpha3 + name) |
preferred_themes | array | Target investment themes |
preferred_horizontals | array | Target horizontal tech categories |
preferred_business_models | array | Target business models |
preferred_techs | array | Target technology focus areas |
preferred_fund_types | array | Fund types the firm allocates to |
preferred_industries | array | Target industries (code + description) |
preferred_other_preferences | string, nullable | Free-text preferences that don’t fit the structured fields |
route_to_market | array of strings, nullable | How the firm deploys, e.g. ["Direct Deals"] |
cheque_size_avg / cheque_size_min / cheque_size_max | integer, nullable | Cheque size in USD |
dry_powder | integer, nullable | Undeployed capital in USD |
median_valuation | integer, nullable | Median entry valuation in USD |
current_allocation / target_allocation | integer, nullable | Capital deployed and target total, in USD |
current_allocation_date | date string, nullable | As-of date for current_allocation |
stated_count_of_investments | integer, nullable | Self-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_account | boolean, nullable | Self-declared preferences. null means “not stated” — treat it as unknown, not as false |
latest_aum | object, nullable | Most recent AUM record: uuid, aum_value_date, aum_value, aum_value_usd, reporting_currency |
actual_count_of_investments | integer | Distinct 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_techs | array of strings | Classifications the firm has actually transacted in — display names, not keys, and flat strings rather than {key, name} objects |
actual_industries | array | Industries actually invested in — {code, description} objects, unlike the other actual_* arrays |
actual_countries | array of strings | Countries of portfolio companies as country names (e.g. "Singapore"), not ISO alpha-3 codes |
actual_min_investment_amount_usd / actual_max_investment_amount_usd | number, nullable | Smallest 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_investmentscounts each commitment regardless of nature.Secondary buys were added in September 2026. Firms with secondary-market activity show higher
actual_count_of_investmentsvalues and longeractual_*lists than before, with no underlying data changed. The/investors/endpoint’sno_of_invested_companiesstill 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. Mostactual_*arrays return bare display-name strings with no key — match them onname, notkey.
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
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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"}
}
]
}
| Field | Type | Description |
|---|---|---|
uuid | string | UUID of the AUM record |
aum_value_date | date string | As-of date |
aum_value | number | Amount in the reporting currency |
aum_value_usd | number | Same amount normalized to USD |
reporting_currency | object, nullable | iso_code and name of the currency aum_value is denominated in |
aum_valueis in the reporting currency andaum_value_usdis normalized — they are equal only whenreporting_currency.iso_codeis"USD".
Query Parameters
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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"}
}
]
}
- A missing or empty
capital_allocator_uuidinfilter returns400 Bad Request(this endpoint never returns unscoped results). - More than 1,000 UUIDs, or a value that is not a valid UUID, returns
400 Bad Request. - No other filter condition is accepted. Any other field — including a misspelled
capital_allocator_uuid— returns400 Bad Requestnaming it and listing what is supported. Filter these rows client-side, or use the per-entity endpoint. - Only
filtersmay be sent in the body. Alimit,offset, ororderingkey there returns400 Bad Request; passlimitandoffsetas query parameters. This endpoint returns rows in a fixed order and takes noordering, so sending it on the query string returns400too.
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
| Field | Type | Description |
|---|---|---|
uuid | string | Commitment transaction UUID |
fund | object, nullable | The 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}/ |
date | date string, nullable | Commitment date. Frequently null, since regulator filings often carry no date |
allocation_type / allocation_subtype | object, nullable | {key, name} classification of the commitment |
provenance | object, nullable | Where the record came from, e.g. Filed with Regulator, Filed via FOIA, Reported |
transaction_currency | object, nullable | iso_code and name of the currency cash_value_transacted is denominated in |
cash_value_transacted | number, nullable | Committed amount in the transaction currency |
cash_value_transacted_usd | number, nullable | Same amount normalized to USD |
The amount fields here are named
cash_value_transacted*, notinvestment_amount_usd. Sort with?ordering=-dateand expect a long tail ofnulldates 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
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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_pctfield returnsnullwhen the computed change exceeds +1000% or falls below −1000%; values at exactly ±1000% are retained. Anulltherefore 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
| Parameter | Description |
|---|---|
limit | Results per page |
offset | Pagination 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
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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
| Method | Endpoint | Access |
|---|---|---|
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_entityhas noheadquarters. It is the compact variant:uuid,display_name,year_founded,date_founded,date_founded_precision,domicile_country,trading_status,registration_numbers. Geography beyond domicile — includingheadquarters— only appears on the detail response, even thoughheadquarters_country_iso_alpha3is filterable on the list.
status,vintage_year, and every nestedlegal_entitysub-object are nullable. Many fund records in the data set are thinly populated, so expectnullforstatus,vintage_year,domicile_country, andtrading_status, and[]forfund_managers.
List Query Parameters
| Parameter | Description |
|---|---|
search | Full-text search on fund and fund manager names |
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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, andninare case-sensitive on every field here exceptstructure, and that an unrecognizedopreturns400 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
| Field | Type | Example |
|---|---|---|
uuid | string | "0f8b2c11-7d3e-4a6f-8c92-1b5a4e7d9c30" — the fund profile UUID; pair with in to re-fetch a known set |
aggregate_updated_at | date-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_alpha3 | string | "CYM" |
domicile_country_name | string | "Cayman Islands" |
headquarters_country_iso_alpha3 | string | "SGP" |
headquarters_country_name | string | "Singapore" |
status_key | string | "fund_status_raising" |
vintage_year | integer | 2020 |
date_founded | date string | "2014-04-01" |
preferred_countries_iso_alpha3 | string | "SGP" |
preferred_allocation_types_names | string | "Series A" |
date_foundedfilters on the fund’s full founding date and supportsgte,lte,range, andeq. Thelegal_entityobject also exposesdate_foundedanddate_founded_precision("year","month", or"day") alongside the retained year-onlyyear_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
| Field | Type | Description |
|---|---|---|
uuid | string | Fund profile UUID |
legal_entity | object, nullable | The fund’s registered vehicle. See Data Model |
display_name | string | Fund name |
description | string, nullable | Fund description |
fund_managers | array | Managing capital allocator(s) — uuid + display_name. Use the uuid with /capital-allocators/{uuid}/. A manager whose record is unpublished or deleted is not listed |
status | object, nullable | Fund lifecycle status, e.g. {"key": "fund_status_closed", "name": "Closed"} |
vintage_year | integer, nullable | Year the fund was established / first close |
is_raising_now | boolean, nullable | Currently in fundraise |
last_updated_at | date-time string | Deprecated, 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_at | date-time string, nullable | When the record, or anything shown on it, last changed. Filter and sort on it to sync incrementally; see Incremental Sync |
term_years | integer, nullable | Fund term in years |
structure | string, nullable | Free-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 |
currency | object, nullable | The fund’s denomination currency: iso_code + name |
target_close_date | date string, nullable | Target 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_industries | array | Stated mandate. Empty arrays when nothing is declared |
preferred_other_preferences | string, nullable | Free-text mandate notes |
latest_fund_size | object, nullable | Most 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_fund | boolean, nullable | Classification flags. null means unclassified — not false |
management_fee_percentage / gp_commitment_percentage / hurdle_percentage / carry_percentage | number, nullable | Percentages, 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_yearsdown is detail-only.
Real Funds for Testing
| Fund | UUID | Size | Vintage |
|---|---|---|---|
| Wavemaker Pacific 1 | 73541611-0738-46fb-abbd-7e9dcf74b00c | $66M | 2017 |
| Bain Capital Asia III | 50280c30-d626-44db-8aff-7cdf1eb323bb | $3B | 2016 |
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
| Field | Type | Description |
|---|---|---|
uuid | string | Performance record UUID |
fund_uuid | string, nullable | The fund this row belongs to. Returned on the per-fund endpoint as well as the batch endpoint |
date | date string | Reporting-period end date |
year / quarter | integer / string, nullable | Reporting period, e.g. 2024 and "Q4" |
source | string, nullable | Data source display name — one of "Financial Statement", "Fund Manager", or "Limited Partner" |
source_type_key | string, nullable | Machine-readable source type — "fund_per_source_type_fs", "fund_per_source_type_fm", or "fund_per_source_type_lp" |
source_capital_allocator_uuid | string, nullable | The 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 |
currency | object, nullable | Reporting currency of the as-reported performance data; the _usd fields are always USD-normalized regardless of this value |
source_url | string, nullable | Link to the source document, when public |
irr | number, nullable | Internal Rate of Return as a fraction — 0.1800 means 18% |
irr_pct | number, nullable | The same figure as a percentage — 18.00 means 18%. Always exactly irr × 100, and null whenever irr is |
dpi | number, nullable | Distributions to Paid-In multiple. A multiple, not a percentage: 1.3000 means 1.3× |
rvpi | number, nullable | Residual Value to Paid-In multiple |
net_multiple | number, nullable | Total 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_usd | number, nullable | USD-normalized amounts |
created_at / last_updated_at | date-time string | When the record was first created and last changed |
irrdid 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, andirr_pctwas added beside it. Readirr_pctif you want the percentage; there is no need to multiply by 100 yourself.dpi,rvpi, andnet_multipleare multiples and get no_pctsibling — adpiof1.30is 1.3×, not 130%.
Coverage is sparse beyond the headline metrics. Expect
nullfor most of the cash-flow fields (retained_earnings_usdthroughdistributions_usd) on any given row.
Query Parameters
| Parameter | Description |
|---|---|
ordering | Sort 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_max | Range 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_max | Range filter on DPI |
rvpi_min / rvpi_max | Range filter on RVPI |
net_multiple_min / net_multiple_max | Range filter on net multiple |
limit | Results per page |
offset | Pagination 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.
- A missing or empty
fund_uuidinfilter returns400 Bad Request(this endpoint never returns unscoped results). - More than 1,000 UUIDs, or a value that is not a valid UUID, returns
400 Bad Request. - No other filter condition is accepted. Any other field — including a misspelled
fund_uuid— returns400 Bad Requestnaming it and listing what is supported. Filter these rows client-side, or use the per-entity endpoint. - Only
filtersmay be sent in the body. Alimit,offset, ororderingkey there returns400 Bad Request; passlimitandoffsetas query parameters. This endpoint returns rows in a fixed order and takes noordering, so sending it on the query string returns400too.
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
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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
| Field | Type | Description |
|---|---|---|
uuid | string | Commitment UUID |
date | date string | Commitment date |
allocation_type_key / allocation_type_name | string, nullable | Allocation type |
allocation_subtype_key / allocation_subtype_name | string, nullable | Allocation subtype |
investment_amount_usd | number, nullable | Total committed amount in USD |
transactions | array | Underlying transactions, one per committing LP. May be empty. |
transactions[].uuid | string | Transaction UUID |
transactions[].investment_amount_usd | number, nullable | Committed amount for this transaction in USD |
transactions[].buyer | object, nullable | The 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 nestedfundobject andcash_value_transacted*amounts instead oftransactions[]andinvestment_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 andcount. A deal with no transactions, or with a transaction whose buyer is not recorded, stays.investment_amount_usdis the deal’s recorded figure and does not change when a transaction is hidden.
Query Parameters
| Parameter | Description |
|---|---|
ordering | Sort field. Prefix with - for descending (default: -date). Supported: date, investment_amount_usd, created_at, updated_at. Anything else returns 400 — see Sorting |
limit | Results per page |
offset | Pagination 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
| Field | Type | Description |
|---|---|---|
capital_receiver_uuid | string | UUID of the capital receiver profile — use with /capital-receivers/{uuid}/ |
legal_entity_uuid | string | UUID of the underlying legal entity |
capital_receiver_name | string | Company name |
shares_issued | number | Shares acquired via primary transactions |
shares_bought_secondary | number | Shares acquired via secondary purchases |
shares_sold | number | Shares sold via secondary transactions |
shares_currently_held | number | Current net shareholding |
total_invested_usd | number | Total cash invested in USD (primary transactions only) |
first_investment_date | date | Date of first investment |
latest_investment_date | date | Date of most recent investment |
Query Parameters
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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_typeislegal_entity,person, orshareholder_group— it never resolves a firm to its capital allocator or fund profile. Filtering?investor_type=capital_allocatoror?investor_type=fundis 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
| Method | Endpoint | Access |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
uuid | string | UUID of the investor entity |
name | string | Investor display name |
investor_type | string | Base 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_key | string, nullable | Asset class key: equity, debt, allocation_type_commitment, or allocation_type_other |
allocation_type_name | string, nullable | Asset class display name: Equity, Debt, Commitment, Other |
total_invested_usd | number, nullable | Total primary capital deployed (USD), within whatever country or startup scope the request applies |
no_of_invested_companies | integer | Distinct companies invested in |
investments_by_deal_type | object | Breakdown 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 nofirst_investment_dateorlatest_investment_date. Useinvested_on_from/invested_on_toto filter by date, or call the per-company endpoint when you need the actual dates.
The
allocation_type_keyvalues are not uniformly prefixed:equityanddebtare bare, while commitment and other carry anallocation_type_prefix. Match on the exact strings above, or read them from reference data.
Query Parameters
| Parameter | Description |
|---|---|
search | Full-text search on investor name |
investor_type | Filter by base entity type: legal_entity, person, or shareholder_group. capital_allocator and fund are accepted but match nothing |
invested_in_stage | Filter by deal-stage key (e.g. seed, series_a) — the same keys as investments_by_deal_type |
invested_on_from | Investments on or after this date (YYYY-MM-DD) |
invested_on_to | Investments on or before this date (YYYY-MM-DD) |
capital_receiver_headquarters_country_iso_alpha3 | Scope to investors in portfolio companies headquartered in these countries (comma-separated ISO alpha-3, e.g. SGP,MYS) |
capital_receiver_domicile_country_iso_alpha3 | Scope to investors in portfolio companies domiciled in these countries (comma-separated ISO alpha-3) |
capital_receiver_is_startup | true or false (any case). Scope to investments in startups, or in companies that are not startups. See below |
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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.alllist of the field/operator pairs below and rejects anything else with400 Bad Requestand a message naming what it can accept. In particularanyandnotare not available here — combine your conditions inall, or issue separate requests.
filtersmust be a JSON object. A list or scalar —{"filters": ["any"]}— returns400 Bad Requestrather than applying no filter; omitfilters, or sendnullor{}, to get the unfiltered list. Each entry inallmust 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 returns400 Bad Request; on this endpoint the value is dropped and you get the complete unfiltered list with a200. Always send the filter in the request body — and do not treat a200from this endpoint as confirmation that a query-string filter was applied.
Supported field and operator combinations:
| Field | Operators |
|---|---|
investor_type | eq |
name | eq, contains |
search | eq, contains |
invested_in_stage | eq |
invested_on_from | eq, gte |
invested_on_to | eq, lte |
capital_receiver_headquarters_country_iso_alpha3 | eq, in |
capital_receiver_domicile_country_iso_alpha3 | eq, in |
capital_receiver_is_startup | eq, with a JSON boolean |
namematches as a substring under botheqandcontains.{"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
| Field | Type | Example |
|---|---|---|
investor_type | string | "legal_entity", "person", or "shareholder_group" — exact match |
name | string | "Temasek" — substring match under eq and contains |
invested_in_stage | string | "seed", "series_a" |
invested_on_from | date string | "2020-01-01" |
invested_on_to | date string | "2024-12-31" |
capital_receiver_headquarters_country_iso_alpha3 | list | ["SGP", "MYS"] |
capital_receiver_domicile_country_iso_alpha3 | list | ["SGP"] |
capital_receiver_is_startup | boolean | true |
The two
capital_receiver_*_country_iso_alpha3filters scope the investor list to those who have transacted in portfolio companies headquartered in — or domiciled in — the given countries. In aPOSTbody useop: inwith 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:
true— only transactions into startups count. Each row’stotal_invested_usdandno_of_invested_companiescover only the investor’s startup investments, and an investor with none drops out.false— only transactions into companies that are not startups count, including transactions with no company recorded.
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
uuididentifies depends oninvestor_type.
person— a Person UUID. Pass it straight to/people/{uuid}/.legal_entity— a Legal Entity UUID, not a profile UUID. It returns404on/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 bynameor registration number on the relevant profile list endpoint and match onlegal_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
| Method | Endpoint | Access |
|---|---|---|
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
| Parameter | Description |
|---|---|
search | Name search across given and family name. Multiple words are matched independently, so full names like Shanru Lai match regardless of word order. |
role_type_key | Filter by role: person_association_founder, person_association_director, person_association_employee |
domicile_country_iso_alpha3 | Filter by the domicile-country ISO alpha-3 code of an associated organization. Repeatable or comma-separated (e.g. SGP,USA). |
headquarters_country_iso_alpha3 | Filter by the headquarters-country ISO alpha-3 code of an associated organization. Repeatable or comma-separated. |
organization_is_startup | true 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 |
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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:
true— people with at least one role at a startup company.false— people with at least one role that is not at a startup: at another company, an investor, a service provider, or an organization not tracked on the platform.
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, andninmatchgiven_name,family_name, and the twolocation_country_*fields regardless of case, and that an unrecognizedopreturns400 Bad Request. A person’s location that has been deleted reads as no location:location_country_nameisnulland the location filters do not match through it.
People Filter Fields
| Field | Type | Example |
|---|---|---|
uuid | string | "7c1f9e02-3b4d-4a58-9f21-6d0e5b8c3a41" — the person UUID; pair with in to re-fetch a known set |
given_name | string | "Henry" |
family_name | string | "Chan" |
location_country_name | string | "Singapore" |
location_country_iso_alpha3 | string | "SGP" |
date_of_birth | date string | "1985-04-12" |
aggregate_updated_at | date-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 Requestnaming the field — including four undocumented names that used to be accepted and then ignored:nationality_name,nationality_iso_alpha3,is_employee, andassociated_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, andorganization_is_startupare query parameters, not filter-body fields. Sending any of them insidefiltersreturns400 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"
}
| Field | Type | Description |
|---|---|---|
uuid | string | Person UUID |
display_name | string | Full name as displayed |
given_name / family_name | string, nullable | Name parts |
location_country | object, nullable | Where the person is based — {iso_alpha3, name}. Note the list endpoint returns a flat location_country_name string instead of this object |
nationality | object, nullable | {iso_alpha3, name} |
date_of_birth | date string, nullable | Rarely populated |
biography | string, nullable | Free-text biography |
email / linkedin_url | string, nullable | Contact details |
last_updated_at | date-time string | Deprecated, use aggregate_updated_at. When the person’s own record was last edited. See last_updated_at is deprecated |
aggregate_updated_at | date-time string, nullable | When 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 nonationality,date_of_birth, orbiography; the detail returnslocation_countryas 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
| Field | Type | Description |
|---|---|---|
uuid | string | Role UUID |
person_uuid | string | The person holding the role. Returned on the per-person endpoint as well as the batch endpoint |
role_type | object, nullable | {key, name} — see Role Type Keys |
organization | object, nullable | The organization: uuid, display_name, profile_type_key, profile_type, plus type-dependent extras. See below |
title | string, nullable | Job title as recorded |
start_date / end_date | date string, nullable | Role dates. Both are commonly null |
is_current | boolean | Derived from end_date being null |
email / linkedin_url | string, nullable | Role-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_key | profile_type | Extra keys |
|---|---|---|
legal_entity | legalentity | registration_numbers — a list of {reg_number, authority_type} |
capital_allocator | capitalallocatorprofile | legal_entity — {uuid, display_name, registration_numbers} for the allocator’s backing company, when it has one |
service_provider | serviceproviderprofile | legal_entity — same shape as above |
capital_receiver | capitalreceiverprofile | none |
fund | fundprofile | none |
null | out_of_scope | none. 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_numbersentries have noauthority_name. They carry onlyreg_numberandauthority_type— narrower than theregistration_numbersreturned everywhere else in the API.
Roles Query Parameters
| Parameter | Description |
|---|---|
role_type_key / role_type_name | Filter to specific role types |
domicile_country_iso_alpha3 | Filter by the domicile-country ISO alpha-3 code of the associated organization. Repeatable or comma-separated. |
headquarters_country_iso_alpha3 | Filter 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.
| Key | Name |
|---|---|
person_association_founder | Founder |
person_association_director | Director |
person_association_employee | Employee |
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.
- A missing or empty
person_uuidinfilter returns400 Bad Request(this endpoint never returns unscoped results). - More than 1,000 UUIDs, or a value that is not a valid UUID, returns
400 Bad Request. - Beyond
person_uuid, this endpoint acceptsrole_type_keyandrole_type_name— the fields listed under Person Role Filter Fields. Any other field, including a misspelled one, returns400 Bad Requestnaming it and listing what is supported. - Only
filtersmay be sent in the body. Alimit,offset, ororderingkey there returns400 Bad Request; passlimitandoffsetas query parameters. This endpoint returns rows in a fixed order and takes noordering, so sending it on the query string returns400too.
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
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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
| Parameter | Description |
|---|---|
ordering | Sort 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 |
limit | Results per page |
offset | Pagination 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
| Method | Endpoint | Access |
|---|---|---|
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_entityhas noheadquarters. 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
nullforyear_founded,date_founded,domicile_country, andtrading_status, and[]forregistration_numbers.
Query Parameters
| Parameter | Description |
|---|---|
search | Substring 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_type | Filter by service type key (see table below) |
ordering | Sort 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 |
limit | Results per page |
offset | Pagination offset |
POSTto this path for an advanced filter body. Supported filter fields areuuid,display_name,description,legal_entity_uuid,service_type_key,service_type_name, andaggregate_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 returns400 Bad Requestnaming it. See the Filtering page for the grammar and operators.
Two things changed here in September 2026.
orderingis validated against the fields above, andlegal_entity__display_name— the only sort value it used to accept — now returns400; senddisplay_nameinstead. 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.
| Key | Name |
|---|---|
service_provider_type_audit | Audit |
service_provider_type_tax | Tax |
service_provider_type_compliance | Compliance |
service_provider_type_law_firm | Law Firm |
service_provider_type_investment_bank | Investment Bank |
service_provider_type_merchant_bank | Merchant Bank |
service_provider_type_commercial_bank | Commercial Bank |
service_provider_type_placement_agent | Placement Agent |
service_provider_type_fund_administrator | Fund Administrator |
service_provider_type_lender | Lender |
service_provider_type_valuation_firm | Valuation Firm |
service_provider_type_management_consultant | Management Consultant |
service_provider_type_financing_advisory | Financing Advisory |
service_provider_type_lp_consultant | LP Consultant |
service_provider_type_business_intermediary | Business Intermediary |
service_provider_type_bus_dev_company | Business Development Company |
service_provider_type_crowdfunding_platform | Crowdfunding Platform |
service_provider_type_insurance_provider | Insurance Provider |
service_provider_type_recruiting_firm | Recruiting Firm |
service_provider_type_software_provider | Software Provider |
Fetch the authoritative list at runtime with
GET /reference-data/?type=enums&enum_categories=service_typesrather 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
}
| Field | Type | Description |
|---|---|---|
uuid | string | Service provider profile UUID |
display_name | string | Firm name |
legal_entity | object, nullable | The 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_types | array | {key, name} per service offered — see Service Type Keys |
last_updated_at | date-time string | Deprecated, 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_at | date-time string, nullable | When the record, or anything shown on it, last changed. Filter and sort on it to sync incrementally; see Incremental Sync |
description | string, nullable | Profile-level description. Distinct from legal_entity.description, and usually null |
public_notes | string, nullable | Free-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
| Method | Endpoint | Access |
|---|---|---|
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
| Value | Returns |
|---|---|
enums | All enumerated choice values grouped by category |
countries | All countries with iso_alpha3 and name |
cities | Cities with name, state_name, and country_iso_alpha3 |
industries | Industry codes with code and description |
business_sic_codes | Business 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 Key | Count | Values (samples for the large categories) |
|---|---|---|
allocation_types | 4 | Commitment, Debt, Equity, Other |
allocation_subtypes | 9 | Commitment, Debt Transactions, Other, Venture Capital (VC), Private Equity (PE), Mergers & Acquisitions (M&A), Distressed Transactions, Liquidity Events, Other Equity |
allocation_deal_types | 75 | Pre-Seed, Seed, Series A – Series K+, Buyout / LBO, Growth / Expansion, IPO, Secondary Transaction, Convertible Debt, Term Loan, Bridge Loan, Commitment, Establishment, ESOP, … |
capital_allocator_types | 25 | Venture 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_from | 3 | Balance Sheet, Fund, Unknown |
person_role_types | 3 | Director, Employee, Founder |
service_types | 20 | Audit, 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_types | 75 | The 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_statuses | 14 | Announced, Closed, Raising, Evergreen, First Close, Second Close, Third Close, Fourth Close, Open, Open - With first close, Upcoming, Estimated, Liquidated, Unknown |
trading_statuses | 3 | Operating, Inactive, Closed |
deal_provenances | 3 | Filed with Regulator, Filed via FOIA, Reported |
fund_performance_sources | 3 | Financial Statement, Fund Manager, Limited Partner |
business_models | 3 | B2B (Business-to-Business), B2C (Business-to-Consumer), P2P (Peer-to-Peer) |
techs | 61 | AgriTech, AI / GenAI / MLTech, BioTech, CleanTech, DeepTech, EduTech, EnergyTech, FinTech, GovTech, HealthTech, HRTech, InsurTech, LegalTech, MarTech, PropTech, RetailTech, TransportTech, … |
themes | 40 | AI / 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, … |
horizontals | 12 | AI / 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
typeobject on news rows usesnews_type_*keys (e.g.news_type_funding,news_type_acquisition) that noenum_categoriesvalue exposes. Readtype.namefrom 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_typesreturns bareequityanddebtalongside prefixedallocation_type_commitmentandallocation_type_other. Never construct a key from a display name; always read it from this endpoint.
enumsvalues are{key, name}pairs,countriesare{iso_alpha3, name},citiesare{name, state_name, country_iso_alpha3}, and bothindustriesandbusiness_sic_codesare{code, description}(withbusiness_sic_codesaddingcountry_iso_alpha3). When atypeparameter 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:
- There is one more row, and
countgoes up by one. - The row that had absorbed the other investor changes. Its share counts,
amount_invested_usd,investment_by_stage_amount_usd, investment dates, andcurrent_share_holding_percentagenow cover only its own transactions. - An investor recorded only as a seller still has no row, because an investor needs at least one recorded purchase to be listed. Its sales are no longer netted against another investor’s holding.
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:
- A code matches itself and every code beneath it.
"16"returns records tagged16,1601, or any16xxxxcode, where before it returned only records tagged exactly16. Expect more rows from a parent code. - A code never matches its parents.
160101does not return a record tagged only16or1601. - Sending every child of a node is the same as sending the node, so all nine
1601xxleaves also return records tagged only16. containsmatches whole codes instead of substrings."0101"no longer matches030101, so acontainsthat relied on substring matching returns fewer rows.
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:
- An unrecognized value on the four routes that used to ignore it:
GET/POST /v3/partners/capital-receivers/{uuid}/deals/, the batchPOST /v3/partners/capital-receivers/deals/,GET/POST /v3/partners/people/, andGET/POST /v3/partners/investors/. The message names the accepted values./investors/sorts on one field at a time, so a comma-separatedorderingis now a400there too; it used to be read as a name sort. - Any
orderingon a route whose order is fixed, even one matching the route’s own order, such as-dateon batch news. That covers both cap tables, everyfinancials/route, the companyinvestors/route, personroles/,reference-data/, and the batchaum/,news/,deal-share-types/,performance/, androles/routes. These never read the parameter, so rows came back in an order you had not asked for. On a route for one record, an unknown UUID is still a404.
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:
- Only Filed with Regulator and Filed via FOIA transactions count. An investor known only from reported rounds has no row.
- An investor needs at least one recorded purchase to be listed. Before this, an investor recorded only as a seller could appear with a negative holding.
current_share_holding_percentageis now a share of the shares all investors currently hold, not of the shares issued in funding rounds. The figures match/captable/.
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.
| Field | Value |
|---|---|
no_shares_bought | Total shares bought in the deal’s Secondary transactions that name a buyer. null when the deal has none. |
no_shares_sold | Total 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 field | v3 field |
|---|---|
job_title | title |
role_type_key | role_type.key |
role_type_name | role_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.
| What | Where |
|---|---|
aggregate_updated_at filter, with eq, gt, gte, lt, lte and an offset-qualified ISO 8601 date-time | Filter body on all five POST list endpoints |
ordering=aggregate_updated_at / -aggregate_updated_at | All five list endpoints, and /capital-allocators/{uuid}/funds/ |
ordering=uuid / -uuid | The same routes, as the tiebreak for keyset paging |
Three things are worth knowing before you build on it:
- Page by keyset, not by
offset. A record edited while you page moves to the end of the list, and anoffsetwalk then skips a row. Page by keyset, not by offset shows the filter that avoids it. - The value can move with no visible change, when the change was to data the API does not show, such as a hidden investor’s transaction.
- Removals are not reported. A deleted or unpublished record stops appearing; it is not re-stamped.
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.
| Endpoint | New filter | Form |
|---|---|---|
POST /v3/partners/capital-receivers/ | is_startup | Filter body, JSON boolean, eq / ne |
POST /v3/partners/capital-allocators/ | has_startup_investments | Filter body, JSON boolean, eq / ne |
GET / POST /v3/partners/investors/ | capital_receiver_is_startup | Query string on GET, filters.all with a JSON boolean on POST |
GET / POST /v3/partners/people/, POST /v3/partners/people/roles/ | organization_is_startup | Query 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 classification is updated periodically, so a company that has just been created, published, or funded can read as a non-startup until the next update.
- On
/people/, sendingorganization_is_startupmakes the role, country, and startup conditions hold on the same role.role_type_key=person_association_director&organization_is_startup=truemeans directors of startups. Without the parameter, each filter can still match a different role of the same person. See People → Startup filter.
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:
- Rows with no value now sort last in either direction. On fund performance,
-irrused to put records with no IRR first. On allocator commitments, rows with no date move from the top of the default-dateorder to the bottom. - An empty
?ordering=now applies the route’s default rather than no order. GET/POST /capital-allocators/and/funds/now actually apply their documenteddisplay_namedefault. Withoutorderingthey previously returned rows in no fixed order.
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.
- Detail routes return
404for a record whose company or person is deleted, not only unpublished, on every sub-route. - Cap tables and a company’s investor list can sum to less than 100%. Hidden holders are left out, and the visible ones keep their percentage of the whole company.
- Deals leave out transactions whose buyer or seller is hidden, and recompute the share counts, the transaction part of the deal sizes,
price_per_share_usd, and the investment dates without them. /investors/totals andcountexclude transactions into hidden companies and under deleted deals, andcountno longer includes deleted shareholder groups — socountcan no longer exceed the rows returned.- People role filters match visible roles only. A person whose only director role was deleted no longer matches
role_type_key=person_association_director. Roles at an organization that no longer exists are no longer returned with anullorganization. - A deleted headquarters location reads as no headquarters in every headquarters filter and in the
legal_entity.headquartersblock. fund_manager_namematches only visible managers, one condition at a time.allof twofund_manager_nameconditions now matches a fund whose two managers each meet one; it used to need one manager to meet both, so it never matched. See Fund Filter Fields.
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
- A value that cannot be read as the field’s type —
"yes"for a boolean,"2025-13-45"for a date — now returns400naming the field. - A
filtersvalue sent as a JSON string now works on the capital receivers list and every batch endpoint, as the object form does. Several batch endpoints returned500on one. - A JSON array or object in
fieldnow returns400everywhere except/capital-allocators/, which still returns500.
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:
- What counts against the limit. Every request that reaches the API, whatever status it returns — a
400or404costs the same as a200. Page size, method, and query cost do not weight it, and every Partner API endpoint draws on the same allowance. - Which of the three limits you hit. The
dimensionfield distinguishes a per-credential limit from your organization’s and your network address’s. Two consequences worth designing around: issuing more credentials does not raise your organization’s total throughput, and parallel workers share one allowance. Retry-Afteris a real wait, not a constant. It measures the time until the rolling window frees capacity, so it shrinks as the window drains and is usually shorter than a full minute. Waiting exactly that long is both sufficient and the fastest correct retry.- The four rate-limit headers appear on
429responses only. A successful response carries no remaining-quota figure, so pace your own sending rather than steering by headers. - A worked retry example, with the three failure modes worth designing out — retrying immediately, retrying in lockstep across parallel workers, and treating
429as fatal. - A checklist for staying under the limit, ordered by how much each step buys you:
limit=1000over the default 20 is a 50x difference in rows per request, and the batch endpoints turn a per-company loop into a handful of calls.
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.
Service providers: ordering=display_name, and a stricter search
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:
| Endpoint | Fields 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
fieldgiven as a JSON array or object is still not handled cleanly — see Rejected bodies for which endpoints return500rather than400for 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:
| Endpoint | Fields |
|---|---|
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-Resetis a duration in seconds, not a Unix timestamp, despite what the header name usually means elsewhere. It carries the same value asRetry-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.
| Endpoint | What ?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:
- Each request mints a fresh signature, so a URL stored in a database, a queue, or a cached response is dead within the hour. Re-request the detail endpoint rather than persisting the link.
- An expired link is not a JSON error. The asset host returns
403 Forbiddenwith an XML body (<Error><Code>AccessDenied</Code></Error>), not the JSON error shape the rest of the API uses. Integrations that parse every failure as JSON will break on it. - The signature alone authorizes the download — no
Authorizationheader is involved — so a live URL is effectively a credential for that document.
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:
eq,ne,in, andninare case-sensitive;contains,ncontains,startswith, andendswithare not. Aneqon"sgp"returns zero rows where"SGP"returns the Singapore-domiciled set. The failure is an empty result set, not an error, so a filter with the wrong casing looks like a filter that matched nothing. Prefer*_keyfields for exact matching — keys have no casing to get wrong. This inconsistency is a known bug; see Case sensitivity for what changes when it is fixed.- An unrecognized
opis silently treated aseqrather than returning400, so a typo in the operator name reads as a working filter. - Seven field names are accepted and then not applied, returning
200with a wider result set than the filter implies:funding_statusandhas_vc_transactionson capital receivers;nationality_name,nationality_iso_alpha3,is_employee, andassociated_ca_typeson people;transaction_labelon deals. See Fields Accepted but Not Applied. - On five of the batch endpoints, conditions beyond the required UUID
infilter have no effect. The request returns200and every row for the UUIDs you passed, with nothing marking the dropped condition. Filter those results client-side, or call the per-entity endpoint where the fields are honored. - A batch endpoint’s required
inleaf must sit directly inside a root-levelall. The other body shapes this documentation describes are not recognized there, and return400reporting the filter as missing even though the UUIDs are in the body. search,ordering,limit, andoffsetin a request body never take effect. Most endpoints return400; the batch endpoints other thanPOST /people/roles/, andPOST /capital-receivers/{uuid}/deals/, discard them without one.
Newly documented, nothing changed:
- The field-keyed body form —
{"filters": {"latest_valuation_usd": {"op": "gte", "value": 50000000}}}— along with its list shorthand (anin) and scalar shorthand (aneq), and the fact that field keys and a logical block can sit side by side at the root. This form has always worked, but appeared in these docs only inside malformed-input examples. - The supported filter field list for every endpoint — the contractual surface, and the only names covered by our compatibility guarantees — replacing the previous per-page selections of common fields.
POST /v3/partners/service-providers/had no filter documentation at all. Its fields aredisplay_name,description,legal_entity_uuid,service_type_key, andservice_type_name— the only list endpoint that filters onlegal_entity_uuid.
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:
legal_entity_uuidis returned on investment rows only — the/investments/sub-resource on capital allocators, funds, and people. It was previously documented as also present on investor and cap-table rows. It is not:/capital-receivers/{uuid}/investors/rows and both managed and snapshot cap-table rows carry a profile-resolveduuidand no root UUID at all. If you have been readinglegal_entity_uuidoff those rows, it has always been absent.capital_receiver_uuidis returned on investment rows and on every batch response row (deals, financials, news, deal share types) — not on cap-table rows, where it was also previously documented.preferred_allocation_typedescribes one profile, not the firm. It was documented as “the primary asset class the firm allocates to”, which reads as one allocator profile per firm.
New guidance:
- How Many Profiles Per Root Record — a capital receiver and a fund are one per legal entity; a capital allocator is one per preferred allocation type, so a firm running equity and debt strategies returns two rows from
/capital-allocators/with twouuidvalues and one sharedlegal_entity.uuid; service providers are not limited at all. Any figure you total per firm has to collapse on the root UUID first, or a multi-strategy firm is counted once per profile. - Counterparty UUIDs Are Not Stable Identifiers — the
uuidon abuyer,seller,shareholder,organization, or investor row is resolved per row, at fetch time, to the most specific profile that entity currently has. Two consequences: the same company resolves to its capital allocator profile where it is the investor and to its capital receiver profile where it is the investee; and once a company gains a profile it did not have before, the same historical row starts returning the new profile’s UUID, with nothing in the response indicating the identifier moved. This extends the 2026-08-07 note that these UUIDs are not always legal-entity IDs. Key your own party or company table on the root UUID —legal_entity.uuid, or the person UUID — and treat the counterpartyuuidas a per-row pointer. - The same section lists where to get a root UUID for each response shape, and notes that pooled holders (ESOP, “Other Shareholders”,
shareholder_group) have no root record to resolve.
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.
| Field | Before | After |
|---|---|---|
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:
| Endpoint | Fields |
|---|---|
| Fund detail | management_fee_percentage, gp_commitment_percentage, hurdle_percentage, carry_percentage, latest_fund_size.aum_value, latest_fund_size.aum_value_usd |
| Fund performance | irr, dpi, rvpi, net_multiple, and all eleven _usd cash-flow amounts |
| Fund / capital allocator AUM | aum_value, aum_value_usd |
| Fund LP commitments | investment_amount_usd, transactions[].investment_amount_usd |
| Capital allocator detail | latest_aum.aum_value, latest_aum.aum_value_usd |
| Capital allocator LP commitments | cash_value_transacted, cash_value_transacted_usd |
| Capital receiver detail | The 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 |
| Deals | transactions[].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×.
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.
| Object | New field | Alongside |
|---|---|---|
Deal transactions[].buyer / .seller | type_key | type |
Cap-table results[].shareholder (managed and snapshot) | type_key | type |
Fund LP commitment transactions[].buyer | type_key | type |
| Capital allocator profile (list and detail) | profile_type_key | profile_type |
Person role organization | profile_type_key | profile_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:
- Deals (
/capital-receivers/{uuid}/deals/and the batch endpoint) gained the newly added deal-leveluuid, pluscapital_receiver_uuid,share_class,price_per_share_usd,description, andheadline. Transaction rows gaineduuid,date,nature_of_transaction,transaction_type, the fourallocation_type*/allocation_subtype*fields, and the buyer/seller registration identifiers. See Capital Receivers → Deals. - Capital receiver list and detail:
display_name,year_founded,date_founded, anddate_founded_precisionare nested underlegal_entity, not top-level as previously shown. The list also returnsthemes,horizontals,techs,business_models, andindustries; the detail also returnsfinancials[],financial_statements_audited[], andfinancial_statements_extracted[], which were undocumented. - Cap tables:
aggregationsuses*_aggregatekeys (shares_issued_aggregate,percentage_held_aggregate,holding_value_usd, …) — the previously documentedtotal_shares_issued/total_shares_heldnever existed. Rows also carrychild_entities,child_entities_count,holding_value_usd,is_held_in_treasury, and theshares_bought_secondary_*pair. - Registration numbers are
{reg_number, authority_type, authority_name}. Examples that showedauthority_labelwere wrong. - Buyer, seller, and shareholder
uuidvalues are not always legal-entity IDs. They point at the most specific record the entity has, and the newtype_keysays which. This was previously implied only by thetypelabel. - Capital allocator LP commitments (
/capital-allocators/{uuid}/commitments/) return a nestedfundobject withallocation_type,allocation_subtype,provenance, andcash_value_transacted/cash_value_transacted_usd— not thebuyer/seller/investment_amount_usdshape previously shown. - Fund detail:
structureis a plain string (e.g."Limited Partnership"), not a{key, name}object. Fee percentages andlatest_fund_sizeamounts were decimal strings at this date — superseded on 2026-08-14. - Fund performance gained eleven previously undocumented cash-flow fields, plus
source_capital_allocator_uuid,source_url, andcreated_at.irr,dpi,rvpi, andnet_multiplewere decimal strings at this date — superseded on 2026-08-14. - Investors list (
/investors/) does not returnfirst_investment_dateorlatest_investment_date; those exist only on the per-company/capital-receivers/{uuid}/investors/rows. - AUM records (capital allocator and fund) include
uuidand the reporting-currencyaum_valuealongsideaum_value_usd. - People: the person detail response includes
display_name,location_country,nationality,date_of_birth,biography, andlast_updated_at. Role rows includeperson_uuidandorganization.registration_numbers.
Filter-syntax corrections:
filtersmust start with a logical operator. A bare condition —{"filters": {"op": "eq", …}}— returns400 Bad Request. Wrap even a single condition inall. Examples using the bare form have been corrected.allandanytake an array;nottakes a single condition object, or an array negated as a whole.- Malformed filter bodies now return
400 Bad Requestwith a message describing the problem, instead of a500. This covers a non-object entry insideall/any/not, the same mistake in the flat field-keyed form ({"name": {"all": ["oops"]}}), arangewithout exactly two values, and a request body that is a JSON array or scalar rather than an object. filtersmust be an object. A list or scalar —{"filters": ["any"]}— previously returned the complete unfiltered result set with a200, because both the validator and the parser independently gave up on a non-object and the body passed every check while applying no filter. It now returns400. Omittingfilters, or sendingnullor{}, still means “no filter” and is unaffected.- An empty
notnow returns400 Bad Request.{"not": []},{"not": {}}, and{"not": {"all": []}}compiled to noWHEREclause at all, so a body asking to exclude rows returned every row with a200. An emptyalloranyis unchanged and remains a permissive no-op — that is a caller who supplied no conditions, not one whose exclusion was silently dropped. /investors/validates its filter body. That endpoint accepts a flatfilters.alllist of a fixed set of field/operator pairs, now documented in Investors → Advanced Filter (POST).any,not, and unsupported operators previously either returned the complete unfiltered result set with a200or failed with a500; both now return400naming what is accepted. It also ignoredopentirely, sonereturned the same rows aseq.- Deal filters match display names. The deals
POSTfilter acceptsallocation_typewith"Equity"; there is noallocation_type_keyfilter field. - Enum keys in examples are now real values. Notably
fund_status_activedoes not exist (usefund_status_raising), the audit-opinion key isaudit_opinion_unqualifiedwith name"Unqualified Opinion", and newstypekeys arenews_type_*.
New guidance:
- Introduction → Field Types and Nulls explains which numeric fields come back as JSON strings and which as numbers, and what
nullmeans for booleans and amounts. (The string/number split it described was removed on 2026-08-14 — everything is a number now.) - Reference Data → Enum Categories now lists the true value count per category and flags that
preferred_fund_typesreturns an empty array.
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.
- Error bodies carry
status_code(repeating the HTTP status) anddetail. Both are shown, with the exactdetailtext for401,403,404, and500, which is fixed per status. 404and500are documented for the first time, including the two distinct404messages — a mistyped URL reads differently from a missing record, so the two are distinguishable.400bodies come in two shapes: a list of validation messages withstatus_code, or a single string without it. Both are documented, along with the recommendation to accept either and to take the status from the HTTP response rather than the body.- Which statuses apply where: every endpoint can return
401,403, and500; endpoints taking a UUID in the path can also return404; endpoints accepting a body or filter parameters can also return400.
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.
- Funds accept
display_name,vintage_year,term_years,created_at, andupdated_at. See Funds → List Funds. - Capital allocators accept
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, andupdated_at. See Capital Allocators → List Capital Allocators.
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.
POST /v3/partners/capital-receivers/news/— batch-fetch news articles across companies bycapital_receiver_uuid. See Capital Receivers → Batch News via POST.POST /v3/partners/capital-allocators/aum/— batch-fetch AUM history across allocators bycapital_allocator_uuid. See Capital Allocators → Batch AUM via POST.POST /v3/partners/funds/performance/— batch-fetch performance across funds byfund_uuid. See Funds → Batch Performance via POST.
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.
POST /v3/partners/people/roles/— batch-fetch roles across people byperson_uuid. See People → Batch Roles via POST.POST /v3/partners/capital-receivers/financials/— batch-fetch financials bycapital_receiver_uuid. See Capital Receivers → Batch Financials via POST.POST /v3/partners/capital-receivers/deals/— batch-fetch deals bycapital_receiver_uuid. See Capital Receivers → Batch Deals via POST.
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):
total_funding_amount_usd→total_funding_usdequity_funding_amount_usd→ split intoreported_and_filed_equity_usd,filed_equity_usd, andreported_equity_usddebt_funding_amount_usd→ split intoreported_and_filed_debt_usd,filed_debt_usd, andreported_debt_usd
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).
People — multi-word search
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.
