Aggregate Transactions
Introduction
POST /transactions/aggregate 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/aggregate
Request
Send a JSON body with the metric to aggregate, an optional time bucket, and a filters object that defines the cohort. metric and filters are required.
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 |
|---|---|---|
|
|
string (required) |
The value aggregated per bucket. One of |
|
|
string |
How deals are grouped over time: |
|
|
object (required) |
Cohort definition. Accepts the full |
filters fields (all optional):
|
field |
type |
description |
|---|---|---|
|
|
array of uuid |
Match specific deals by ID. |
|
|
array of uuid |
Restrict to deals whose target is one of these companies. |
|
|
array of uuid |
Match deals where any of these companies participated as a lead investor or buyer. |
|
|
array of string |
One or more of |
|
|
array of string |
One or more of |
|
|
date ( |
Earliest deal date to include. Defaults to 5 years before today when omitted. |
|
|
date ( |
Latest deal date to include. Defaults to today when omitted. |
|
|
number |
Bound deal valuation (millions USD). |
|
|
number |
Bound amount raised. |
|
|
number |
Bound deal revenue. |
|
|
number |
Bound deal EBITDA. |
|
|
number |
Bound the EV/Revenue multiple. |
|
|
number |
Bound the EV/EBITDA multiple. |
|
|
array of string |
Target country (ISO 3166-1 alpha-3 codes). |
|
|
array of string |
Target region (e.g. |
|
|
array of string |
Match deals whose target operates in any of these verticals. |
|
|
array of string |
Match deals whose target maps to any of these themes. |
|
|
array of string |
Match by target client focus (e.g. |
|
|
array of string |
Match by target revenue model. |
|
|
array of string |
Match by target company type. |
|
|
date-time |
Change-feed checkpoint: only deals updated at or after this timestamp. |
|
|
boolean |
Include soft-deleted deals. Defaults to |
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, and buckets below the small-N threshold report n but suppress the statistics with warning: "insufficient_sample". 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": 3,
"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": 3,
"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 |
|---|---|---|
|
|
string | null |
The bucket label: |
|
|
integer |
Number of deals in the bucket after outlier trimming. |
|
|
number | null |
Median of the chosen metric for the bucket. |
|
|
number | null |
First quartile (25th percentile) of the metric. |
|
|
number | null |
Third quartile (75th percentile) of the metric. |
|
|
string | null |
|
meta - context and methodology:
|
field |
type |
description |
|---|---|---|
|
|
string |
The metric that was aggregated (echoes the request). |
|
|
string |
The time bucket that was applied (echoes the request). |
|
|
integer |
Total matching deals across all buckets, before outlier trimming. |
|
|
integer |
Number of deals removed by the outlier rule before computing statistics. |
|
|
object |
Constants used for this aggregation (see below). |
meta.methodology:
|
field |
type |
description |
|---|---|---|
|
|
integer |
Buckets with fewer than this many deals report |
|
|
string |
The outlier-trimming rule applied, e.g. |
|
|
integer |
Lookback window (in years) used when |
|
|
boolean |
Whether zero-deal buckets are emitted in |
|
|
object |
The effective date window applied, with |
|
|
string (date) |
Start of the effective window (inclusive). |
|
|
string (date) |
End of the effective window (inclusive). |

