findata, free financial data via CDP
Free, keyless financial data scraped/fetched through a real browser via CDP, the same data the paid financialdatasets.ai API sells, but sourced directly from the free public origins (SEC EDGAR for statements, Yahoo Finance for prices). Each call opens its own tab and WebSocket session, safe for parallel use.
Core Principle
Free, keyless financial data scraped/fetched through a real browser via CDP, sourced directly from the free public origins (SEC EDGAR for statements, Yahoo Finance for prices), the same data the paid financialdatasets.ai API sells. Each call opens its own tab and WebSocket session, safe for parallel use.
When to Use / NOT
Use, stock price snapshots, historical OHLCV, or income / balance-sheet / cash-flow statements without an API key or account.
NOT, when per-quarter (non-cumulative) statement values are needed (findata returns as-reported YTD cumulative values; subtract consecutive periods yourself); when recently listed tickers must appear before a server restart (the ticker map is cached for the process lifetime).
Workflow
findata price <ticker>, live/last price snapshot with day-over-day change.
findata prices <ticker> [--range R | --start S --end E] [--interval R] [--limit N], historical OHLCV.
findata income|balance|cashflow <ticker> [--period annual|quarterly] [--limit N], statements.
- Add
--json for raw JSON instead of pretty tables; parallelize, each call uses its own tab.
Commands
findata price <ticker> # live/last price snapshot
findata prices <ticker> [--range 1mo|--start S --end E] [--interval 1d] [--limit N]
findata income <ticker> [--period annual|quarterly] [--limit N] # income statement
findata balance <ticker> [--period annual|quarterly] [--limit N] # balance sheet
findata cashflow <ticker> [--period annual|quarterly] [--limit N] # cash flow statement
findata --json <command> ... # raw JSON instead of pretty table
| Flag |
Applies to |
Meaning |
--json |
all |
Emit raw JSON instead of pretty-printed text |
--period annual|quarterly |
statements |
Reporting period (default annual) |
--limit N |
statements/prices |
Statements: periods to show (default 4, 0 = up to 40). Prices: most-recent N bars (default all) |
--range R |
prices |
Yahoo range: 1d 5d 1mo 3mo 6mo 1y 2y 5y 10y ytd max (default 1mo) |
--start/--end YYYY-MM-DD |
prices |
Exact date range (alternative to --range; needs node) |
--interval R |
prices |
Bar interval: 1m 2m 5m 15m 30m 60m 1d 5d 1wk 1mo (default 1d) |
Examples
findata price AAPL
findata prices TSLA --range 3mo --interval 1wk
findata prices MSFT --start 2025-01-01 --end 2025-06-30 --limit 5
findata income AAPL --period quarterly --limit 4 --json
findata balance NVDA --limit 2
findata cashflow AAPL
# Parallel — each call uses its own tab
findata price AAPL & findata price MSFT & wait
Result shapes
price (snapshot):
{"ticker":"AAPL","name":"Apple Inc.","price":310.05,"previous_close":308.63,
"change":1.42,"change_percent":0.46,"currency":"USD","exchange":"NMS",
"time":"2026-07-06T14:16:41Z"}
previous_close is the prior trading day's close (derived from the second-to-last daily bar), not the start-of-range close, so change is the true day-over-day move. price is the live/last trade (regularMarketPrice).
prices (historical OHLCV):
{"ticker":"AAPL","name":"Apple Inc.","currency":"USD","interval":"1d","prices":[
{"date":"2026-07-02","open":294.12,"high":309.42,"low":293.68,"close":308.63,"volume":75352800,"adj_close":308.63}
]}
income / balance / cashflow (statements):
{"ticker":"AAPL","name":"Apple Inc.","cik":320193,"period":"annual","statement":"income",
"statements":[{
"period_end":"2025-09-27","fiscal_year":2025,"fiscal_period":"FY","form":"10-K",
"filed":"2025-10-31","accession":"0000320193-25-000079",
"filing_url":"https://www.sec.gov/Archives/edgar/data/320193/000032019325000079/0000320193-25-000079-index.htm",
"revenue":416161000000,"cost_of_revenue":220960000000,"gross_profit":195201000000,
"operating_income":133050000000,"net_income":112010000000,
"eps_basic":7.49,"eps_diluted":7.46,"shares_basic":14948500000,"shares_diluted":15004697000
}]}
Each statement object carries period_end, fiscal_year, fiscal_period, form, filed, accession, filing_url for traceability back to the SEC filing, plus the line-item fields. cashflow adds a derived free_cash_flow = operating_cf + capex.
Statement fields
- income:
revenue, cost_of_revenue, gross_profit, operating_expenses, sgna, rnd, operating_income, interest_expense, income_tax, net_income, eps_basic, eps_diluted, shares_basic, shares_diluted, dividends_per_share
- balance:
total_assets, current_assets, cash, inventory, investments, total_liabilities, current_liabilities, long_term_debt, total_debt, equity, retained_earnings, shares_outstanding
- cashflow:
net_income, operating_cf, capex, investing_cf, financing_cf, dividends_paid, share_repurchases, debt_repayment, free_cash_flow
Each field maps to one or more candidate US-GAAP XBRL concepts (preferred order); the concept whose most recent period is latest wins, so a stale legacy concept (e.g. Revenues after a company switches to RevenueFromContractWithCustomerExcludingAssessedTax) never shadows the current one.
How it works
| Data |
Source |
CDP technique |
| Prices |
query1.finance.yahoo.com/v8/finance/chart/<ticker>?interval=&range=&period1=&period2= |
Event-driven fetch: navigate to the JSON URL, wait for the Page.frameNavigated commit (which does fire for application/json, unlike loadEventFired/networkIdle), then a same-origin fetch(window.location.href) returns the raw body without waiting for Chrome's JSON-viewer to render it into the DOM. The async projection IIFE runs with awaitPromise. Yahoo's chart endpoint returns clean OHLCV + regularMarketPrice as JSON, no DOM scraping. Going through the browser sidesteps the crumb/consent walls that block curl. |
| Ticker→CIK |
www.sec.gov/files/company_tickers.json |
Same event-driven fetch; parsed into a map cached on globalThis.__secTickerMap for the server's lifetime (one fetch per session). |
| Statements |
data.sec.gov/api/xbrl/companyfacts/CIK<padded>.json (SEC EDGAR XBRL) |
Readiness poll (not the fetch path, see below). Navigate to the JSON URL, then poll the in-page projection IIFE every ~80ms until it reports {ready:true}. Top-level navigation bypasses CORS; only the small projection crosses CDP, so the multi-MB companyfacts object stays in the page. |
Why two capture strategies. JSON navigations fire neither Page.loadEventFired nor networkIdle (verified), so the event-driven wait gsearch/xsearch use doesn't apply directly. Page.frameNavigated does fire on commit, and a same-origin fetch after it returns the raw body the instant it's downloaded, skipping the JSON-viewer render entirely. That's a clear win for small endpoints (prices ~0.3s, ticker map ~0.25s), where the viewer render is pure overhead with little parse to reuse.
For the multi-MB companyfacts, the opposite is true: by the time document.body.innerText is ready, Chrome's JSON viewer has already parsed the 3.7 MB body, so the projection's own JSON.parse is nearly free (16 ms). A cold fetch+parse costs ~800 ms there, so polling (total ~1.1s) beats the fetch path (1.5s). The clean way to skip the viewer render there would be Network.getResponseBody at loadingFinished (~0.56s), but that crashes the harness's WebSocket on multi-MB bodies, so polling is kept for companyfacts.
Per call: Target.createTarget(about:blank, background) → Target.attachToTarget → per-call sessionId → navigate + (fetch | poll) → fire-and-forget closeTab in finally. Same pattern as gsearch/xsearch, safe for parallel use, no activeSessionId clobbering.
Typical latency (warm server, cold HTTP cache): price / prices ~0.3s, statements ~1.1–1.4s (the companyfacts download + parse dominates; the first statement call per session adds ~0.25s for the one-time ticker-map fetch). Warm HTTP cache cuts prices to ~0.25s.
Why these sources
- SEC EDGAR for statements: it is the source of truth,
financialdatasets.ai's statements are derived from it (their JSON even carries the same SEC accession_number and filing_url). Free, keyless, structured JSON, both annual (10-K) and quarterly (10-Q). No reason to go anywhere else.
- Yahoo Finance v8 chart for prices: the only free source that returns OHLCV as structured JSON (not an HTML table) and covers snapshot + history + intraday in one endpoint.
curl is blocked by bot protection on every free price site; the browser bypasses it. (stockanalysis.com 403s curl and shows a Cloudflare "Just a moment…" wall even to a real browser; Yahoo's quote page is inconsistent due to EU consent redirects, but its JSON chart endpoint works cleanly through the browser.)
Red Flags
- Quarterly statements are YTD cumulative, not per-quarter. SEC 10-Q facts are reported year-to-date within the fiscal year (a Q3 figure is the 9-month cumulative).
findata returns as-reported values, it does not difference them into 3-month stubs. This matches the filing; if you need per-quarter, subtract consecutive periods yourself. Balance-sheet items are point-in-time (not cumulative), as expected.
- Cash-flow line-item signs are normalized. SEC filers are inconsistent: some report
capex/dividends_paid/share_repurchases/debt_repayment as positive magnitudes, some as negative. These four are always cash outflows, so findata normalizes them to negative. The net subtotals (operating_cf, investing_cf, financing_cf) keep their as-reported directional signs. free_cash_flow is derived as operating_cf + capex (with capex normalized negative), which equals operating_cf − |capex| regardless of the filer's convention.
period_end duplicate facts. companyfacts often contains the original 10-K fact and a comparative-column restatement filed in a later 10-K (same end, same value, the later one tagged with a frame like CY2024). findata dedupes periods by period_end and prefers the earliest-filed 10-K fact, so each column points back to the original filing, not a later comparative.
- Concept coverage varies by filer. A field shows
-/null if the company reports that line item under a concept not in the candidate list (e.g. Apple reports dividends under PaymentsOfDividends and repurchases under PaymentsForRepurchaseOfCommonStock). The candidate lists cover the common concepts; esoteric filers may leave some fields null.
previous_close for price is the close of the prior trading day (second-to-last daily bar of a 5-day window), so change is day-over-day. chartPreviousClose in Yahoo's meta is the close before the range start (~5 days ago), not yesterday, that's why it isn't used.
- Yahoo rate limits. Yahoo may return
429 / a non-JSON page under heavy use; findata surfaces this as a Yahoo Finance error … non-JSON response (possibly rate-limited). Back off and retry. Intraday intervals are limited by Yahoo to recent windows (e.g. 1-minute bars only for the last ~7 days).
- SEC rate limit / User-Agent. SEC asks for <10 req/s and a descriptive
User-Agent; the browser supplies one automatically. Normal use is 1–2 requests per call (ticker map is cached), well within limits.
- Ticker map is cached on
globalThis.__secTickerMap for the server process lifetime, so recently listed tickers won't appear until browser-harness-js --restart.
- Share-class tickers use a hyphen. SEC EDGAR's
company_tickers.json and Yahoo's chart endpoint both key share-class symbols with - (BRK-B, BF-B), not .. findata uppercases the ticker and normalizes .→-, so BRK.B, brk.b, and BRK-B all resolve. Without this, BRK.B failed with Ticker not found in SEC EDGAR.
- Two capture paths.
Page.frameNavigated + same-origin fetch for prices and the ticker map (skips the JSON-viewer render, fast for small bodies); readiness polling for the multi-MB companyfacts, where the viewer's pre-parse makes the projection nearly free (a cold fetch+parse is slower there). See How it works.
- No lifecycle events for JSON pages. Chrome emits neither
Page.loadEventFired nor networkIdle for application/json navigations. The fetch path keys off Page.frameNavigated (which does fire on commit); the poll path keys off document.body.innerText parseability, bailing fast on a non-JSON content-type instead of waiting out the timeout.
- No
jq dependency, JSON is parsed in-page and projected to the exact output shape; pretty-printing is done in JS. Date→unix conversion uses node (already required by browser-harness-js).
Verification
Each statement object carries period_end, fiscal_year, fiscal_period, form, filed, accession, and filing_url, trace any figure back to the SEC filing. price returns regularMarketPrice with a true day-over-day change. On a Yahoo rate-limit error, back off and retry.
References
N/A, no reference files; commands, result shapes, and traps are inline in this skill.
1---2name: findata3description: Use when the user asks for stock prices, price history, or financial statements without an API key. Keyless via CDP: Yahoo Finance price snapshots and OHLCV history plus SEC EDGAR XBRL statements. Returns structured JSON or tables. Requires browser-harness-js on PATH and a Chromium browser with remote debugging.4---56# findata, free financial data via CDP78Free, keyless financial data scraped/fetched through a real browser via CDP, the same data the paid `financialdatasets.ai` API sells, but sourced directly from the free public origins (SEC EDGAR for statements, Yahoo Finance for prices). Each call opens its own tab and WebSocket session, safe for parallel use.910## Core Principle1112Free, keyless financial data scraped/fetched through a real browser via CDP, sourced directly from the free public origins (SEC EDGAR for statements, Yahoo Finance for prices), the same data the paid `financialdatasets.ai` API sells. Each call opens its own tab and WebSocket session, safe for parallel use.1314## When to Use / NOT1516**Use**, stock price snapshots, historical OHLCV, or income / balance-sheet / cash-flow statements without an API key or account.1718**NOT**, when per-quarter (non-cumulative) statement values are needed (`findata` returns as-reported YTD cumulative values; subtract consecutive periods yourself); when recently listed tickers must appear before a server restart (the ticker map is cached for the process lifetime).1920## Workflow21221. `findata price <ticker>`, live/last price snapshot with day-over-day change.232. `findata prices <ticker> [--range R | --start S --end E] [--interval R] [--limit N]`, historical OHLCV.243. `findata income|balance|cashflow <ticker> [--period annual|quarterly] [--limit N]`, statements.254. Add `--json` for raw JSON instead of pretty tables; parallelize, each call uses its own tab.2627## Commands2829```bash30findata price <ticker> # live/last price snapshot31findata prices <ticker> [--range 1mo|--start S --end E] [--interval 1d] [--limit N]32findata income <ticker> [--period annual|quarterly] [--limit N] # income statement33findata balance <ticker> [--period annual|quarterly] [--limit N] # balance sheet34findata cashflow <ticker> [--period annual|quarterly] [--limit N] # cash flow statement35findata --json <command> ... # raw JSON instead of pretty table36```3738| Flag | Applies to | Meaning |39|------|-----------|---------|40| `--json` | all | Emit raw JSON instead of pretty-printed text |41| `--period annual\|quarterly` | statements | Reporting period (default `annual`) |42| `--limit N` | statements/prices | Statements: periods to show (default 4, 0 = up to 40). Prices: most-recent N bars (default all) |43| `--range R` | prices | Yahoo range: `1d 5d 1mo 3mo 6mo 1y 2y 5y 10y ytd max` (default `1mo`) |44| `--start`/`--end YYYY-MM-DD` | prices | Exact date range (alternative to `--range`; needs `node`) |45| `--interval R` | prices | Bar interval: `1m 2m 5m 15m 30m 60m 1d 5d 1wk 1mo` (default `1d`) |4647### Examples4849```bash50findata price AAPL51findata prices TSLA --range 3mo --interval 1wk52findata prices MSFT --start 2025-01-01 --end 2025-06-30 --limit 553findata income AAPL --period quarterly --limit 4 --json54findata balance NVDA --limit 255findata cashflow AAPL5657# Parallel — each call uses its own tab58findata price AAPL & findata price MSFT & wait59```6061## Result shapes6263**`price`** (snapshot):64```json65{"ticker":"AAPL","name":"Apple Inc.","price":310.05,"previous_close":308.63,66 "change":1.42,"change_percent":0.46,"currency":"USD","exchange":"NMS",67 "time":"2026-07-06T14:16:41Z"}68```69`previous_close` is the prior trading day's close (derived from the second-to-last daily bar), not the start-of-range close, so `change` is the true day-over-day move. `price` is the live/last trade (`regularMarketPrice`).7071**`prices`** (historical OHLCV):72```json73{"ticker":"AAPL","name":"Apple Inc.","currency":"USD","interval":"1d","prices":[74 {"date":"2026-07-02","open":294.12,"high":309.42,"low":293.68,"close":308.63,"volume":75352800,"adj_close":308.63}75]}76```7778**`income` / `balance` / `cashflow`** (statements):79```json80{"ticker":"AAPL","name":"Apple Inc.","cik":320193,"period":"annual","statement":"income",81 "statements":[{82 "period_end":"2025-09-27","fiscal_year":2025,"fiscal_period":"FY","form":"10-K",83 "filed":"2025-10-31","accession":"0000320193-25-000079",84 "filing_url":"https://www.sec.gov/Archives/edgar/data/320193/000032019325000079/0000320193-25-000079-index.htm",85 "revenue":416161000000,"cost_of_revenue":220960000000,"gross_profit":195201000000,86 "operating_income":133050000000,"net_income":112010000000,87 "eps_basic":7.49,"eps_diluted":7.46,"shares_basic":14948500000,"shares_diluted":1500469700088 }]}89```90Each statement object carries `period_end`, `fiscal_year`, `fiscal_period`, `form`, `filed`, `accession`, `filing_url` for traceability back to the SEC filing, plus the line-item fields. `cashflow` adds a derived `free_cash_flow = operating_cf + capex`.9192### Statement fields9394- **income**: `revenue, cost_of_revenue, gross_profit, operating_expenses, sgna, rnd, operating_income, interest_expense, income_tax, net_income, eps_basic, eps_diluted, shares_basic, shares_diluted, dividends_per_share`95- **balance**: `total_assets, current_assets, cash, inventory, investments, total_liabilities, current_liabilities, long_term_debt, total_debt, equity, retained_earnings, shares_outstanding`96- **cashflow**: `net_income, operating_cf, capex, investing_cf, financing_cf, dividends_paid, share_repurchases, debt_repayment, free_cash_flow`9798Each field maps to one or more candidate US-GAAP XBRL concepts (preferred order); the concept whose most recent period is latest wins, so a stale legacy concept (e.g. `Revenues` after a company switches to `RevenueFromContractWithCustomerExcludingAssessedTax`) never shadows the current one.99100## How it works101102| Data | Source | CDP technique |103|------|--------|----------------|104| Prices | `query1.finance.yahoo.com/v8/finance/chart/<ticker>?interval=&range=&period1=&period2=` | **Event-driven fetch**: navigate to the JSON URL, wait for the `Page.frameNavigated` commit (which *does* fire for `application/json`, unlike `loadEventFired`/`networkIdle`), then a same-origin `fetch(window.location.href)` returns the raw body without waiting for Chrome's JSON-viewer to render it into the DOM. The async projection IIFE runs with `awaitPromise`. Yahoo's chart endpoint returns clean OHLCV + `regularMarketPrice` as JSON, no DOM scraping. Going through the browser sidesteps the crumb/consent walls that block `curl`. |105| Ticker→CIK | `www.sec.gov/files/company_tickers.json` | Same event-driven fetch; parsed into a map cached on `globalThis.__secTickerMap` for the server's lifetime (one fetch per session). |106| Statements | `data.sec.gov/api/xbrl/companyfacts/CIK<padded>.json` (SEC EDGAR XBRL) | **Readiness poll** (not the fetch path, see below). Navigate to the JSON URL, then poll the in-page projection IIFE every ~80ms until it reports `{ready:true}`. Top-level navigation bypasses CORS; only the small projection crosses CDP, so the multi-MB companyfacts object stays in the page. |107108**Why two capture strategies.** JSON navigations fire *neither* `Page.loadEventFired` *nor* `networkIdle` (verified), so the event-driven wait `gsearch`/`xsearch` use doesn't apply directly. `Page.frameNavigated` *does* fire on commit, and a same-origin `fetch` after it returns the raw body the instant it's downloaded, skipping the JSON-viewer render entirely. That's a clear win for **small** endpoints (prices ~0.3s, ticker map ~0.25s), where the viewer render is pure overhead with little parse to reuse.109110For the **multi-MB companyfacts**, the opposite is true: by the time `document.body.innerText` is ready, Chrome's JSON viewer has *already parsed* the 3.7 MB body, so the projection's own `JSON.parse` is nearly free (~16 ms). A cold `fetch`+parse costs ~800 ms there, so polling (total ~1.1s) beats the fetch path (~1.5s). The clean way to skip the viewer render there would be `Network.getResponseBody` at `loadingFinished` (~0.56s), but that crashes the harness's WebSocket on multi-MB bodies, so polling is kept for companyfacts.111112Per call: `Target.createTarget(about:blank, background)` → `Target.attachToTarget` → per-call `sessionId` → navigate + (fetch | poll) → fire-and-forget `closeTab` in `finally`. Same pattern as `gsearch`/`xsearch`, safe for parallel use, no `activeSessionId` clobbering.113114**Typical latency** (warm server, cold HTTP cache): `price` / `prices` ~0.3s, statements ~1.1–1.4s (the companyfacts download + parse dominates; the first statement call per session adds ~0.25s for the one-time ticker-map fetch). Warm HTTP cache cuts prices to ~0.25s.115116## Why these sources117118- **SEC EDGAR for statements**: it is the *source of truth*, `financialdatasets.ai`'s statements are derived from it (their JSON even carries the same SEC `accession_number` and `filing_url`). Free, keyless, structured JSON, both annual (10-K) and quarterly (10-Q). No reason to go anywhere else.119- **Yahoo Finance v8 chart for prices**: the only free source that returns OHLCV as structured JSON (not an HTML table) and covers snapshot + history + intraday in one endpoint. `curl` is blocked by bot protection on every free price site; the browser bypasses it. (`stockanalysis.com` 403s `curl` and shows a Cloudflare "Just a moment…" wall even to a real browser; Yahoo's quote page is inconsistent due to EU consent redirects, but its JSON chart endpoint works cleanly through the browser.)120121## Red Flags122123- **Quarterly statements are YTD cumulative, not per-quarter.** SEC 10-Q facts are reported year-to-date within the fiscal year (a Q3 figure is the 9-month cumulative). `findata` returns **as-reported** values, it does not difference them into 3-month stubs. This matches the filing; if you need per-quarter, subtract consecutive periods yourself. Balance-sheet items are point-in-time (not cumulative), as expected.124- **Cash-flow line-item signs are normalized.** SEC filers are inconsistent: some report `capex`/`dividends_paid`/`share_repurchases`/`debt_repayment` as positive magnitudes, some as negative. These four are always cash outflows, so `findata` normalizes them to negative. The net subtotals (`operating_cf`, `investing_cf`, `financing_cf`) keep their as-reported directional signs. `free_cash_flow` is derived as `operating_cf + capex` (with `capex` normalized negative), which equals `operating_cf − |capex|` regardless of the filer's convention.125- **`period_end` duplicate facts.** companyfacts often contains the original 10-K fact *and* a comparative-column restatement filed in a later 10-K (same `end`, same value, the later one tagged with a `frame` like `CY2024`). `findata` dedupes periods by `period_end` and prefers the earliest-filed `10-K` fact, so each column points back to the original filing, not a later comparative.126- **Concept coverage varies by filer.** A field shows `-`/`null` if the company reports that line item under a concept not in the candidate list (e.g. Apple reports dividends under `PaymentsOfDividends` and repurchases under `PaymentsForRepurchaseOfCommonStock`). The candidate lists cover the common concepts; esoteric filers may leave some fields null.127- **`previous_close` for `price`** is the close of the prior trading day (second-to-last daily bar of a 5-day window), so `change` is day-over-day. `chartPreviousClose` in Yahoo's meta is the close *before the range start* (~5 days ago), not yesterday, that's why it isn't used.128- **Yahoo rate limits.** Yahoo may return `429` / a non-JSON page under heavy use; `findata` surfaces this as a `Yahoo Finance error … non-JSON response (possibly rate-limited)`. Back off and retry. Intraday intervals are limited by Yahoo to recent windows (e.g. 1-minute bars only for the last ~7 days).129- **SEC rate limit / User-Agent.** SEC asks for <10 req/s and a descriptive `User-Agent`; the browser supplies one automatically. Normal use is 1–2 requests per call (ticker map is cached), well within limits.130- **Ticker map is cached** on `globalThis.__secTickerMap` for the server process lifetime, so recently listed tickers won't appear until `browser-harness-js --restart`.131- **Share-class tickers use a hyphen.** SEC EDGAR's `company_tickers.json` and Yahoo's chart endpoint both key share-class symbols with `-` (`BRK-B`, `BF-B`), not `.`. `findata` uppercases the ticker and normalizes `.`→`-`, so `BRK.B`, `brk.b`, and `BRK-B` all resolve. Without this, `BRK.B` failed with `Ticker not found in SEC EDGAR`.132- **Two capture paths.** `Page.frameNavigated` + same-origin `fetch` for prices and the ticker map (skips the JSON-viewer render, fast for small bodies); readiness polling for the multi-MB companyfacts, where the viewer's pre-parse makes the projection nearly free (a cold fetch+parse is slower there). See *How it works*.133- **No lifecycle events for JSON pages.** Chrome emits neither `Page.loadEventFired` nor `networkIdle` for `application/json` navigations. The fetch path keys off `Page.frameNavigated` (which does fire on commit); the poll path keys off `document.body.innerText` parseability, bailing fast on a non-JSON `content-type` instead of waiting out the timeout.134- **No `jq` dependency**, JSON is parsed in-page and projected to the exact output shape; pretty-printing is done in JS. Date→unix conversion uses `node` (already required by `browser-harness-js`).135136## Verification137138Each statement object carries `period_end`, `fiscal_year`, `fiscal_period`, `form`, `filed`, `accession`, and `filing_url`, trace any figure back to the SEC filing. `price` returns `regularMarketPrice` with a true day-over-day `change`. On a Yahoo rate-limit error, back off and retry.139140141## References142143N/A, no reference files; commands, result shapes, and traps are inline in this skill.