A valuation needs an equity risk premium that reflects the country of the cash flows. Narwhal returns the mature market premium, the country default spread, the country risk premium and the total for each country, and a company cost of capital route that uses the same premium.
Read the equity risk premium for a country
curl -s "https://api.narwhalapi.com/v1/economics/BRA/equity-risk-premium" \
-H "Authorization: Bearer $NARWHAL_API_KEY"{
"country": "BRA",
"as_of": "2026-11-01",
"unit": "percent",
"mature_erp": "3.2973",
"country_default_spread": "2.1300",
"volatility_ratio": "1.5211",
"country_risk_premium": "3.2400",
"total_erp": "6.5373",
"basis": {
"mature_erp": "observed",
"country_default_spread": "table",
"volatility_ratio": "table",
"country_risk_premium": "table",
"total_erp": "table"
},
"methodology_url": "https://narwhalapi.com/methodology/equity-risk-premium"
}All figures are in percent, and as_of is the date they describe. The five numbers build on each other, as the equity risk premium methodology describes.
mature_erpis the implied premium of the mature market, which is the US. It is the same in every country's response.country_default_spreadis the extra yield the country pays over the mature market for default risk.volatility_ratiois equity volatility divided by government bond volatility.country_risk_premiumis the spread times the ratio. For Brazil, 2.13 times 1.5211 is 3.24.total_erpismature_erppluscountry_risk_premium. For Brazil, 3.2973 plus 3.24 is 6.5373.
The basis object tells you which parts are observed, modelled or table. A table value comes from a public country risk table. Those rows are as current as the table, so they move when the table is updated and not with every bond yield. The US response has a spread of 0 and a null volatility_ratio, because the US is the mature market, so its total_erp equals its mature_erp.
Turn the premium into a cost of equity
A cost of equity for a project in one country is the risk-free rate plus a beta times the total premium of the country where the cash flows arise. The methodology states that the figures are in US dollar terms, so use a US risk-free rate. The script reads the 10 year US yield from the yield curve route, takes your beta, then reads all countries from GET /v1/economics/equity-risk-premium and checks that mature_erp plus country_risk_premium equals total_erp before it uses the row.
- Choose the inputs The risk-free rate comes from the API. The beta of 1.2 is a placeholder for your own estimate, since the premium route does not supply a beta.
- Run the script It prints the number of countries and the cost of equity for four of them.
import os from decimal import Decimal import requests HEAD = {"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"} API = "https://api.narwhalapi.com" def get(path, **params): r = requests.get(API + path, params=params, headers=HEAD, timeout=15) r.raise_for_status() return r.json() # Risk-free rate: the 10 year US yield, in percent rf = Decimal(get("/v1/economics/USA/yield-curve", maturity="10y")["yields"][0]["yield_percent"]) beta = Decimal("1.2") # your own estimate for the project countries = {c["country"]: c for c in get("/v1/economics/equity-risk-premium")["countries"]} print(len(countries), "countries") for code in ("USA", "IDN", "BRA", "ZAF"): c = countries[code] mature, crp, total = (Decimal(c[k]) for k in ("mature_erp", "country_risk_premium", "total_erp")) assert mature + crp == total print(f"{code} as_of {c['as_of']} mature {mature} crp {crp} total {total} " f"cost of equity {rf + beta * total:.2f}%") - Read the result The 10 year yield was 4.91 percent. The script returned a cost of equity of 8.87 percent for the US, 11.82 for Indonesia, 12.75 for Brazil and 13.55 for South Africa, from 175 countries in the list.
These are dollar discount rates. A local-currency rate needs the inflation difference between the two currencies added, as the methodology says.
Take beta, cost of debt and WACC for a listed company
For a listed stock the cost of capital route does the same sum and goes on to a WACC. It returns the beta, the risk-free rate, the premium it used, the cost of equity, the cost of debt, the tax rate and the weights in one answer. By default the premium is Narwhal's own total for the stock's country. You can send your own with erp. The cost of capital methodology lists the formulas and refusals.
curl -s "https://api.narwhalapi.com/v1/equities/XNAS:MSFT/cost-of-capital" \
-H "Authorization: Bearer $NARWHAL_API_KEY"{
"id": "XNAS:MSFT",
"currency": "USD",
"beta": {
"window": "2y",
"frequency": "weekly",
"observations": 103,
"raw": "1.1507",
"adjusted": "1.1010",
"used": "raw"
},
"risk_free": {
"rate": "4.9100",
"source": "yield_curve",
"as_of": "2026-11-02"
},
"equity_risk_premium": {
"rate": "3.2973",
"source": "narwhal",
"country": "USA",
"as_of": "2026-11-01",
"basis": "observed"
},
"cost_of_equity": "8.7042",
"cost_of_debt": {
"pre_tax": "8.1917",
"after_tax": "6.0922",
"period": "ttm"
},
"tax_rate": {
"rate": "25.6300",
"basis": "table"
},
"weights": {
"equity": "99.0666",
"debt": "0.9334"
},
"wacc": "8.6798",
"warnings": [],
"methodology_url": "https://narwhalapi.com/methodology/cost-of-capital"
}The cost_of_equity of 8.7042 is the 4.91 risk-free rate plus the raw beta of 1.1507 times the 3.2973 premium. The weights give equity 99.0666 percent and debt 0.9334 percent, and the wacc is 8.6798 percent. Every input carries its basis and date in the full response, and a number that needs care comes with an entry in warnings, which is empty here.
Does the premium change over time?
Yes. The history route lists earlier dates. For Indonesia it returned as_of 2026-10-28 with a total_erp of 5.6830 and as_of 2026-11-01 with 5.7573, while the country_default_spread stayed at 1.6200. mature_erp moved from 3.2230 to 3.2973, and that moved the total.
Store the as_of date next to any rate you use in a valuation, so the number can be traced later. Read the history route when you need to see how a premium has moved between dates.
What happens when the cost of capital route cannot answer?
It returns 422 and names the problem instead of filling a gap. A call for AAPL returned inputs_unavailable with the detail that interest expense was not on file. The route also refuses banks and insurers. Pass your own erp, risk_free or tax_rate where the route accepts them, and handle the 422 in code.
Which countries are covered?
The list route returned 175 countries in the run above. A country appears only when every input for it is available, so a missing code means no row, not a zero. Brazil, Indonesia and South Africa answered, as did the US.
Published .