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.
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.
/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.
-
Project revenue
Grow the most recent reported revenue forward using your
revenue_growthrates, year by year. -
Turn revenue into free cash flow
Apply your
ebit_marginandtax_rate, then add back D&A and subtract capex and the change in working capital — what's left is unlevered free cash flow. -
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. -
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. -
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.
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
-
Clone the repository
git clone https://github.com/abdshaat/pubTools-DCF.git cd pubTools-DCF -
Create and activate a virtual environment
python -m venv .venv # Windows (PowerShell): .venv\Scripts\Activate.ps1 # macOS / Linux: source .venv/bin/activate -
Install dependencies
Installs the app plus its test tools (pytest, hypothesis).
pip install -e ".[dev]" -
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 -
Start the server
The app loads
.envautomatically 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%:
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?..."
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.
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.
Signed in as
Your API keys
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
Get a valuation
/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
| Parameter | Type | Description | |
|---|---|---|---|
wacc | decimal | required | Discount rate. 0.09 = 9%. Must be finite, 0.001–0.50, and greater than
terminal_growth. |
terminal_growth | decimal | required | Finite perpetual growth rate from -0.10 to 0.10 and below WACC. |
ebit_margin | decimal | required | Finite operating margin from -1.0 to 1.0. 0.30 = 30%. |
revenue_growth | decimal | list | required | 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_rate | decimal | default 0.21 | Finite effective tax rate applied to EBIT. Must be from 0.0 to 1.0. |
projection_years | integer | default 5 | Explicit forecast horizon. 3–15. |
sensitivity | boolean | default 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.
| Field | Description |
|---|---|
model_version | Version of the valuation engine that produced this result. Pin this when comparing valuations over time. |
base_financials | The normalized inputs: revenue, EBIT, D&A,
capex, ΔNWC, net debt, diluted shares, current price — with
source_period identifying the fiscal period they came from. |
assumptions | Your 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_value | Sum of all pv_fcf plus
pv_terminal_value. |
equity_value | Enterprise value minus net debt. |
intrinsic_value_per_share | Equity value divided by diluted shares. |
current_price / upside_pct | Live 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. |
sensitivity | Per-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. |
{
"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:
{
"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)" }
]
}
}
| Status | When |
|---|---|
| 200 | Valuation computed. |
| 404 | No 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. |
| 422 | Invalid 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. |
| 429 | Daily valuation request limit exceeded. Wait until
the reset time from X-RateLimit-Reset or follow Retry-After. |
| 502 | Provider data for this ticker couldn't be normalized. Not retryable; contact support. |
| 503 | Upstream data provider unavailable. Retry with backoff. |
Health check
/health
Returns 200 with the service status and current model_version.
Unauthenticated; suitable for load-balancer probes.
Methodology
For each projected year , the engine computes unlevered free cash flow:
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:
The terminal value captures all cash beyond the forecast using Gordon growth off the final projected year:
From enterprise value, subtract net debt for equity value, then divide by diluted shares outstanding:
where is your terminal_growth and
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.