Multiples

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/aggregate

Request

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:

fieldtypedescription
metricstring (required)The value aggregated per bucket. One of ev_revenue, ev_ebitda, valuation, raised, revenue, ebitda.
time_bucketstringHow deals are grouped over time: month, quarter, year, or none. Defaults to none (one overall bucket).
filtersobjectCohort definition. Accepts the full /transactions filter vocabulary (below).

filters fields (all optional):

fieldtypedescription
idarray of uuidMatch specific deals by ID.
target_idarray of uuidRestrict to deals whose target is one of these companies.
investor_idarray of uuidMatch deals where any of these companies took part in any role: lead investor, other investor or buyer.
deal_typearray of stringOne 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_subtypearray of stringOne 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_fromdate (YYYY-MM-DD)Earliest deal date to include. Defaults to 5 years before today when omitted.
date_todate (YYYY-MM-DD)Latest deal date to include. Defaults to today when omitted.
valuation_min / valuation_maxnumberBound deal valuation (millions USD).
raised_min / raised_maxnumberBound amount raised.
revenue_min / revenue_maxnumberBound deal revenue.
ebitda_min / ebitda_maxnumberBound deal EBITDA.
ev_revenue_min / ev_revenue_maxnumberBound the EV/Revenue multiple.
ev_ebitda_min / ev_ebitda_maxnumberBound the EV/EBITDA multiple.
target_countryarray of stringTarget country (ISO 3166-1 alpha-3 codes).
target_regionarray of stringTarget region (e.g. North America, Western Europe).
target_verticalsarray of stringMatch deals whose target operates in any of these verticals.
target_themesarray of stringMatch deals whose target maps to any of these themes.
target_client_focusarray of stringMatch by target client focus (e.g. B2C).
target_revenue_modelarray of stringMatch by target revenue model.
target_company_typearray of stringMatch by target company type.
updated_atdate-timeChange-feed checkpoint: only deals updated at or after this timestamp. Setting it also includes soft-deleted deals.
include_deletedbooleanInclude 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:

fieldtypedescription
bucketstring | nullThe bucket label: YYYY-MM (month), YYYY-Qn (quarter), or YYYY (year). null when time_bucket is none.
nintegerNumber of deals in the bucket after outlier trimming. 0 when the bucket is empty or below small_n_threshold.
mediannumber | nullMedian of the chosen metric for the bucket. null when n is 0.
q1number | nullFirst quartile (25th percentile) of the metric. null under the same conditions as median.
q3number | nullThird quartile (75th percentile) of the metric. null under the same conditions as median.
warningstring | nullCurrently always null.

meta - context and methodology:

fieldtypedescription
metricstringThe metric that was aggregated (echoes the request).
time_bucketstringThe time bucket that was applied (echoes the request).
total_dealsintegerMatching deals across reported buckets, before outlier trimming. Buckets below small_n_threshold are not counted.
outliers_trimmedintegerDeals removed by the outlier rule in reported buckets. Buckets below small_n_threshold are not counted.
methodologyobjectConstants used for this aggregation (see below).
usageobjectAlways { "records_billable": 0, "records_charged": 0 }: aggregates are not charged. See Rate Limits.

meta.methodology:

fieldtypedescription
small_n_thresholdintegerBuckets with fewer than this many deals after trimming are returned like empty ones: n: 0 and null statistics.
outlier_rulestringThe outlier-trimming rule applied, e.g. iqr_1.5 (values outside 1.5×IQR of the quartiles are dropped).
default_date_range_yearsintegerLookback window (in years) used when date_from/date_to are omitted.
empty_buckets_returnedbooleanWhether zero-deal buckets are emitted in data (so time series have explicit gaps).
date_rangeobjectThe effective date window applied, with from and to (YYYY-MM-DD).
date_range.fromstring (date)Start of the effective window (inclusive).
date_range.tostring (date)End of the effective window (inclusive).

On this page