Stocks API reference
Prices, history, company events and valuation ratios for listed stocks
Overview
Prices, price history, company events, market cap and valuation ratios for listed stocks. Every request needs a Bearer key, and REST and MCP share one account quota.
A listing id is the exchange's four-letter code, a colon and the ticker, such as XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing, and every answer carries the canonical id. Prices and amounts are decimal strings.
Each listing can carry a company_id. It links the listing to the company in the Filings API, so a price, a valuation and a financial statement meet on one id.
Base URL: https:/. The MCP tool is equities, with one action for each route below.
A company_ joins a listing to the Filings API: financial statements, filings and holders.
Plans and limits
| What | Free | Basic | Pro |
|---|---|---|---|
| Latest price | 15 minutes late | Live | Live |
| Day volume on a price | Left out | Included | Included |
| Daily bars and past dividends, splits and earnings | Last 12 months | Last 5 years | Full history |
| One-minute bars | Not included | Included, within the plan's history | Included, within the plan's history |
| Search and profile | Included | Included | Included |
| Movers | Not included | Included | Included |
| Market cap, P/E, P/S, P/B and dividend yield | Not included | Included | Included |
| Valuation history | Not included | Last 5 years | Full history |
| Cost of capital and beta | Not included | Included | Included |
See pricing for plan prices and monthly request limits. Each request counts as one call unless a route says otherwise.
- Free prices are delayed by 15 minutes. A delayed answer carries delayed: true and leaves out the day volume.
- Valuation covers US and Korea listings that match a company in our filings. Korea shows P/E only for now.
- A ratio that cannot be worked out correctly is left out. It is never sent as 0.
- The earnings eps_actual is on the market data basis. The EPS behind pe_ratio comes from company filings. The two can differ, so do not compare them directly.
- Cost of capital covers US and Korea listings that match a company in our filings. You send your own equity risk premium in erp. The method is written once in the cost of capital methodology, and every answer links to it.
- A bank or an insurer returns 422 not_applicable, because interest is an operating cost for it. An input that cannot be worked out is named in 422 inputs_unavailable, never filled in.
- Valuation history has one point for each fiscal period end (quarter or year end), not one for each day. Each point uses the close on or just before that day, adjusted for splits only, and the figures as filed for that period.
- Basic reaches back 5 years and Pro has full history. When your plan cuts older rows, start comes back with the first date served.
- Earnings are past results only. Upcoming earnings dates are not included yet.
- Japan profiles carry only name, exchange, country, currency and company_id.
Endpoints
Every route is a GET and needs your key in the Authorization: Bearer header. The examples show the shape of an answer, not live data.
Search stocks by ticker or name
GET /v1/equities/search
Listings whose ticker or name starts with q, any case: an exact ticker first, then other tickers, then names. Optional country (three letters) and type filters. Up to 50 a page, with next_. Read from our own symbol list, so it is open to every plan and counts one call.
MCP: tool equities, action search.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
q | query | Yes | string | Start of a ticker or of a company name, any case, at least 2 characters (for example ms or micro). Order: the exact ticker, other tickers, the start of a name, then the start of a later word of a name; US primary listings first within each. |
country | query | No | string | Only listings in this country, three letters (USA, KOR). |
type | query | No | one of common_stock, preferred_stock, depositary_receipt, reit, closed_end_fund, unit, right, warrant, other | Only this kind of listing. |
cursor | query | No | string | next_ of the previous page, with the same other parameters. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
results | list of object | Always | At most 50 a page. |
results[].id | string | Always | Canonical listing id. |
results[].symbol | string | Always | Ticker on that exchange. |
results[].name | string | Always | Name as our symbol list holds it. |
results[].type | one of common_stock, preferred_stock, depositary_receipt, reit, closed_end_fund, unit, right, warrant, other | Always | What kind of instrument it is. |
results[].mic | string | Always | The exchange's four-letter code. |
results[].country | string | Optional | Country of the listing, three letters. |
results[].currency | string | Always | Currency of the prices. |
results[].company_ | string | Optional | The company's filings id, when the listing matches one cleanly. |
next_ | string | Always | Pass it back to read the next page. Null on the last page. |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/search?q=micro"{
"results": [
{
"id": "XNAS:MSFT",
"symbol": "MSFT",
"name": "Microsoft Corporation",
"type": "common_stock",
"mic": "XNAS",
"country": "USA",
"currency": "USD",
"company_id": "usa:0000789019"
}
],
"next_cursor": null
}Profile of one stock
GET /v1/equities/{id}
The listing (symbol, name, type, exchange, country, currency), the company's sector, industry and website, and company_ when the listing matches a company in our filings. Open to every plan. Japan listings carry only name, exchange, country, currency and company_. Fields that are not known are left out.
MCP: tool equities, action profile.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
id | path | Yes | string | Listing id: the exchange's four-letter code, a colon and the ticker, for example XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing; the answer carries the canonical id. Share classes use a dot (BRK.B); BRK-B is accepted. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
id | string | Always | Canonical listing id. |
symbol | string | Always | Ticker on that exchange. |
name | string | Always | Name as our symbol list holds it. |
type | one of common_stock, preferred_stock, depositary_receipt, reit, closed_end_fund, unit, right, warrant, other | Always | What kind of instrument it is. |
mic | string | Always | The exchange's four-letter code. |
country | string | Optional | Country of the listing, three letters. |
currency | string | Always | Currency of the prices. |
sector | string | Optional | Sector. Left out for Japan listings, which carry no profile text. |
industry | string | Optional | Industry. Left out for Japan listings. |
website | string | Optional | The company's website. Left out for Japan listings. |
logo_ | string | Optional | Our own copy of the company's logo. Left out when we have none. |
company_ | string | Optional | The company's filings id, when the listing matches one cleanly. |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/XNAS:MSFT"{
"id": "XNAS:MSFT",
"symbol": "MSFT",
"name": "Microsoft Corporation",
"type": "common_stock",
"mic": "XNAS",
"country": "USA",
"currency": "USD",
"sector": "Technology",
"industry": "Software",
"website": "https://www.microsoft.com",
"company_id": "usa:0000789019"
}Latest price of one stock
GET /v1/equities/{id}/price
Latest price of one listing. Free plans see the price 15 minutes late and the answer carries delayed: true. change_ is in percent. An unknown id is 404 not_found; a market without prices yet is 404 not_covered.
MCP: tool equities, action price.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
id | path | Yes | string | Listing id: the exchange's four-letter code, a colon and the ticker, for example XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing; the answer carries the canonical id. Share classes use a dot (BRK.B); BRK-B is accepted. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
id | string | Always | Canonical listing id. |
symbol | string | Always | Ticker on that exchange. |
currency | string | Always | Currency of every price field. Pence are converted to pounds. |
price | string | Always | Last price, in currency. |
previous_ | string | Optional | Previous session's close. Left out when it is not known. |
change | string | Optional | price minus previous_. Left out with previous_. |
change_ | string | Optional | Change since previous_, in percent (1.5 means 1.5%). |
volume | string | Optional | Shares traded today. Left out when it is not known, and always left out on a delayed answer. |
market_ | one of pre, open, post, closed | Always | State of the listing's exchange at timestamp: pre, open, post or closed. Only US exchanges have pre and post. |
timestamp | date-time | Always | When the price was valid (UTC, whole seconds). |
stale | boolean | Always | True when the price is older than 15 minutes while the exchange is open, or could not be refreshed. A closed market is never stale. |
delayed | always true | Optional | Present, as true, only when the price is 15 minutes late (Free plan). |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/XNAS:MSFT/price"{
"id": "XNAS:MSFT",
"symbol": "MSFT",
"currency": "USD",
"price": "421.35",
"previous_close": "418.90",
"change": "2.45",
"change_percent": "0.58",
"volume": "18234100",
"market_session": "open",
"timestamp": "2026-10-26T15:42:00Z",
"stale": false
}Latest prices for up to 50 stocks
GET /v1/equities/prices
Latest price of each listed id. One call is counted for each id that returns a price, taken in one step: if fewer calls are left than ids that resolved, the whole request is refused with 429 quota_exceeded. Ids that return no price are listed in errors and cost nothing. Free plans see prices 15 minutes late. change_ is in percent.
Send up to 50 ids. A batch is charged in one step: if you have fewer calls left than ids that resolve, the whole request returns 429 quota_exceeded.
MCP: tool equities, action prices.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
ids | query | Yes | string | Comma-separated listing ids, at most 50 after duplicates are removed. Each id follows the same rules as the single-price route. One call is counted for each id that returns a price. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
prices | list of object | Always | One price per id that resolved, by id. |
prices[].id | string | Always | Canonical listing id. |
prices[].symbol | string | Always | Ticker on that exchange. |
prices[].currency | string | Always | Currency of every price field. Pence are converted to pounds. |
prices[].price | string | Always | Last price, in currency. |
prices[].previous_ | string | Optional | Previous session's close. Left out when it is not known. |
prices[].change | string | Optional | price minus previous_. Left out with previous_. |
prices[].change_ | string | Optional | Change since previous_, in percent (1.5 means 1.5%). |
prices[].volume | string | Optional | Shares traded today. Left out when it is not known, and always left out on a delayed answer. |
prices[].market_ | one of pre, open, post, closed | Always | State of the listing's exchange at timestamp: pre, open, post or closed. Only US exchanges have pre and post. |
prices[].timestamp | date-time | Always | When the price was valid (UTC, whole seconds). |
prices[].stale | boolean | Always | True when the price is older than 15 minutes while the exchange is open, or could not be refreshed. A closed market is never stale. |
prices[].delayed | always true | Optional | Present, as true, only when the price is 15 minutes late (Free plan). |
errors | list of object | Always | Ids that returned no price. They are not counted against the quota. |
errors[].id | string | Always | The id as it was asked for. |
errors[].code | one of not_found, not_covered, upstream_busy | Always | not_found: no such listing. not_covered: the exchange has no prices yet. upstream_busy: the price could not be fetched now; try again shortly. |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/prices?ids=XNAS:MSFT,XNAS:NVDA"{
"prices": [
{
"id": "XNAS:MSFT",
"symbol": "MSFT",
"currency": "USD",
"price": "421.35",
"previous_close": "418.90",
"change": "2.45",
"change_percent": "0.58",
"volume": "18234100",
"market_session": "open",
"timestamp": "2026-10-26T15:42:00Z",
"stale": false
},
{
"id": "XNAS:NVDA",
"symbol": "NVDA",
"currency": "USD",
"price": "188.20",
"previous_close": "187.10",
"change": "1.10",
"change_percent": "0.59",
"volume": "152003400",
"market_session": "open",
"timestamp": "2026-10-26T15:42:00Z",
"stale": false
}
],
"errors": [
{
"id": "XNAS:NOPE",
"code": "not_found"
}
]
}Top US stock movers
GET /v1/equities/movers
The top 25 US stocks and ETFs by direction: gainers, losers or active (most traded by value among the day's biggest gainers and losers, not the whole market). Only ordinary shares and ETFs priced at $1 or more, with at least 100,000 shares and $1 million traded today; ranked by us and cached for 10 minutes. change_ is in percent. Basic and Pro only (Free gets 403 plan_upgrade_required). market is us for now; another market is 404 not_covered, an unknown direction is 400 invalid_query. One call.
Movers are for the US only. A different market returns 404 not_covered.
MCP: tool equities, action movers.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
market | query | No | string | Market to rank. Only us for now; another market is 404 not_covered. |
direction | query | No | string | gainers (biggest rise), losers (biggest fall) or active (most traded by value among the day's biggest gainers and losers, not the whole market). Anything else is 400 invalid_query. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
market | always "us" | Always | The market ranked. |
direction | one of gainers, losers, active | Always | The ranking asked for. |
movers | list of object | Always | Up to 25, best first. Only ordinary shares and ETFs priced at $1 or more, with at least 100,000 shares and $1 million traded today. |
movers[].id | string | Always | Canonical listing id. |
movers[].symbol | string | Always | Ticker on that exchange. |
movers[].name | string | Always | Company or fund name. |
movers[].price | string | Always | Latest price, in US dollars. |
movers[].change_ | string | Always | Change since the previous close, in percent (31.4 means 31.4%), 2 places. |
movers[].volume | string | Always | Shares traded today. |
timestamp | date-time | Always | When the newest price in the list was valid (UTC). |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/movers?market=us&direction=gainers"{
"market": "us",
"direction": "gainers",
"movers": [
{
"id": "XNAS:NVDA",
"symbol": "NVDA",
"name": "NVIDIA Corporation",
"price": "188.20",
"change_percent": "4.12",
"volume": "152003400"
}
],
"timestamp": "2026-10-26T15:42:00Z"
}Price bars of one stock
GET /v1/equities/{id}/bars
Daily (1d) or one-minute (1m) bars, oldest first, adjusted for splits and dividends. History: Free 12 months, Basic 5 years, Pro everything; start is sent back when the plan raised it. One-minute bars are for Basic and Pro (Free gets 403 plan_upgrade_required), reach back to 2020 and cover at most 5 days a call (400 range_too_large). Today's daily bar appears after the exchange closes, and for Free 15 minutes after; end is sent back when that cut the range. Up to 5,000 bars a page, with next_; a cursor that no longer matches the prices is 409 cursor_expired. One call a page.
Bars are adjusted for splits and dividends. If a split or dividend changes the history between two pages, the cursor stops working and returns 409 cursor_expired.
MCP: tool equities, action bars.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
id | path | Yes | string | Listing id: the exchange's four-letter code, a colon and the ticker, for example XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing; the answer carries the canonical id. Share classes use a dot (BRK.B); BRK-B is accepted. |
interval | query | No | one of 1d, 1m | 1d (one bar per session) or 1m (one-minute bars, Basic and Pro only). Anything else is 400 invalid_interval. |
start | query | No | string | First bar. 1d: a date (YYYY-MM-DD), inclusive; left out means the oldest bar your plan reaches. 1m: a UTC time (YYYY-MM-DDTHH:MM:SSZ, or a date for 00:00 UTC), inclusive; left out means one day before end. |
end | query | No | string | Last bar. 1d: a date, inclusive; left out means the latest session whose bar is out. 1m: a UTC time, exclusive; left out means now, or five days after start. At most 5 days between start and end for 1m (400 range_too_large). |
cursor | query | No | string | next_ of the previous page, with the same other parameters. A cursor stops working when the prices behind it changed (a split or dividend): 409 cursor_expired. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
id | string | Always | Canonical listing id. |
currency | string | Always | Currency of every price. Pence become pounds. |
adjusted | boolean | Always | Always true: prices are adjusted for splits and dividends. A page and the next one always agree; if a split or dividend changes the history between two pages, the cursor stops working (409 cursor_expired) instead of mixing the two. |
start | string | Optional | Sent only when the plan raised the start: the first day (daily) or time (1m) served. |
end | string | Optional | Sent only on Free daily bars, when the 15-minute delay cut the range: the last day served. Today's bar reaches Free 15 minutes after the exchange closes. |
bars | list of object | Always | At most 5,000 bars a page. |
bars[].date | string | Optional | Session date (YYYY-MM-DD). Daily bars only; left out on one-minute bars. |
bars[].timestamp | date-time | Always | Start of the bar's period (UTC): the exchange's open for a daily bar, the minute for a one-minute bar. |
bars[].open | string | Always | First price of the period. |
bars[].high | string | Always | Highest price of the period. |
bars[].low | string | Always | Lowest price of the period. |
bars[].close | string | Always | Last price of the period. |
bars[].volume | string | Optional | Shares traded in the period. Left out when it is not known. |
next_ | string | Always | Pass it back to read the next page. Null on the last page. |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/XNAS:MSFT/bars?interval=1d&start=2026-10-01"{
"id": "XNAS:MSFT",
"currency": "USD",
"adjusted": true,
"bars": [
{
"date": "2026-10-23",
"timestamp": "2026-10-23T13:30:00Z",
"open": "417.10",
"high": "420.55",
"low": "415.80",
"close": "418.90",
"volume": "21034500"
}
],
"next_cursor": null
}Dividends of one stock
GET /v1/equities/{id}/dividends
Cash dividends of one listing, newest ex-date first, paged. The list may include announced future rows. The plan sets how far back the past goes: Free 12 months, Basic 5 years, Pro full. start is sent only when older rows were cut off; upcoming rows are never cut. Each request counts one call. An unknown id is 404 not_found; a market without data yet is 404 not_covered.
MCP: tool equities, action dividends.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
id | path | Yes | string | Listing id: the exchange's four-letter code, a colon and the ticker, for example XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing; the answer carries the canonical id. Share classes use a dot (BRK.B); BRK-B is accepted. |
cursor | query | No | string | next_ of the previous page, with the same other parameters. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
id | string | Always | Canonical listing id. |
start | date | Optional | First day the plan's history window reaches (Free 12 months, Basic 5 years, Pro full). Present only when older rows exist that the plan cannot see; upcoming rows are never cut. |
dividends | list of object | Always | Newest ex-date first. May include announced future rows. |
dividends[].ex_ | date | Always | First day the share trades without the dividend. |
dividends[].amount | string | Always | Dividend per share, in currency. |
dividends[].currency | string | Always | Currency of amount. Pence are converted to pounds. |
dividends[].pay_ | date | Optional | Day it is paid. Left out when not known. |
dividends[].record_ | date | Optional | Day the holder must be on record. Left out when not known. |
next_ | string | Always | Pass as cursor for the next page; null on the last page. |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/XNAS:MSFT/dividends"{
"id": "XNAS:MSFT",
"dividends": [
{
"ex_date": "2026-08-20",
"amount": "0.91",
"currency": "USD",
"pay_date": "2026-09-10",
"record_date": "2026-08-20"
}
],
"next_cursor": null
}Splits of one stock
GET /v1/equities/{id}/splits
Share splits of one listing, newest first. The plan sets how far back the past goes: Free 12 months, Basic 5 years, Pro full. start is sent only when older rows were cut off; upcoming rows are never cut. Each request counts one call. An unknown id is 404 not_found; a market without data yet is 404 not_covered.
MCP: tool equities, action splits.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
id | path | Yes | string | Listing id: the exchange's four-letter code, a colon and the ticker, for example XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing; the answer carries the canonical id. Share classes use a dot (BRK.B); BRK-B is accepted. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
id | string | Always | Canonical listing id. |
start | date | Optional | First day the plan's history window reaches (Free 12 months, Basic 5 years, Pro full). Present only when older rows exist that the plan cannot see; upcoming rows are never cut. |
splits | list of object | Always | Newest first. |
splits[].date | date | Always | First day the shares trade split-adjusted. |
splits[].ratio | string | Always | New shares for old: 10:1 gives ten shares for each one held; 1:10 is a reverse split. |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/XNAS:NFLX/splits"{
"id": "XNAS:NFLX",
"splits": [
{
"date": "2025-11-17",
"ratio": "10:1"
}
]
}Earnings of one stock
GET /v1/equities/{id}/earnings
Past earnings reports of one listing (dated today or earlier at the exchange), with estimate, actual and surprise, newest first, paged. Upcoming report dates are not included yet. surprise_ is in percent. eps_ is on the market data basis and can differ from the filings EPS in the valuation. filing_ names the annual or quarterly report that covers a past report when we hold it. The plan sets how far back the past goes: Free 12 months, Basic 5 years, Pro full. start is sent only when older rows were cut off. Each request counts one call. An unknown id is 404 not_found; a market without data yet is 404 not_covered.
This route returns past reports only. Upcoming report dates are not included yet.
filing_id links a past report to the filing that covers it. Pass it to GET /v1/filings/{filing_id} in the Filings API.
MCP: tool equities, action earnings.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
id | path | Yes | string | Listing id: the exchange's four-letter code, a colon and the ticker, for example XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing; the answer carries the canonical id. Share classes use a dot (BRK.B); BRK-B is accepted. |
cursor | query | No | string | next_ of the previous page, with the same other parameters. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
id | string | Always | Canonical listing id. |
start | date | Optional | First day the plan's history window reaches (Free 12 months, Basic 5 years, Pro full). Present only when older rows exist that the plan cannot see. |
earnings | list of object | Always | Past reports only, newest first. Upcoming report dates are not included yet. |
earnings[].date | date | Always | Report day (today or earlier at the exchange). |
earnings[].time | one of before_open, after_close, during | Optional | When in the day it reports. Left out when not known. |
earnings[].eps_ | string | Optional | Analysts' earnings per share estimate. Left out when there is none. |
earnings[].eps_ | string | Optional | Reported earnings per share on the market data basis, which can differ from the filings EPS in eps_ of the valuation. Left out until it is reported. |
earnings[].surprise | string | Optional | eps_ minus eps_, rounded to 2 places. |
earnings[].surprise_ | string | Optional | surprise as a percent of the estimate (8.31 means 8.31%), 2 places. |
earnings[].filing_ | string | Optional | The annual, semiannual or quarterly report in our filings that covers this report: pass it to the filings GET /. Left out when the company has no filings match and when no report fits the date. |
next_ | string | Always | Pass as cursor for the next page; null on the last page. |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/XNAS:MSFT/earnings"{
"id": "XNAS:MSFT",
"earnings": [
{
"date": "2026-07-29",
"time": "after_close",
"eps_estimate": "3.31",
"eps_actual": "3.45",
"surprise": "0.14",
"surprise_percent": "4.23",
"filing_id": "usa:0000789019:0000950170-26-000123"
}
],
"next_cursor": null
}Market cap, P/E, P/S, P/B and dividend yield of one stock
GET /v1/equities/{id}/valuation
Market cap (latest price times shares outstanding), P/E (latest price over trailing 12-month diluted EPS from company filings, GAAP or IFRS as reported), P/S (market cap over trailing 12-month revenue), P/B (market cap over total equity of the latest balance sheet) and dividend yield (trailing 12-month dividends per share over the price, in percent) of one US or Korean listing, with the inputs and the dates they are as of. Korea has P/E only for now; market cap, P/S, P/B and yield come with share counts and dividends. A figure that cannot be worked out correctly is left out, never sent as 0: market cap after a split since the share count, for several share classes without a count each, and for a depositary receipt or foreign filer; P/E when EPS is negative or under 0.01, reported in another currency, or for a depositary receipt. Another class of a company, such as a preferred line, has no figures of its own and shows none. Basic and Pro only (Free gets 403 plan_upgrade_required). A listing outside the US and Korea, or without a clean company match, is 404 not_covered. One call; paid plans see live prices.
Each ratio comes with its inputs and the dates they are as of: shares_as_of, eps_period_end, revenue_period_end, equity_as_of and dividends_period_end. Paid plans see live prices.
MCP: tool equities, action valuation.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
id | path | Yes | string | Listing id: the exchange's four-letter code, a colon and the ticker, for example XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing; the answer carries the canonical id. Share classes use a dot (BRK.B); BRK-B is accepted. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
id | string | Always | Canonical listing id. |
currency | string | Always | Currency of market_ and of the price behind pe_. |
market_ | string | Optional | Latest price times shares_, in currency, whole units. Left out when a split happened after shares_, when the company has several share classes without a count for each, and for a depositary receipt or foreign filer. Korea has no market cap yet: its listings show P/E only for now. |
pe_ | string | Optional | Latest price divided by eps_, 2 places. Left out when eps_ is negative or under 0.01, when the reporting currency is not currency, when a split happened after eps_, and for a depositary receipt. |
ps_ | string | Optional | Price to sales: market_ divided by revenue_, 2 places. Left out whenever market_ is, when revenue is not above 0, and when it is reported in another currency than currency. |
pb_ | string | Optional | Price to book: market_ divided by total_, 2 places. Left out whenever market_ is, when total equity is 0 or negative, and when it is reported in another currency than currency. |
dividend_ | string | Optional | Dividends per share of the last 12 months over the latest price, in percent (0.71 means 0.71%), 2 places. Left out when no dividend is on file for those 12 months (a company that pays none included), when a split happened after dividends_, when the dividends are in another currency than currency, and for a depositary receipt. |
shares_ | string | Optional | Shares outstanding of the company's main common share class. Left out for a listing that is another class, such as a preferred line, which has no count here. |
shares_ | date | Optional | Date shares_ is counted at. Shown whenever the count is. |
eps_ | string | Optional | Trailing 12-month diluted EPS from company filings (GAAP or IFRS as reported); the fourth quarter is the annual figure minus the first three. Shown whenever it can be worked out; left out for a listing that is another class of the company. It can differ from the eps_ of stock earnings, which is on the market data basis. |
eps_ | date | Optional | End of the newest quarter in eps_. Shown whenever eps_ is. |
revenue_ | string | Optional | Revenue of the last 12 months from company filings, whole units of the reporting currency (the fourth quarter is the annual figure minus the first three). Left out when no four quarters or full year are on file and for a listing that is another class of the company. |
revenue_ | date | Optional | End of the newest quarter in revenue_. Shown whenever it is. |
total_ | string | Optional | Total equity of the latest balance sheet, including non-controlling interests, whole units of the reporting currency. Can be negative. |
equity_ | date | Optional | Balance sheet date of total_. Shown whenever it is. |
dividend_ | string | Optional | Dividends declared per share of the main common class over the last 12 months, in the reporting currency. Shown whenever it can be worked out. |
dividends_ | date | Optional | End of the newest quarter in dividend_. Shown whenever it is. |
company_ | string | Always | The company in our filings that the listing matches. |
timestamp | date-time | Always | When the price behind the answer was valid (UTC). |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/XNAS:MSFT/valuation"{
"id": "XNAS:MSFT",
"currency": "USD",
"market_cap": "3130000000000",
"pe_ratio": "33.50",
"ps_ratio": "11.40",
"pb_ratio": "10.20",
"dividend_yield": "0.86",
"shares_outstanding": "7430000000",
"shares_as_of": "2026-07-24",
"eps_ttm": "12.58",
"eps_period_end": "2026-06-30",
"revenue_ttm": "274500000000",
"revenue_period_end": "2026-06-30",
"total_equity": "307000000000",
"equity_as_of": "2026-06-30",
"dividend_per_share_ttm": "3.64",
"dividends_period_end": "2026-06-30",
"company_id": "usa:0000789019",
"timestamp": "2026-10-26T15:42:00Z"
}Beta, cost of equity, cost of debt and WACC of one stock
GET /v1/equities/{id}/cost-of-capital
Beta of one US or Korean stock against a benchmark ETF (weekly returns over two years by default, from prices adjusted for splits and dividends), the risk-free rate (the 10-year point of the country's government yield curve, or yours), the cost of equity (risk-free plus beta times the equity risk premium: yours in erp, or by default our own published total premium of the stock's country), the cost of debt (interest expense over average total debt from company filings, or, where that cannot be used, the risk-free rate plus a public table's default spread for the company's interest coverage; before and after tax, with the country's corporate tax rate from a public table unless you send tax_) and WACC, weighted by market cap and total debt. Every input is in the answer with its value, its basis and its date. Banks and insurers are 422 not_applicable; an input that cannot be worked out correctly is named in 422 inputs_unavailable, never repaired; a number that is shown but needs care comes with a warnings entry. Basic and Pro only (Free gets 403 plan_upgrade_required). One call.
erp is required. Korea has no yield curve here, so send risk_free for a Korean stock. Every input comes with its value and date, and warnings lists numbers to check before you rely on them.
MCP: tool equities, action cost_.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
id | path | Yes | string | Listing id: the exchange's four-letter code, a colon and the ticker, for example XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing; the answer carries the canonical id. Share classes use a dot (BRK.B); BRK-B is accepted. |
erp | query | No | string | Your equity risk premium, in percent (5 means 5%). Left out, it is our own published total equity risk premium of the stock's country (the answer names its date and basis). Where we publish none, without it the answer is 400 erp_required. Above 0 and at most 25. |
benchmark | query | No | string | Listing id of the market proxy beta is measured against: an ETF, fund or stock priced in the same currency as the stock. Left out, it is ARCX:SPY for a US stock, XKRX:069500 for a Korean one and XTKS:1306 for a Japanese one; any other market needs it. |
window | query | No | one of 2y, 5y | How far back the returns reach: 2y (default) or 5y. |
frequency | query | No | one of weekly, monthly | Return interval: weekly (default; the last common trading day of each ISO week) or monthly. |
beta | query | No | one of raw, adjusted | Which beta feeds the cost of equity: raw (default) or adjusted (0.67 raw plus 0.33). Both are always in the answer. |
risk_ | query | No | string | Your own risk-free rate, in percent. Left out, it is the 10-year point of the country's government yield curve. Needed where we have no curve (Korea): without it the answer is 422 risk_free_unavailable. |
tax_ | query | No | string | Your own tax rate for the debt shield, in percent. Left out, it is the corporate tax rate of the stock's country from Damodaran's public table (PwC rates). Between 0 and 60. |
period | query | No | one of ttm, annual | Period of interest expense: the last 12 months (ttm, default) or the latest full fiscal year (annual). Korea and Japan have annual figures only, so annual is what they get. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
id | string | Always | Canonical listing id. |
currency | string | Always | Currency of the money fields. |
company_ | string | Always | The company in our filings that the listing matches. |
beta | object | Always | |
beta.benchmark | object | Always | |
beta.benchmark.id | string | Always | Listing id of the benchmark. |
beta.benchmark.default | always true | Optional | Present, as true, when the benchmark is our default for the stock's market. |
beta.window | one of 2y, 5y | Always | |
beta.frequency | one of weekly, monthly | Always | |
beta.start | date | Always | Date of the first price behind the first return. |
beta.end | date | Always | Date of the last price behind the last return. |
beta.observations | integer | Always | Number of returns in the regression. |
beta.raw | string | Always | Slope of the stock's returns on the benchmark's, both from prices adjusted for splits and dividends. |
beta.adjusted | string | Always | 0.67 times raw plus 0.33. |
beta.used | one of raw, adjusted | Always | The beta in cost_. |
beta.r_ | string | Always | Share of the stock's variance explained. |
beta.std_ | string | Always | Standard error of raw. |
risk_ | object | Always | |
risk_ | string | Always | The risk-free rate, in percent (4.25 means 4.25%). |
risk_ | one of yield_curve, caller | Always | |
risk_ | one of ours, caller | Always | ours: the official government yield curve we publish. caller: yours. |
risk_ | string | Optional | Country of the yield curve. Not sent for caller. |
risk_ | always "10y" | Optional | Point of the curve. Not sent for caller. |
risk_ | string | Optional | How the curve is quoted (par, zero-coupon, Svensson, yield to maturity): they differ a little between countries. Not sent for caller. |
risk_ | date | Optional | Date of the curve. Not sent for caller. |
equity_ | object | Always | |
equity_ | string | Always | The equity risk premium, in percent (4.25 means 4.25%). |
equity_ | one of narwhal, caller | Always | Who set it: narwhal, our own published total equity risk premium of the stock's country, or caller, the erp you sent. |
equity_ | string | Optional | Country of our premium, ISO 3166-1 alpha-3. narwhal only. |
equity_ | date | Optional | Date our premium describes. narwhal only. |
equity_ | one of observed, modelled, table | Optional | How our total premium was built: observed from our own data, modelled by our spread model, table with the country part taken from a public table. narwhal only. |
cost_ | string | Always | Risk-free rate plus beta times equity risk premium, in percent (4.25 means 4.25%). |
cost_ | object | Optional | Left out for a company with no debt on file. |
cost_ | string | Always | Interest expense over average total debt, in percent (4.25 means 4.25%). Where that cannot be used (interest not positive, or a result under 0.1% or over 30%) and EBIT is on file: the risk-free rate plus the default spread of the company's interest coverage ratio from a public table; basis says which. |
cost_ | one of ours, table | Always | ours: interest expense over average debt from the company's filings. table: risk-free rate plus the table's default spread. |
cost_ | string | Optional | EBIT over interest expense. table only; left out when there is no interest. |
cost_ | string | Optional | Default spread of that coverage ratio, in percent (4.25 means 4.25%). table only. |
cost_ | string | Optional | Name of the spread table. table only. |
cost_ | string | Optional | The update of the table, for example January 2026. table only. |
cost_ | string | Optional | Where the table is published. table only. |
cost_ | string | Optional | pre_ times one minus the tax rate. Left out while no tax rate is known. |
cost_ | one of ttm, annual | Always | |
cost_ | date | Always | End of the newest quarter or year in the interest. |
cost_ | string | Always | Interest expense of the period, whole units of currency. |
cost_ | string | Always | Total debt at the start and at the end of the period, averaged; the end balance alone when the start is not on file. Whole units of currency; leases excluded. |
cost_ | string | Optional | Total debt a year before period_. Left out when it is not on file. |
cost_ | string | Always | Total debt at period_. |
tax_ | object | Optional | Left out when the country has no rate in the table. Send tax_ to set one. |
tax_ | string | Always | The tax rate on debt, in percent (4.25 means 4.25%). |
tax_ | one of table, caller | Always | table: the country's rate from a public table. caller: yours. |
tax_ | one of country_table, caller | Always | country_: the corporate tax rate of the stock's country from a public table. caller: the tax_ you sent. |
tax_ | string | Optional | ISO 3166-1 alpha-3 code of the country the rate is for. Table rate only. |
tax_ | string | Optional | Name of the table, with its publisher and source. Table rate only. |
tax_ | string | Optional | The update of the table the rate is from, for example January 2026. |
tax_ | string | Optional | Where the table is published. Table rate only. |
weights | object | Always | |
weights.equity | string | Always | Market cap over market cap plus debt, in percent (4.25 means 4.25%). |
weights.debt | string | Always | Debt over market cap plus debt, in percent (4.25 means 4.25%). |
weights.market_ | string | Always | Latest price times shares outstanding, whole units of currency. |
weights.market_ | date-time | Always | When the price behind market_ was valid. |
weights.total_ | string | Always | Latest total debt on file, whole units of currency; leases excluded. |
weights.total_ | date | Always | Balance sheet date of total_. |
wacc | string | Optional | Weighted average cost of capital, in percent (4.25 means 4.25%). Equals cost_ for a company with no debt. Left out while debt exists and no tax rate is known. |
warnings | list of object | Always | Things to check before using the numbers. Empty when there are none. |
warnings[].code | string | Always | Stable machine-readable code. |
warnings[].message | string | Always | What it means, in plain words. |
methodology_ | string | Always | How every figure is worked out. |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/XNAS:MSFT/cost-of-capital?erp=5"{
"id": "XNAS:MSFT",
"currency": "USD",
"company_id": "usa:0000789019",
"beta": {
"benchmark": {
"id": "ARCX:SPY",
"default": true
},
"window": "2y",
"frequency": "weekly",
"start": "2024-10-25",
"end": "2026-10-16",
"observations": 103,
"raw": "1.0842",
"adjusted": "1.0564",
"used": "raw",
"r_squared": "0.5731",
"std_error": "0.0894"
},
"risk_free": {
"rate": "4.1200",
"source": "yield_curve",
"basis": "ours",
"country": "USA",
"maturity": "10y",
"curve_type": "nominal_par",
"as_of": "2026-10-22"
},
"equity_risk_premium": {
"rate": "5.0000",
"source": "caller"
},
"cost_of_equity": "9.5410",
"cost_of_debt": {
"pre_tax": "3.9100",
"basis": "ours",
"after_tax": "2.9078",
"period": "ttm",
"period_end": "2026-06-30",
"interest_expense": "2900000000",
"average_debt": "74200000000",
"opening_debt": "72000000000",
"closing_debt": "76400000000"
},
"tax_rate": {
"rate": "25.6300",
"source": "country_table",
"basis": "table",
"country": "USA",
"table": "Damodaran, corporate marginal tax rates by country (PwC, January 2026 update)",
"table_update": "January 2026",
"table_url": "https://pages.stern.nyu.edu/~adamodar/New_Home_Page/datafile/countrytaxrates.html"
},
"weights": {
"equity": "98.0000",
"debt": "2.0000",
"market_cap": "3893412000000",
"market_cap_as_of": "2026-10-26T15:42:00Z",
"total_debt": "76400000000",
"total_debt_as_of": "2026-06-30"
},
"wacc": "9.4083",
"warnings": [],
"methodology_url": "https://narwhalapi.com/methodology/cost-of-capital"
}Valuation history of one stock
GET /v1/equities/{id}/valuation/history
Market cap, P/E, P/S, P/B and dividend yield at every fiscal period end (quarter or year end) of one US or Korean listing, oldest first. Each point is the closing price on or just before that day, adjusted for splits only, and the figures filed for that period (trailing 12-month EPS, revenue and dividends, total equity, the nearest share count), so a later restatement is in it. A figure that cannot be worked out is left out, never sent as 0, by the same rules as the valuation. History: Basic 5 years, Pro everything; start is sent back when the plan cut older periods. Basic and Pro only (Free gets 403 plan_upgrade_required). A listing outside the US and Korea, or without a clean company match, is 404 not_covered. One call.
MCP: tool equities, action valuation_.
Parameters
| Parameter | Where | Required | Type | Meaning |
|---|---|---|---|---|
id | path | Yes | string | Listing id: the exchange's four-letter code, a colon and the ticker, for example XNAS:MSFT or XNYS:BRK.B. A bare US ticker such as MSFT also works when it names one US listing; the answer carries the canonical id. Share classes use a dot (BRK.B); BRK-B is accepted. |
Response fields
| Field | Type | Sent | Meaning |
|---|---|---|---|
id | string | Always | Canonical listing id. |
currency | string | Always | Currency of every price and market cap. |
company_ | string | Always | The company in our filings that the listing matches. |
start | date | Optional | First day the plan's history window reaches (Basic 5 years, Pro full). Present only when older periods exist that the plan cannot see. |
points | list of object | Always | One point per fiscal period end of the company, oldest first. Each is worked out from the figures filed for that period and the price on that day, not from what was known then: a later restatement is in it. A period without a price, or without any figure, has no point. |
points[].date | date | Always | A fiscal period end of the company (quarter or year end): every figure in the point is as of this day. |
points[].price | string | Optional | Closing price on price_, in currency, adjusted for splits only (never for dividends), so it is on today's share basis. Left out, with every figure below, when the prices of that period could not be read right now: try again later. |
points[].price_ | date | Optional | Last trading day on or before date (at most 7 days earlier) the price is from. |
points[].market_ | string | Optional | price times the share count nearest to date (counted from 130 days before to 45 days after it), on today's share basis, whole units of currency. Left out when there is no such count, for several share classes without a count for each, and for a depositary receipt or foreign filer. |
points[].pe_ | string | Optional | price over diluted EPS of the four quarters ending on date, 2 places. Left out when EPS is negative or under 0.01, not reported in currency, or the four quarters (or the year) ending on date are not on file, and for a depositary receipt. |
points[].ps_ | string | Optional | market_ over revenue of the four quarters ending on date, 2 places. Left out whenever market_ is, or the revenue is not on file, not above 0 or not in currency. |
points[].pb_ | string | Optional | market_ over total equity of the balance sheet dated date, 2 places. Left out whenever market_ is, or the equity is not on file, not above 0 or not in currency. |
points[].dividend_ | string | Optional | Dividends per share of the four quarters ending on date over price, in percent (0.71 means 0.71%), 2 places. Left out when no dividend is on file for those quarters, they are not in currency, and for a depositary receipt. |
curl -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/equities/XNAS:MSFT/valuation/history"{
"id": "XNAS:MSFT",
"currency": "USD",
"company_id": "usa:0000789019",
"points": [
{
"date": "2026-06-30",
"price": "497.40",
"price_date": "2026-06-30",
"market_cap": "3697000000000",
"pe_ratio": "39.54",
"ps_ratio": "13.47",
"pb_ratio": "12.04",
"dividend_yield": "0.73"
}
]
}Errors
Errors use the shared problem shape with a request_. See Errors and quota for the shape and the quota headers. Failed requests are not charged.
| Code | Status | Meaning |
|---|---|---|
invalid_ | 400 | A parameter is missing, unknown, repeated or outside its range. |
invalid_ | 400 | An id or pair is not written the way this API expects. |
too_ | 400 | A batch holds more than 50 ids. |
invalid_ | 400 | The interval is not 1d or 1m, or not available for this instrument. |
range_ | 400 | The window is longer than the route allows. |
plan_ | 403 | The request needs a Basic or Pro plan. |
not_ | 404 | No such listing, fund or pair. |
not_ | 404 | The market, country or company is not covered yet. |
invalid_ | 400 | The cursor is not one this request returned, or it comes from a different request. |
cursor_ | 409 | The prices behind the cursor changed. Start again without the cursor. |
quota_ | 429 | Your plan has fewer calls left than the request needs. |
upstream_ | 503 | A price could not be fetched now. In a batch it is listed in errors. Try again shortly. |
area_ | 503 | This area cannot answer right now. |
erp_ | 400 | Send erp, your equity risk premium in percent. |
insufficient_ | 422 | The stock and its benchmark share fewer returns than 80% of the window. |
risk_ | 422 | No yield curve for this country. Send risk_free. |
not_ | 422 | WACC is not meaningful for a bank or an insurer. |
currency_ | 422 | The benchmark or the filings are in another currency than the price. |
inputs_ | 422 | An input cannot be worked out correctly. The detail names it. |
Docs