Discounted Cash Flow · Valuation API

A company is worth the cash it will earn — in today's money.

This API turns that idea into a number. Give it a ticker and your assumptions; it fetches the company's financials, runs a discounted cash flow model, and returns an intrinsic value per share with every intermediate figure, auditable to the dollar.

Year 1Year 2Year 3Year 4Year 5Terminal
Future cash flow Present value today Terminal value
The faint bar is the cash a company throws off in each future year. The solid bar is what that cash is worth today once discounted — and the further out it lands, the more it shrinks. Sum the solid bars and you have the valuation.

You supply the assumptions; we supply the financials. Company fundamentals (revenue, EBIT, D&A, capex, working capital, debt, share count) are fetched from our data provider, normalized into a canonical schema, and combined with your inputs by a deterministic valuation engine. Identical requests return identical results for a given model_version and source period.

GET /v1/valuations/{ticker}

v1 scope: non-financial US large caps. Banks and insurers are rejected with 422 — a standard free-cash-flow DCF doesn't apply to financial balance sheets. New to DCF? Start with What is a DCF? Already fluent? Jump to the Quickstart.

What is a DCF?

A discounted cash flow model answers one question: what is a business worth today, based on the cash it will generate in the future? It rests on two ideas most people already believe.

A company is worth the cash it returns to its owners. Not its share price, not its headlines — the actual money left over each year after running the business and reinvesting in it. That leftover is free cash flow.

A dollar later is worth less than a dollar now. Money in hand today can be invested and grow, and the future is uncertain — so a dollar arriving in five years is worth less to you than one today. To compare future cash against today's, you discount it: shrink it by a rate that reflects how far away and how risky it is. That rate is the WACC (weighted average cost of capital).

Put those together: forecast a company's free cash flow for several years, estimate a terminal value for everything after that, discount it all back to today, add it up, subtract debt, and divide by shares. The result is an intrinsic value per share — what the business is worth on fundamentals, independent of the market's current mood.

How it's used

Investors compare intrinsic value to the market price. Trading well below your estimate, a stock may be undervalued; well above, it may be overvalued. But a DCF is a structured way to reason about value, not a crystal ball — the output is only ever as good as the assumptions you feed it. Its real power is showing how much the answer moves when your view of growth, margins, or risk changes. That's why this API returns a full sensitivity grid alongside every valuation, not just a single number.

How a valuation is built

Every request runs the same five steps. The response exposes the output of each one, so you can follow the money from this year's revenue all the way to a per-share value.

  1. Project revenue

    Grow the most recent reported revenue forward using your revenue_growth rates, year by year.

  2. Turn revenue into free cash flow

    Apply your ebit_margin and tax_rate, then add back D&A and subtract capex and the change in working capital — what's left is unlevered free cash flow.

  3. Discount each year back to today

    Divide each year's cash flow by (1 + wacc) raised to that year. A dollar in year five counts for less than a dollar in year one.

  4. Add a terminal value

    Capture all the cash beyond the forecast horizon with the Gordon-growth formula using terminal_growth, then discount that back too. This is often the largest single piece of the answer.

  5. Go from enterprise value to per share

    Sum the present values, subtract net debt to get equity value, and divide by diluted shares for the intrinsic value per share.

Making good assumptions

The financials are ours; the assumptions are yours — and they drive the result far more than the underlying data does. Here's how to choose each one well. Two habits matter most: change one variable at a time so you can see its effect, and read the range, not a single point — the sensitivity grid does this for you.

revenue_growth history → GDP

How fast revenue grows each year. Anchor to the company's own recent growth and its industry, not to hope.

Avoid the hockey stick. No company compounds 20% a year forever. Fade high early rates toward long-run GDP (~2–4%) using per-year values, e.g. 0.12,0.10,0.08,0.06,0.04.

ebit_margin recent actuals

Operating profit as a share of revenue. Start from the last reported margin and the direction it's been heading.

Don't assume expansion for free. Margin gains need a reason — scale, pricing power, or mix shift. Flat is often the honest default.

wacc ~7–10%

The discount rate: the annual return investors require to hold this risk. Higher for volatile, cyclical, or heavily indebted businesses; lower for stable ones.

The single biggest lever. A one-point change can move the valuation 15–25%. When you're unsure, don't guess a point — test a range and read the grid.

terminal_growth ≤ 2–3%

The perpetual growth rate applied after your explicit forecast ends, forever.

Keep it below long-run GDP, and never near WACC. Terminal value is often most of the total, so a high rate quietly inflates everything. Values at or above WACC break the math and are rejected with a 422.

tax_rate ~21%

The effective tax rate applied to operating profit. Use what the company actually pays; the 21% US federal rate is a reasonable starting default.

projection_years 5

How many years you forecast explicitly before the terminal value takes over.

Longer isn't more accurate. Only extend the horizon if you can credibly forecast that far — most businesses can't be called reliably beyond about 5–7 years.

Rule of thumb: because WACC and terminal growth dominate the result, treat any single valuation as one point in a range. Leave sensitivity on (it's the default) and read the spread of the grid before you trust the headline number.

Quickstart

Run the API on your own machine in five steps, then make your first request. You'll need Python 3.11 or newer and a free Financial Modeling Prep API key for the company financials.

Run it locally

  1. Clone the repository

    git clone https://github.com/abdshaat/pubTools-DCF.git
    cd pubTools-DCF
  2. Create and activate a virtual environment

    python -m venv .venv
    # Windows (PowerShell):
    .venv\Scripts\Activate.ps1
    # macOS / Linux:
    source .venv/bin/activate
  3. Install dependencies

    Installs the app plus its test tools (pytest, hypothesis).

    pip install -e ".[dev]"
  4. Add your data-provider key

    Copy the template to a local .env (it's gitignored) and paste your FMP key in.

    # macOS / Linux:
    cp .env.example .env
    # Windows (PowerShell):
    copy .env.example .env
    
    # then edit .env:
    FMP_API_KEY=your-key-here
  5. Start the server

    The app loads .env automatically on startup.

    uvicorn app.api:app --reload

The API is now live at http://127.0.0.1:8000 — interactive OpenAPI docs at /docs, and a health probe at /health. Want to check the engine without a key first? Run the test suite, which uses recorded fixtures: pytest -q.

Make your first request

Value Apple with a 9% discount rate, 2.5% perpetual growth, 30% EBIT margin, and a five-year growth ramp from 8% down to 4%:

Request
curl "http://127.0.0.1:8000/v1/valuations/AAPL?\
wacc=0.09&\
terminal_growth=0.025&\
ebit_margin=0.30&\
revenue_growth=0.08,0.07,0.06,0.05,0.04&\
projection_years=5"

All rates are decimals: 0.09 means 9%. revenue_growth takes a single value applied to every year, or one comma-separated value per projection year. Not sure what to put? See Making good assumptions, or let the endpoint builder assemble the URL for you. Against a hosted deployment, swap the base URL and add the X-API-Key header from Authentication.

Authentication

Requests are authenticated with an API key sent in the X-API-Key header:

curl -H "X-API-Key: YOUR_KEY" "https://ashaat.dev/v1/valuations/AAPL?..."
Production: Supabase-backed deployments require API keys and usage metering is enforced for valuation requests. Public landing, health, and OpenAPI endpoints remain public.
Rate limit: valuation requests are capped at 100 per API key per UTC day by default. Calls made from this website use the same API endpoint and count toward the same key limit. Every valuation response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset; a 429 adds Retry-After.

Live price & caching

Every valuation carries a live market price: current_price is fetched from Finnhub on every request and is never cached anywhere — not by the API, not by a CDN, not by your browser. Responses are therefore Cache-Control: no-store, and conditional requests (ETag/If-None-Match) are not supported: every request returns a fresh 200 priced at that moment, and every request counts against your daily quota.

What is cached — server-side, per ticker — are the slow-moving financial statements. Running several valuations of the same ticker with different assumptions reuses one statement snapshot instead of refetching it, so repeat requests stay fast; the DCF math itself is recomputed on every request from that snapshot plus the live price. fundamentals_as_of tells you which filing the statements come from, while freshness_status, next_refresh_window_at, last_refresh_attempt_at, and last_refresh_success_at expose the durable daily-refresh state. price_as_of/price_fetched_at independently timestamp the live quote.

If the live price is unavailable (provider outage, or a symbol the quote provider doesn't recognize), the API still returns the full valuation — current_price and upside_pct come back null with a warning naming the cause. The intrinsic-value math never depends on the market price.

Your account

Sign in to generate your own API key instantly — no waiting on an operator. Keys you create here are scoped to your account only, and you can label, rename, rotate, and revoke them at any time.

Sign in with GitHub

Or sign in with a one-time link sent to your email:

Try the API

Enter your assumptions, call the API from this page, and keep the generated endpoint URL. Values you'd quote as percentages go in as percentages here — the builder converts them to the decimal form the API expects, validates them against the same rules the server enforces, and emits parameters in canonical order (canonically-ordered URLs are HTTP-cacheable and dedupe cleanly).

Your assumptions

Revenue growth (% per year)
Your endpoint
As curl

Get a valuation

GET /v1/valuations/{ticker}

Runs a DCF for one ticker using the supplied assumptions and the company's most recent annual financials. The ticker is case-insensitive.

Query parameters

ParameterTypeDescription
waccdecimalrequired Discount rate. 0.09 = 9%. Must be finite, 0.001–0.50, and greater than terminal_growth.
terminal_growthdecimalrequired Finite perpetual growth rate from -0.10 to 0.10 and below WACC.
ebit_margindecimalrequired Finite operating margin from -1.0 to 1.0. 0.30 = 30%.
revenue_growthdecimal | listrequired Single value applied to all years (0.05) or comma-separated per-year values (0.08,0.07,0.06,0.05,0.04). A list must have exactly projection_years entries. Each value within ±50%.
tax_ratedecimaldefault 0.21 Finite effective tax rate applied to EBIT. Must be from 0.0 to 1.0.
projection_yearsintegerdefault 5 Explicit forecast horizon. 3–15.
sensitivitybooleandefault true Include a 3×3 sensitivity grid around your assumptions (WACC ±1% × terminal growth ±0.5%). Pass false to omit it.

Response schema

Every number the engine used or produced is returned, so the valuation can be rebuilt in a spreadsheet line by line. Monetary values are raw units of the returned currency.

The statement selector fetches multiple annual candidates and uses the newest complete set whose income, balance-sheet, and cash-flow records share an FY period and exact statement date. It validates available fiscal-year and currency metadata, prefers the latest accepted filing for duplicate periods, and never mixes a newer incomplete period into an older complete set. The response exposes the selection and any fallback through provenance fields and warnings. Quotes refresh independently every 60 seconds by default.

FieldDescription
model_versionVersion of the valuation engine that produced this result. Pin this when comparing valuations over time.
base_financialsThe normalized inputs: revenue, EBIT, D&A, capex, ΔNWC, net debt, diluted shares, current price — with source_period identifying the fiscal period they came from.
assumptionsYour assumptions, fully resolved: a scalar revenue_growth is echoed back expanded to one value per year, and defaults you omitted are filled in. What you see is exactly what the engine used.
projections[]Per year: growth, revenue, EBIT margin, EBIT, cash taxes, NOPAT, D&A, capex, ΔNWC, FCF, discount period/factor, and PV of FCF.
terminal_value / pv_terminal_value Gordon-growth terminal value and its present value.
enterprise_valueSum of all pv_fcf plus pv_terminal_value.
equity_valueEnterprise value minus net debt.
intrinsic_value_per_shareEquity value divided by diluted shares.
current_price / upside_pctLive market price (fetched from Finnhub on every request, never cached) and the implied difference between it and intrinsic value, in percent. null when the live price is unavailable — the rest of the valuation is still returned, with a warning.
sensitivityPer-share value across a 3×3 grid: rows are wacc_values (your WACC ±1%), columns are terminal_growth_values (±0.5%). The center cell equals intrinsic_value_per_share. Cells where the math is undefined (growth ≥ WACC) are null. Omitted when sensitivity=false.
200 OK — abridged example
{
  "request_id": "8bd53754-b11d-4d6f-9634-a47480f6b97d",
  "computed_at": "2026-07-11T18:30:00Z",
  "model_version": "0.2.0",
  "data_version": "sha256:…",
  "data_provider": "financialmodelingprep",
  "currency": "USD",
  "monetary_unit": "raw_currency_units",
  "fundamentals_as_of": "2025-09-27",
  "freshness_status": "current_as_of_daily_refresh",
  "next_refresh_window_at": "2026-07-18T22:00:00Z",
  "last_refresh_attempt_at": "2026-07-17T22:00:03Z",
  "last_refresh_success_at": "2026-07-17T22:00:08Z",
  "price_as_of": "2026-07-17T20:00:00Z",
  "price_fetched_at": "2026-07-11T18:29:59Z",
  "fiscal_year": "2025",
  "statement_period": "FY",
  "filing_date": null,
  "accepted_at": null,
  "statement_selection": "latest_complete_annual",
  "disclaimer": "Model estimate based on supplied assumptions; not investment advice.",
  "ticker": "AAPL",
  "base_financials": {
    "source_period": "FY2025 (2025-09-27)",
    "revenue": 391035000000.0,
    "ebit": 123216000000.0,
    "da": 11445000000.0,
    "capex": 9447000000.0,
    "delta_nwc": -3651000000.0,
    "net_debt": 76686000000.0,
    "diluted_shares": 15408095000.0
  },
  "assumptions": {
    "wacc": 0.09,
    "terminal_growth": 0.025,
    "tax_rate": 0.21,
    "ebit_margin": 0.3,
    "projection_years": 5,
    "revenue_growth": [0.08, 0.07, 0.06, 0.05, 0.04]
  },
  "projections": [
    {
      "year": 1,
      "revenue_growth": 0.08,
      "revenue": 422317800000.0,
      "ebit_margin": 0.3,
      "ebit": 126695340000.0,
      "cash_taxes": 26606021400.0,
      "nopat": 100089318600.0,
      "da": 12360600000.0,
      "capex": 10202760000.0,
      "delta_nwc": -3943080000.0,
      "fcf": 106190238600.0,
      "discount_period": 1.0,
      "discount_factor": 0.9174,
      "pv_fcf": 97422237247.71
    },
    … years 2–4 …
    {
      "year": 5,
      "revenue": 523060190845.92,
      "ebit": 156918057253.78,
      "fcf": 131521537733.17,
      "discount_factor": 0.6499,
      "pv_fcf": 85479975347.01
    }
  ],
  "terminal_value": 2073993479638.47,
  "pv_terminal_value": 1347953457395.16,
  "enterprise_value": 1809082459088.78,
  "equity_value": 1732396459088.78,
  "intrinsic_value_per_share": 112.4342,
  "current_price": 245.5,
  "upside_pct": -54.202,
  "warnings": [],
  "sensitivity": {
    "wacc_values": [0.08, 0.09, 0.1],
    "terminal_growth_values": [0.02, 0.025, 0.03],
    "intrinsic_value_per_share": [
      [124.53, 134.04, 145.45],
      [105.79, 112.43, 120.19],
      [91.74, 96.6, 102.15]
    ]
  }
}

Validation & errors

Assumption errors return 422 with the offending field named. Existing clients can keep reading detail; new clients should use the versioned error envelope and retain the request_id when contacting support:

422 Unprocessable Entity
{
  "detail": [
    { "field": "terminal_growth", "message": "must be less than wacc (Gordon growth formula)" }
  ],
  "error": {
    "version": "1",
    "code": "invalid_assumptions",
    "message": "DCF assumptions are invalid.",
    "request_id": "8bd53754-b11d-4d6f-9634-a47480f6b97d",
    "fields": [
      { "field": "terminal_growth", "code": "invalid_value", "message": "must be less than wacc (Gordon growth formula)" }
    ]
  }
}
StatusWhen
200Valuation computed.
404No valuation available for this ticker — either the symbol doesn't exist, or it falls outside the supported universe (non-financial US large caps). Retrying won't change the result.
422Invalid assumptions (terminal_growth ≥ wacc, non-finite or out-of-range rates, projection_years outside 3–15, growth beyond ±50%, malformed or wrong-length revenue_growth) — or a financial-sector ticker, which this model doesn't support.
429Daily valuation request limit exceeded. Wait until the reset time from X-RateLimit-Reset or follow Retry-After.
502Provider data for this ticker couldn't be normalized. Not retryable; contact support.
503Upstream data provider unavailable. Retry with backoff.

Health check

GET /health

Returns 200 with the service status and current model_version. Unauthenticated; suitable for load-balancer probes.

Methodology

For each projected year t, the engine computes unlevered free cash flow:

FCFt= EBITt × (1tax) +D&At capext ΔNWCt

Revenue grows by your per-year rates; EBIT applies your margin to each year's revenue; D&A, capex, and ΔNWC scale with revenue at the base year's observed ratios. Each year's cash flow is discounted at your WACC with end-of-year timing, then summed with the discounted terminal value to give enterprise value:

EV= t=1 N FCFt (1+WACC)t + TV (1+WACC)N

The terminal value captures all cash beyond the forecast using Gordon growth off the final projected year:

TV= FCFN×(1+g) WACCg

From enterprise value, subtract net debt for equity value, then divide by diluted shares outstanding:

Equity value=EVnet debt Value / share= Equity valuediluted shares

where g is your terminal_growth and N is projection_years.

DCF outputs are highly sensitive to wacc and terminal_growth — small changes move the result a lot. That's why every response includes the sensitivity grid by default: read the range it spans, not just the point estimate. In the example above, ±1% of WACC moves the per-share value from $96.60 to $134.04.

Disclaimer

Valuations returned by this API are model outputs computed from your assumptions and third-party financial data. They are estimates for analysis, not investment advice, recommendations, or offers to buy or sell any security. Verify important figures against primary sources before relying on them.