---
title: "Two-stage DCF intrinsic value + sensitivity"
method: GET
path: "/api/v1/equities/{symbol}/intrinsic-value"
tags: ["Equities Indices"]
---

# Two-stage DCF intrinsic value + sensitivity

`GET /api/v1/equities/{symbol}/intrinsic-value`

Two-stage discounted-cash-flow intrinsic value per share for a U.S. filer, derived entirely from SEC EDGAR XBRL fundamentals. Free cash flow is computed per fiscal year as operating cash flow minus the absolute capital expenditure, outer-joined by period; the base FCF is the most recent valid year (override-able). Stage 1 projects `stage1_years` of explicit FCF at `stage1_growth` (defaulting to the historical FCF CAGR, clamped to [-20%, +50%]); the terminal value uses the Gordon growth model. Enterprise value is bridged to equity by net debt and divided by shares outstanding. A 25-cell (5x5) sensitivity table varies the discount rate (+-150bp/75bp) and stage-1 growth (+-4pp/2pp). The result is an assumption-sensitive MODEL ESTIMATE, not investment advice (see `disclaimer`). For financial-company SIC codes (banks / insurers / brokers) the value is still computed but flagged `dcf_applicable=false` with a warning. A missing price degrades `margin_of_safety` to null without affecting the valuation. Source is `sec_edgar_xbrl`.

## Path parameters

- `symbol` string, required

## Query parameters

- `discount_rate` number — Annual discount rate (WACC proxy), 0.01-0.50. Default 0.09.
- `terminal_growth` number — Perpetual terminal growth rate, -0.05 to 0.15. Must be strictly less than discount_rate (min spread 0.005). Default 0.025.
- `stage1_years` integer — Number of explicit stage-1 projection years, 1-10. Default 5.
- `stage1_growth` number, nullable — Stage-1 annual FCF growth, -0.20 to 0.50. Omit to derive from the historical FCF CAGR (clamped).
- `base_fcf_override` number, nullable — Override the base free cash flow (in dollars). Must be > 0. Lets a short-history filer through the >=2-year guard.

## Response `200`

Successful Response

- EnvelopeIntrinsicValuePayload
  - `data` IntrinsicValuePayload, required — Full intrinsic-value response payload.
    - `symbol` string, required — Ticker as requested, normalized to upper case.
    - `assumptions` DcfAssumptions, required — The assumption set the valuation was run under (echoed back).
      - `discount_rate` number, required — Annual discount rate (WACC proxy) applied to projected cash flows.
      - `terminal_growth` number, required — Perpetual growth rate used in the Gordon terminal value. Strictly less than discount_rate (min spread 0.005).
      - `stage1_years` integer, required — Number of explicit stage-1 projection years.
      - `stage1_growth` number, nullable — Stage-1 annual FCF growth rate actually used.
      - `growth_source` string, required — How stage1_growth was set: historical_cagr / clamped_min / clamped_max / user_override.
      - `base_fcf_override_used` boolean, required — True when the caller supplied base_fcf_override (history guard bypassed).
    - `inputs` DcfInputs, required — The resolved fundamental inputs that fed the valuation.
      - `base_fcf` number, nullable — Base free cash flow grown across stage 1. Most recent valid year unless overridden.
      - `base_fcf_source` string, required — latest_fiscal_year or user_override.
      - `base_fcf_year` string, nullable — Fiscal period end (`YYYY-MM-DD`) the base FCF came from. Null when overridden.
      - `fcf_3y_median` number, nullable — Median of the most recent (up to 3) valid annual FCF values, for the anomaly warning.
      - `fcf_history` FcfHistoryRow[], required — Up to 6 most-recent annual FCF rows, newest first.
        - `end` string, required — Fiscal period end date (`YYYY-MM-DD`).
        - `operating_cash_flow` number, nullable — Reported operating cash flow for the period. Null when the concept is absent.
        - `capex` number, nullable — Reported capital expenditure (as filed; sign as reported). Null when absent.
        - `fcf` number, nullable — Free cash flow = operating_cash_flow - abs(capex). Null when either component is missing for this year.
      - `shares_outstanding` number, nullable — Shares used for the per-share value.
      - `shares_source` string, nullable — CommonStockSharesOutstanding or WeightedAverageNumberOfSharesOutstandingBasic.
      - `total_debt` number, nullable — Total debt used in the net-debt bridge.
      - `debt_source` string, nullable — DebtAndCapitalLeaseObligations / long_term_debt+short_term_debt / long_term_debt / short_term_debt. Null when no debt concept resolved.
      - `cash` number, nullable — Cash and equivalents used in the net-debt bridge. Null when the concept is absent (treated as 0).
      - `net_debt` number, nullable — total_debt - cash. Missing components treated as 0 with net_debt_partial=true.
      - `net_debt_partial` boolean, required — True when debt or cash was partially missing (e.g. long-term debt only, or cash absent) so net debt is an underestimate.
      - `current_price` number, nullable — Most recent close used for margin_of_safety. Null when the price fetch degraded (the SEC valuation is unaffected).
      - `as_of` string, nullable — Fiscal period end the valuation rests on (`YYYY-MM-DD`).
    - `outputs` DcfOutputs, required — The valuation outputs.
      - `enterprise_value` number, nullable — Present value of stage-1 cash flows plus the discounted terminal value.
      - `equity_value` number, nullable — Enterprise value minus net debt.
      - `intrinsic_value_per_share` number, nullable — Equity value divided by shares outstanding.
      - `margin_of_safety` number, nullable — (intrinsic_per_share - current_price) / current_price. Null when the price is unavailable.
      - `dcf_applicable` boolean, required — False for financial-company SIC codes (banks / insurers / brokers) where a single-name FCF DCF is structurally inappropriate; the value is still computed but is illustrative only.
      - `warning` string, nullable — Caveat(s): financial-company applicability and/or base-FCF deviation from the 3-year median. Null when none apply.
    - `stage1_projections` Stage1Projection[], required — Explicit stage-1 projection, one row per year.
      - `year` integer, required — Projection year (1..stage1_years).
      - `fcf` number, nullable — Projected free cash flow for the year = base_fcf * (1 + stage1_growth)^year.
      - `present_value` number, nullable — Discounted present value of that year's FCF.
    - `sensitivity` SensitivityRow[], required — 25-row (5x5) sensitivity of intrinsic value per share over discount_rate x stage1_growth.
      - `discount_rate` number, required — Discount rate for this cell (center +-150bp/75bp).
      - `stage1_growth` number, required — Stage-1 growth for this cell (center +-4pp/2pp).
      - `intrinsic_per_share` number, nullable — Intrinsic value per share for this assumption pair. Null when discount_rate - terminal_growth < 0.005 (the Gordon denominator is too small).
    - `disclaimer` string, required — Plain-language caveat that this is an assumption-sensitive model estimate, not investment advice.
    - `source` string — Fundamentals source. Always `sec_edgar_xbrl`.
  - `meta` SugraMeta, required — Metadata on a /api/v1/* response envelope built through `helpers.response.sugra_response`, which is how routes are expected to answer. A route that assembles its own `meta` dict carries only the keys it writes itself, so an optional field below can be absent because this response has nothing to report OR because that route does not build its envelope here - the two are not distinguishable from the outside (API-43).
    - `endpoint` string, required — Requested endpoint path.
    - `data_time` string, required — ISO 8601 timestamp the data on this response is stamped with. It is the source's own timestamp whenever the source supplied one this API could read; when it did not, this field falls back to the value of `response_time` and `data_age_days` is omitted, so the PRESENCE of that field is the signal to read - with the one exception named in its own description, a route that substitutes its own current time for a source timestamp it never received. Usually UTC (`Z`), but a source stating its own numeric offset keeps it (2026-04-16T14:30:00+09:00) rather than being converted a second time. For a source that publishes by period this is the period's START (see `period`) and for one that publishes by calendar day it is that day's midnight - in neither case a moment at which anything was observed or released.
    - `response_time` string, required — ISO 8601 UTC timestamp when this response was produced.
    - `provider` string, required — API name and version.
    - `data_age_days` number, nullable — Age of the data in days at the moment this response was produced, i.e. `response_time` minus `data_time`. Present ONLY when the timestamp this response is stamped with is a clock time that could be read as a real instant. It is ABSENT - never 0 - in every other case. Absent when no readable source timestamp was supplied, because `data_time` then repeats `response_time` and a zero age would assert that the data is current precisely where its true age is unknown. Absent when the source names a calendar day, a month, a quarter or a year (see `period`): the instant is then a boundary this API anchored at midnight, and time since a day or a quarter BEGAN is a different quantity from the age of the data - a daily series is out by up to a day, a quarterly one by up to a quarter. A midnight counts as such a boundary whichever zone it is stated in, and whether the source stated it or this API anchored it. The one case this field cannot see is a route that substitutes its own current time for a source timestamp it never received: the substituted value is a real, readable instant and is indistinguishable from one the source stated, so the age reads as roughly 0. The shared cache-and-fetch helper behind most routes stopped doing that (API-43), but the presence of this field is a statement about the timestamp the response carries, not a guarantee about the route that supplied it. Rounded to 0.001 day (86.4 seconds), so 0.0 is a real measured age anywhere within roughly +/-43 seconds and not a stand-in for unknown; a source stamping an instant in the future reports a negative value (-0.001 or less) rather than being clamped. Sources publish on very different cadences, so a non-zero age is normal, not an error. Preserve absence in client code: a generated client that materialises a missing optional number as its numeric default turns 'age unknown' back into 'age zero', which is the exact confusion this field exists to remove.
    - `source` string, nullable — Identifier of the primary upstream source used for this response.
    - `attribution` string, nullable — Human-readable attribution mandated by an upstream source (e.g. a securities regulator or self-regulatory organization). Present only on responses whose source requires the owner and source to be clearly identified. Do not remove or alter it when using the response.
    - `fallback_used` boolean, nullable — True when the primary source failed and a fallback produced the data.
    - `fallback_chain` string[], nullable — Ordered list of sources attempted, in the order they were tried.
    - `cached` boolean, nullable — True when this response was served from the internal cache.
    - `stale` boolean, nullable — True when the cached response was returned after the upstream rate-limited or errored. Clients can use this to detect degraded data.
    - `period` string, nullable — Unit of observation, when the source publishes by period rather than by instant. `data_time` carries the period's START instant so it stays machine-readable; this field preserves what that instant used to mean, which the conversion would otherwise erase. Present only for such sources, and only when the source hands the API the label itself - a client that converts the period to its start instant before building the envelope loses the label, though not the age exclusion, which is decided by the instant. Note that `data_age_days` is omitted whenever this is present, because an age measured from a period start is not a freshness figure.
    - `notes` string, nullable — Data-quality caveat about THIS response - how old the underlying report is, a chokepoint AIS lower-bound, or that a source-reported `data_time` could not be read and the response time is shown instead. Distinct from `attribution`, which is a licensing obligation. Multiple caveats are joined with ' | '. Present only when there is one.

## Other responses

- `401` — Missing or invalid `x-api-key` header. JSON body with a stable `code` distinguishing `missing_api_key` (no header sent) from `invalid_api_key` (header sent, key not accepted); any other 401 source carries the generic `unauthorized` with its detail as `reason`. Plus `hint`. `plan` is always null on 401 - an unauthenticated request has no plan; quota exhaustion is 429, not 401.
- `422` — Validation Error
- `429` — Daily rate limit exceeded. Check `X-RateLimit-Reset` for the next window.
- `503` — Upstream source is temporarily unavailable. Retry after a short delay.

## Changes

- **2026-09-01** `328d061c12ca` — 3 info
  - added the optional property `meta/data_age_days` to the response with the `200` status
  - added the optional property `meta/notes` to the response with the `200` status
  - added the optional property `meta/period` to the response with the `200` status
- **2026-08-08** `4c4530760ba1` — 12 info
  - added the optional property `code` to the response with the `401` status
  - added the optional property `code` to the response with the `429` status
  - added the optional property `code` to the response with the `503` status
  - added the optional property `hint` to the response with the `401` status
  - …8 more

[Change history](https://skmtc.dev/sugra/apis/sugra-api/changes/api/v1/equities/:symbol/intrinsic-value/get.md)

---

[API](https://skmtc.dev/sugra/apis/sugra-api.md) · [All operations](https://skmtc.dev/sugra/apis/sugra-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/sugra/sugra-api/revisions/68953d8a221b/schema)
