{"openapi":"3.1.0","info":{"title":"Upswitch Index — Public Benchmark API","version":"1.0.0","summary":"Versioned, citable SME valuation multiples (EV/EBITDA, EV/Revenue, P/E) by NACE business type, market view, and disclosed evidence basis.","description":"Defensibility-first benchmark API. Benelux can be native-local where row evidence supports it; wider European market views are disclosed as beta, borrowed, aggregate, or compatibility coverage until local evidence depth is sufficient. Each row carries blended percentile bands (P10..P90), effective sample size (n), triage layer, freshness band, and country-basis disclosure. Anonymous access is teaser-safe; signed-in free accounts unlock the full current snapshot; Pro accounts unlock historical vintages, draft preview, and audit-grade exports. See https://index.upswitch.app/en/markets/methodology for cascade, MAD outlier detection, DLOM gating, and how to cite a vintage in a fairness opinion or deal binder.","termsOfService":"https://www.upswitch.app/en/terms-and-conditions","contact":{"name":"Upswitch Index","url":"https://index.upswitch.app/en/markets"},"license":{"name":"Proprietary — Upswitch"}},"servers":[{"url":"https://index.upswitch.app","description":"Public benchmark API origin"}],"tags":[{"name":"Benchmarks","description":"Published valuation multiples by business type, country, and metric."},{"name":"Snapshots","description":"Immutable, citable vintage records (Pro for full history)."},{"name":"Audit","description":"Row-level audit packets (Pro session or PAT required)."},{"name":"Accuracy","description":"Backtest accuracy metadata for the published universe."},{"name":"Market Data","description":"Country macro, currency, tax, risk-free, and country-risk factors from Delphi."}],"components":{"securitySchemes":{"pat":{"type":"apiKey","in":"header","name":"Authorization","description":"Pro Personal Access Token (`Authorization: Bearer upat_…`). Verified via Titan with `BENCHMARK_TOKEN_SERVICE_SECRET`. PATs unlock historical vintages, draft preview, audit JSON / PDF, and 600 req/min rate limits (vs 120/min anonymous)."},"patAltHeader":{"type":"apiKey","in":"header","name":"x-api-key","description":"Same Pro PAT, alternate header (`x-api-key: upat_…`). Use when `Authorization` is reserved by your HTTP client."}},"schemas":{"SourceAlignment":{"type":"string","enum":["independent_confirmation","single_source","not_disclosed"],"description":"Coarse blend signal — never raw source names, raw counts, or internal weights."},"CountryBasis":{"type":"string","enum":["native","aggregate_clone","aggregate"],"description":"Disclosure-first basis label. `native` = country-specific evidence; `aggregate_clone` = per-country slot inheriting the BENELUX-wide regional aggregate (use as a defensible regional prior, not a country-specific number); `aggregate` = the BENELUX-wide aggregate row itself, with country_code null."},"FreshnessClass":{"type":"string","enum":["fresh","recent","aging","stale"]},"Provenance":{"type":"object","description":"Public-safe evidence provenance — selected triage layer + freshness + optional NACE walk path. Never includes provider names, raw layer-by-layer attempts, or chain-of-thought.","properties":{"selected_layer":{"type":"integer","minimum":1,"maximum":7,"description":"Triage layer that produced the row. 1 = local official; 2 = local listed; 3 = Tier-1 proximity; 4 = Eurostat multi-country; 5 = broader sector local; 6 = Damodaran / macro; 7 = semantic fallback."},"selected_layer_name":{"type":"string","nullable":true},"freshness":{"type":"object","properties":{"freshness_class":{"$ref":"#/components/schemas/FreshnessClass"},"freshness_age_days":{"type":"number","nullable":true},"freshness_budget_days":{"type":"number","nullable":true},"last_observation_at":{"type":"string","format":"date-time","nullable":true}}},"cross_pollination":{"type":"object","description":"NACE walk path when the business type had no native data and the row was lifted from a nearby code.","properties":{"cross_pollinated":{"type":"boolean"},"walk_step":{"type":"string","nullable":true},"walk_proximity":{"type":"number","nullable":true}}}}},"IndexRow":{"type":"object","required":["business_type_id","metric_name"],"properties":{"business_type_id":{"type":"string","example":"be-b2b-saas","description":"Stable public business type id. Usually slug-shaped, not guaranteed to be a UUID."},"title":{"type":"string","nullable":true},"primary_nace_code":{"type":"string","nullable":true},"metric_name":{"type":"string","enum":["EV_EBITDA","EV_REVENUE","PE_RATIO"]},"country_code":{"type":"string","nullable":true,"description":"ISO-2. Null when the row is the BENELUX-wide regional aggregate."},"region_key":{"type":"string"},"size_band":{"type":"string","nullable":true},"p10":{"type":"number","nullable":true},"p25":{"type":"number","nullable":true},"median":{"type":"number","nullable":true},"p75":{"type":"number","nullable":true},"p90":{"type":"number","nullable":true},"sample_size":{"type":"integer","nullable":true,"description":"Effective sample size (observations + Bayesian prior weight). Null on seed-derived rows."},"observation_count":{"type":"integer","description":"Raw count of comparables behind the row before MAD trimming. 0 on seed-derived rows."},"confidence_score":{"type":"number","nullable":true},"confidence_band":{"type":"string","nullable":true,"description":"Human-readable blended-percentile-band label (e.g. 'P10–P90: 4.6× – 6.8×'). NOT a posterior credible interval — see /markets/methodology."},"evidence_class":{"type":"string","nullable":true,"description":"DAMA quality class for this row. 'inferred' for seeded fallbacks."},"source_alignment":{"$ref":"#/components/schemas/SourceAlignment"},"country_basis":{"$ref":"#/components/schemas/CountryBasis"},"provenance":{"$ref":"#/components/schemas/Provenance"},"published_at":{"type":"string","format":"date-time"}}},"IndexResponse":{"type":"object","properties":{"index":{"type":"string","const":"Upswitch Index","description":"Public-facing index brand label."},"summary":{"type":"object","properties":{"snapshot_id":{"type":"string","format":"uuid","description":"Pro-only."},"as_of":{"type":"string","format":"date-time","description":"Pro-only."},"draft_preview":{"type":"boolean"},"total_benchmarks":{"type":"integer"},"generated_at":{"type":"string","format":"date-time"},"access_tier":{"type":"string","enum":["public_teaser","free","pro"]},"teaser_redacted":{"type":"boolean"},"fallback_basis":{"type":"string","enum":["benelux_aggregate"],"description":"Set when active BENELUX-wide aggregate rows stand in for a per-country request (no active country-specific rows yet). Rows carry country_basis 'aggregate' and must not be read as country-specific."},"requested_country_code":{"type":"string","description":"Requested market the aggregate rows stand in for; present only alongside fallback_basis."}}},"rows":{"type":"array","items":{"$ref":"#/components/schemas/IndexRow"}},"market_data":{"$ref":"#/components/schemas/MarketDataResponse","description":"Present only when `include_market_data=1`; bounded to the requested or represented countries in the multiples response."},"warning":{"type":"string","nullable":true},"warning_code":{"type":"string","enum":["aggregate_basis","materialized_fallback","benchmarks_unavailable"],"nullable":true,"description":"Machine-readable degradation tier accompanying `warning`: aggregate_basis (active BENELUX aggregate rows serve a per-country request), materialized_fallback (latest materialized active rows; live view unavailable), benchmarks_unavailable (honest empty state — no active rows at any tier)."}}},"Snapshot":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"version_number":{"type":"integer"},"published_at":{"type":"string","format":"date-time"},"is_active":{"type":"boolean"},"is_draft":{"type":"boolean","description":"Pro-only payload field."}}},"MarketDataRow":{"type":"object","properties":{"country_code":{"type":"string","example":"BE"},"as_of_date":{"type":"string","format":"date","nullable":true},"currency_code":{"type":"string","example":"EUR","nullable":true},"gdp_growth_1y_pct":{"type":"number","nullable":true,"description":"Backward-looking 1-year GDP growth, percent."},"gdp_growth_forecast_2y_pct":{"type":"number","nullable":true,"description":"2-year GDP growth forecast, percent."},"inflation_hicp_pct":{"type":"number","nullable":true,"description":"HICP inflation, percent."},"unemployment_pct":{"type":"number","nullable":true,"description":"Unemployment rate, percent."},"risk_free_rate_10y_pct":{"type":"number","nullable":true,"description":"10-year sovereign yield used as risk-free rate, percent."},"corporate_tax_statutory_pct":{"type":"number","nullable":true,"description":"Top-bracket statutory corporate tax rate, percent."},"default_spread":{"type":"number","nullable":true,"description":"Country credit-default spread, decimal."},"country_risk_premium":{"type":"number","nullable":true,"description":"Country risk premium, decimal."},"data_basis":{"type":"string","enum":["live","bootstrap","stored"],"description":"`bootstrap` rows are value-preserving seed rows until live monthly country-context refreshes supersede them."},"last_updated":{"type":"string","format":"date-time","nullable":true}}},"MarketDataResponse":{"type":"object","properties":{"index":{"type":"string","const":"Upswitch Market Data"},"summary":{"type":"object","properties":{"status":{"type":"string","enum":["ok","build_skipped","unavailable"]},"countries":{"type":"integer"},"generated_at":{"type":"string","format":"date-time"},"source":{"type":"string","description":"Delphi country_contextual_factors."},"disclosure":{"type":"string"},"detail":{"type":"string","nullable":true}}},"countries":{"type":"array","items":{"$ref":"#/components/schemas/MarketDataRow"}}}},"ValuationContextResponse":{"type":"object","properties":{"index":{"type":"string","const":"Upswitch Valuation Context","description":"Model-ready response that bundles Upswitch Index multiples with Delphi country market data."},"version":{"type":"string","example":"1.0"},"summary":{"type":"object","properties":{"generated_at":{"type":"string","format":"date-time"},"source":{"type":"string","const":"Upswitch Index + Delphi country_contextual_factors"},"country_code":{"type":"string","nullable":true},"business_type_id":{"type":"string","nullable":true},"metric_name":{"type":"string","nullable":true},"total_benchmarks":{"type":"integer"},"market_data_status":{"type":"string","enum":["ok","build_skipped","unavailable"]},"countries":{"type":"integer"},"access_tier":{"type":"string","enum":["public_teaser","free","pro"]},"snapshot_id":{"type":"string","format":"uuid","nullable":true},"warning":{"type":"string","nullable":true}}},"multiples":{"$ref":"#/components/schemas/IndexResponse"},"market_data":{"$ref":"#/components/schemas/MarketDataResponse"},"links":{"type":"object","properties":{"multiples":{"type":"string"},"market_data":{"type":"string"},"openapi":{"type":"string"}}}}},"Error":{"type":"object","properties":{"error":{"type":"string"},"detail":{"type":"string","nullable":true}}}},"parameters":{"countryCode":{"name":"country_code","in":"query","schema":{"type":"string","example":"BE"},"description":"ISO-2 country code. `UK` aliases to `GB`. BE/NL/LU return per-country BENELUX rows; non-BENELUX returns the country slot plus GLOBAL rows. See methodology for country_basis semantics."},"countries":{"name":"countries","in":"query","schema":{"type":"string","example":"BE,NL,DE"},"description":"Comma-separated ISO-2 country codes for bounded multi-market pulls. Use either `country_code` or `countries`, not both. `UK` aliases to `GB`."},"metricName":{"name":"metric_name","in":"query","schema":{"type":"string","enum":["EV_EBITDA","EV_REVENUE","PE_RATIO"]}},"businessTypeId":{"name":"business_type_id","in":"query","schema":{"type":"string","example":"be-b2b-saas"},"description":"Exact public business type id filter. Used by business-type detail Dataset links to pull the current slice for one published activity."},"minConfidence":{"name":"min_confidence","in":"query","schema":{"type":"number","default":0,"minimum":0,"maximum":1},"description":"Filter out rows with confidence below this threshold. Comma or dot decimals accepted."},"locale":{"name":"locale","in":"query","schema":{"type":"string","enum":["en","nl"],"default":"en"},"description":"Localizes the `title` field on each row. Falls back to canonical English when a translation is missing."},"beneluxScope":{"name":"benelux_scope","in":"query","schema":{"type":"string","enum":["aggregate"]},"description":"Integration-only: with no `country_code`, return only BENELUX-wide aggregate rows (`country_code` null)."},"includeMarketData":{"name":"include_market_data","in":"query","schema":{"type":"string","enum":["1","true"],"default":"0"},"description":"Set to `1` or `true` to embed Delphi country market data beside the multiples rows. The embedded bundle is scoped to `country_code`, represented row countries, or all published markets when no country filter is used."},"contextFormat":{"name":"format","in":"query","schema":{"type":"string","enum":["json","csv"],"default":"json"},"description":"Set to `csv` for an Excel-ready flat export joining multiples rows to matching country market-data fields. Omit or use `json` for the canonical nested context response."},"snapshotId":{"name":"snapshot_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Pro-only — pin the response to a specific vintage."},"draftPreview":{"name":"draft_preview","in":"query","schema":{"type":"string","enum":["1"]},"description":"Pro-only — preview the unpublished draft vintage. Requires Pro session or PAT."},"marketDataFormat":{"name":"format","in":"query","schema":{"type":"string","enum":["json","csv"],"default":"json"},"description":"Set to `csv` for an Excel-ready country market-data export. Omit or use `json` for the canonical JSON response."}},"responses":{"RateLimited":{"description":"Rate limit exceeded (120 req/min anonymous, 600 req/min PAT). Retry-After header included.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Pro tier required (PAT or signed-in Pro session).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/benchmarks/v1/index":{"get":{"tags":["Benchmarks"],"summary":"Latest published multiples (or pinned vintage with PAT)","description":"Returns rows for the active published vintage by default. Anonymous responses are teaser-redacted past the public-visible row cap. Pro PATs unlock `snapshot_id`, `as_of`, and `draft_preview`. Pass `include_market_data=1` to receive Delphi country market data beside the multiples rows. **Defensibility fields**: every row carries `p10..p90`, `sample_size`, `confidence_band`, `provenance.selected_layer`, `provenance.freshness`, and `country_basis`.","parameters":[{"$ref":"#/components/parameters/countryCode"},{"$ref":"#/components/parameters/businessTypeId"},{"$ref":"#/components/parameters/metricName"},{"$ref":"#/components/parameters/minConfidence"},{"$ref":"#/components/parameters/locale"},{"$ref":"#/components/parameters/beneluxScope"},{"$ref":"#/components/parameters/includeMarketData"},{"$ref":"#/components/parameters/snapshotId"},{"$ref":"#/components/parameters/draftPreview"}],"security":[{},{"pat":[]},{"patAltHeader":[]}],"responses":{"200":{"description":"Published rows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IndexResponse"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/benchmarks/v1/context":{"get":{"tags":["Benchmarks","Market Data"],"summary":"Model-ready multiples plus country market data","description":"One-call valuation context for spreadsheets, agents, and M&A tooling. Returns the same teaser-safe or authenticated multiples rows as `/api/benchmarks/v1/index`, with Delphi country market data embedded as `market_data`.","parameters":[{"$ref":"#/components/parameters/countryCode"},{"$ref":"#/components/parameters/businessTypeId"},{"$ref":"#/components/parameters/metricName"},{"$ref":"#/components/parameters/minConfidence"},{"$ref":"#/components/parameters/locale"},{"$ref":"#/components/parameters/beneluxScope"},{"$ref":"#/components/parameters/contextFormat"},{"$ref":"#/components/parameters/snapshotId"},{"$ref":"#/components/parameters/draftPreview"}],"security":[{},{"pat":[]},{"patAltHeader":[]}],"responses":{"200":{"description":"Valuation context bundle","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValuationContextResponse"}},"text/csv":{"schema":{"type":"string","description":"Flat CSV with one multiples row per line plus `market_*` country-context columns."}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/benchmarks/v1/sources":{"get":{"tags":["Benchmarks"],"summary":"Per-country sourcing roadmap","description":"The public, citable per-country source registry — for each market, the named national registry, statistics office, listed-comp set, gazette monitor, central-bank database, and trade-body publication that Upswitch pulls from (or commits to pulling from). Each source carries `status` (`active` / `in_development` / `planned` / `partner_data`), `tier`, `cadence`, `cost`, a coverage note, and the triage layer it feeds. This is the disclosure-first answer behind every market that today inherits the BENELUX regional aggregate.","parameters":[{"$ref":"#/components/parameters/countryCode"},{"name":"status","in":"query","schema":{"type":"string","enum":["active","in_development","planned","partner_data"]}},{"name":"tier","in":"query","schema":{"type":"string","enum":["national_registry","national_stats_office","listed_comps","deal_gazette","trade_body_report","central_bank","academic_macro"]}}],"security":[{}],"responses":{"200":{"description":"Sourcing roadmap (single country or all markets)","content":{"application/json":{"schema":{"type":"object","description":"Single-country shape when `country_code` is provided; multi-market shape (`countries: []`) when omitted."}}}},"400":{"description":"Invalid country_code, status, or tier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"country_code is not in the published registry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/benchmarks/v1/country-summary":{"get":{"tags":["Benchmarks"],"summary":"Per-country basis breakdown (native vs aggregate-derived)","description":"Powers the disclosure-first evidence footnote on `/markets/{country}` pages. Returns per-country counts of distinct business types grouped by `country_basis` (`native | aggregate_clone | aggregate`) for the active published vintage. Anonymous, IP-rate-limited, no PAT required. Returns only public-safe aggregate counts; internal columns are never exposed (see methodology).","parameters":[{"$ref":"#/components/parameters/countryCode"},{"$ref":"#/components/parameters/metricName"}],"security":[{}],"responses":{"200":{"description":"Coverage summary","content":{"application/json":{"schema":{"type":"object","description":"Single-country shape when `country_code` is provided; multi-country shape (`countries: []`) when omitted.","properties":{"country_code":{"type":"string","nullable":true},"metric_name":{"type":"string","nullable":true},"active_snapshot":{"type":"object","nullable":true,"properties":{"snapshot_id":{"type":"string","format":"uuid"},"version_number":{"type":"integer"},"published_at":{"type":"string","format":"date-time"}}},"summary":{"type":"object","properties":{"country_code":{"type":"string"},"total":{"type":"integer"},"native":{"type":"integer"},"aggregate_clone":{"type":"integer"},"native_pct":{"type":"number","minimum":0,"maximum":1}}},"countries":{"type":"array","items":{"type":"object","properties":{"country_code":{"type":"string"},"total":{"type":"integer"},"native":{"type":"integer"},"aggregate_clone":{"type":"integer"},"native_pct":{"type":"number"}}}}}}}}},"400":{"description":"Invalid country_code or metric_name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/benchmarks/v1/market-data":{"get":{"tags":["Market Data"],"summary":"Country market data for valuation context","description":"Returns the latest country-level macro, currency, tax, risk-free, and country-risk fields from Delphi's country-context authority. Use alongside `/api/benchmarks/v1/index` when an M&A model needs both sector multiples and market context. Public, IP-rate-limited, no PAT required.","parameters":[{"$ref":"#/components/parameters/countryCode"},{"$ref":"#/components/parameters/countries"},{"$ref":"#/components/parameters/marketDataFormat"}],"security":[{}],"responses":{"200":{"description":"Country market factors","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketDataResponse"}},"text/csv":{"schema":{"type":"string","description":"CSV with one row per country and stable column names matching MarketDataRow fields."}}}},"400":{"description":"Invalid country_code, countries, format, or ambiguous country filters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"country_code is not in the published market-data set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Delphi country-context schema is not available in this environment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/benchmarks/v1/snapshots":{"get":{"tags":["Snapshots"],"summary":"Published vintages","description":"Vintage list. Anonymous responses include the active vintage only; Pro PATs include the full history.","security":[{},{"pat":[]}],"responses":{"200":{"description":"Snapshots","content":{"application/json":{"schema":{"type":"object","properties":{"snapshots":{"type":"array","items":{"$ref":"#/components/schemas/Snapshot"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/benchmarks/v1/audit":{"get":{"tags":["Audit"],"summary":"Row-level audit JSON for a snapshot (Pro)","description":"Working-paper packet for a vintage with provenance and methodology metadata. Source_mix and provider names are redacted.","security":[{"pat":[]},{"patAltHeader":[]}],"responses":{"200":{"description":"Audit JSON"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/benchmarks/v1/audit/pdf":{"get":{"tags":["Audit"],"summary":"Audit PDF working paper (Pro)","security":[{"pat":[]},{"patAltHeader":[]}],"responses":{"200":{"description":"PDF","content":{"application/pdf":{}}},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/api/benchmarks/v1/accuracy":{"get":{"tags":["Accuracy"],"summary":"Backtest accuracy metadata","security":[{},{"pat":[]}],"responses":{"200":{"description":"Accuracy metadata"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/benchmarks/v1/diff":{"get":{"tags":["Snapshots"],"summary":"Diff between two published vintages","description":"Returns rows that changed (median, P25, P75, sample_size, confidence_band, country_basis) between two snapshot ids. Pro session or PAT required for non-active 'from' snapshot.","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"metric_name","in":"query","schema":{"type":"string","enum":["EV_EBITDA","EV_REVENUE","PE_RATIO"]}},{"$ref":"#/components/parameters/countryCode"}],"security":[{},{"pat":[]}],"responses":{"200":{"description":"Snapshot diff","content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string","format":"uuid"},"to":{"type":"string","format":"uuid"},"changed":{"type":"array","items":{"type":"object","properties":{"business_type_id":{"type":"string","example":"be-b2b-saas"},"metric_name":{"type":"string"},"country_code":{"type":"string","nullable":true},"median_from":{"type":"number","nullable":true},"median_to":{"type":"number","nullable":true},"median_delta":{"type":"number","nullable":true},"sample_size_from":{"type":"integer","nullable":true},"sample_size_to":{"type":"integer","nullable":true},"country_basis_from":{"$ref":"#/components/schemas/CountryBasis"},"country_basis_to":{"$ref":"#/components/schemas/CountryBasis"}}}}}}}}},"400":{"description":"Invalid snapshot ids","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"}}}}}}