Aggregate Transactions
Introduction
This endpoint computes a per-bucket median multiple for a single cohort of deals.
You choose one metric (such as ev_revenue or valuation), slice the cohort by time_bucket (month, quarter, year, or a single overall bucket), and scope it with the same filters vocabulary as the transactions list.
The response returns {bucket, n, median, q1, q3} arrays plus the methodology constants used, so you can chart valuation trends or benchmark a comparable set without pulling and reducing the raw deals yourself.
Set up
Endpoint
POST https://multiples.vc/api/private/v1/transactions/aggregateRequest
Send a JSON body with the metric to aggregate, an optional time bucket, and an optional filters object that defines the cohort. Only metric is required. Without filters, the cohort is every deal in the default date window.
Median EV/Revenue per quarter across M&A deals:
{
"metric": "ev_revenue",
"time_bucket": "quarter",
"filters": {
"deal_type": ["M&A"],
"date_from": "2021-01-01",
"date_to": "2025-12-31"
}
}Median EV/Revenue per year for European Growth/Venture rounds:
{
"metric": "ev_revenue",
"time_bucket": "year",
"filters": {
"deal_type": [
"Growth",
"Venture"
],
"target_region": [
"Western Europe",
"Northern Europe"
],
"date_from": "2020-01-01",
"date_to": "2024-12-31"
}
}Top-level parameters:
| field | type | description |
|---|---|---|
metric | string (required) | The value aggregated per bucket. One of ev_revenue, ev_ebitda, valuation, raised, revenue, ebitda. |
time_bucket | string | How deals are grouped over time: month, quarter, year, or none. Defaults to none (one overall bucket). |
filters | object | Cohort definition. Accepts the full /transactions filter vocabulary (below). |
filters fields (all optional):
| field | type | description |
|---|---|---|
id | array of uuid | Match specific deals by ID. |
target_id | array of uuid | Restrict to deals whose target is one of these companies. |
investor_id | array of uuid | Match deals where any of these companies took part in any role: lead investor, other investor or buyer. |
deal_type | array of string | One or more of Early, Growth, M&A, Public listing, Secondary, Venture. Write-down and its subtypes Impairment and Insolvency are accepted but match nothing: a write-down is a valuation event, not a transaction (see Transaction filters and vocabulary). |
deal_subtype | array of string | One or more of Angel, ICO, PE buyout, Pre-seed, Secondary - private, Secondary - public, Seed, Series A-Series J, Strategic M&A, Strategic investment, Undisclosed stage. |
date_from | date (YYYY-MM-DD) | Earliest deal date to include. Defaults to 5 years before today when omitted. |
date_to | date (YYYY-MM-DD) | Latest deal date to include. Defaults to today when omitted. |
valuation_min / valuation_max | number | Bound deal valuation (millions USD). |
raised_min / raised_max | number | Bound amount raised. |
revenue_min / revenue_max | number | Bound deal revenue. |
ebitda_min / ebitda_max | number | Bound deal EBITDA. |
ev_revenue_min / ev_revenue_max | number | Bound the EV/Revenue multiple. |
ev_ebitda_min / ev_ebitda_max | number | Bound the EV/EBITDA multiple. |
target_country | array of string | Target country (ISO 3166-1 alpha-3 codes). |
target_region | array of string | Target region (e.g. North America, Western Europe). |
target_verticals | array of string | Match deals whose target operates in any of these verticals. |
target_themes | array of string | Match deals whose target maps to any of these themes. |
target_client_focus | array of string | Match by target client focus (e.g. B2C). |
target_revenue_model | array of string | Match by target revenue model. |
target_company_type | array of string | Match by target company type. |
updated_at | date-time | Change-feed checkpoint: only deals updated at or after this timestamp. Setting it also includes soft-deleted deals. |
include_deleted | boolean | Include soft-deleted deals. Defaults to false. |
Tag filters accept the same {"all": ["A", "B"]} shape as POST /transactions: the target must carry every listed value. A plain array still matches any listed value. An unknown value returns 400 and names the closest matches.
The accepted filter fields, metric enums, time buckets, and methodology constants are also discoverable at runtime via GET /lookup/transactions.
Response
Each data entry is one bucket. Buckets with no deals (n: 0) are returned so charts have explicit gaps. A bucket with fewer than 5 deals after outlier trimming (small_n_threshold) is returned exactly like an empty one: n: 0, with median, q1 and q3 set to null. Example for median EV/Revenue per year for European Growth/Venture rounds (the second request above):
{
"data": [
{
"bucket": "2020",
"n": 42,
"median": 10.7718120805369,
"q1": 5.55991124260355,
"q3": 17.9166666666667,
"warning": null
},
{
"bucket": "2021",
"n": 90,
"median": 9.88470557582074,
"q1": 5,
"q3": 18.7724830128347,
"warning": null
},
{
"bucket": "2022",
"n": 68,
"median": 12.5009557522124,
"q1": 6.70950704225352,
"q3": 22.0485210171251,
"warning": null
},
{
"bucket": "2023",
"n": 24,
"median": 6.76799805385352,
"q1": 4.93762963252847,
"q3": 11.3538205980066,
"warning": null
},
{
"bucket": "2024",
"n": 37,
"median": 9.5,
"q1": 4.46632478632479,
"q3": 17,
"warning": null
}
],
"meta": {
"metric": "ev_revenue",
"time_bucket": "year",
"total_deals": 284,
"outliers_trimmed": 23,
"methodology": {
"small_n_threshold": 5,
"outlier_rule": "iqr_1.5",
"default_date_range_years": 5,
"empty_buckets_returned": true,
"date_range": {
"from": "2020-01-01",
"to": "2024-12-31"
}
}
}
}When time_bucket is none, data holds a single entry whose bucket is null, covering the whole cohort:
{
"data": [
{ "bucket": null, "n": 211, "median": 390, "q1": 100.05, "q3": 1000, "warning": null }
],
"meta": {
"metric": "valuation",
"time_bucket": "none",
"total_deals": 242,
"outliers_trimmed": 31,
"methodology": {
"small_n_threshold": 5,
"outlier_rule": "iqr_1.5",
"default_date_range_years": 5,
"empty_buckets_returned": true,
"date_range": { "from": "2021-06-15", "to": "2026-06-15" }
}
}
}Fields
data[] - one object per bucket:
| field | type | description |
|---|---|---|
bucket | string | null | The bucket label: YYYY-MM (month), YYYY-Qn (quarter), or YYYY (year). null when time_bucket is none. |
n | integer | Number of deals in the bucket after outlier trimming. 0 when the bucket is empty or below small_n_threshold. |
median | number | null | Median of the chosen metric for the bucket. null when n is 0. |
q1 | number | null | First quartile (25th percentile) of the metric. null under the same conditions as median. |
q3 | number | null | Third quartile (75th percentile) of the metric. null under the same conditions as median. |
warning | string | null | Currently always null. |
meta - context and methodology:
| field | type | description |
|---|---|---|
metric | string | The metric that was aggregated (echoes the request). |
time_bucket | string | The time bucket that was applied (echoes the request). |
total_deals | integer | Matching deals across reported buckets, before outlier trimming. Buckets below small_n_threshold are not counted. |
outliers_trimmed | integer | Deals removed by the outlier rule in reported buckets. Buckets below small_n_threshold are not counted. |
methodology | object | Constants used for this aggregation (see below). |
usage | object | Always { "records_billable": 0, "records_charged": 0 }: aggregates are not charged. See Rate Limits. |
meta.methodology:
| field | type | description |
|---|---|---|
small_n_threshold | integer | Buckets with fewer than this many deals after trimming are returned like empty ones: n: 0 and null statistics. |
outlier_rule | string | The outlier-trimming rule applied, e.g. iqr_1.5 (values outside 1.5×IQR of the quartiles are dropped). |
default_date_range_years | integer | Lookback window (in years) used when date_from/date_to are omitted. |
empty_buckets_returned | boolean | Whether zero-deal buckets are emitted in data (so time series have explicit gaps). |
date_range | object | The effective date window applied, with from and to (YYYY-MM-DD). |
date_range.from | string (date) | Start of the effective window (inclusive). |
date_range.to | string (date) | End of the effective window (inclusive). |