Yahoo Finance Stock News Search
Purpose
Given a stock ticker symbol (e.g. AAPL, TSLA, ^GSPC), return the latest news headlines listed on Yahoo Finance's per-ticker news tab. Each item carries title, article URL, publisher, relative age, content type (story / video / press / ad), and the tickers + percent changes annotated in the stream. Read-only — never follows article links, never submits forms beyond the search box, never interacts with sponsored content.
When to Use
- "What's the latest on $TICKER?" / "Show me news for Apple."
- Periodic monitoring of headlines for a watchlist of tickers.
- Pre-trade sentiment lookup before a price decision is taken elsewhere.
- Anywhere you would otherwise hand-navigate Yahoo Finance's quote → news tab.
Workflow
The fastest and most reliable path is a direct deep-link to the per-ticker news page — https://finance.yahoo.com/quote/{TICKER}/news/. This skips the search box, the typeahead, and the [title="News"] click entirely. The UI-traversal path (search form #ybar-sf → quote page → click [title="News"]) works and is documented under Browser fallback below, but it costs ~3 extra turns and depends on the search-box typeahead intercepting Enter correctly (an undocumented behavior — see gotchas).
-
Resolve the ticker. Accept the ticker exactly as given. Yahoo's ticker space includes
.-suffixed regional listings (AAPL.B),^-prefixed indices (^GSPC),=Xcurrencies (EURUSD=X),-USDcryptos (BTC-USD), and=Ffutures (CL=F). Do NOT uppercase or normalize — pass through verbatim. -
Open the news page directly.
browse open "https://finance.yahoo.com/quote/${TICKER}/news/" --remote --session "$SID"- 200 OK + News page → proceed. Page title matches
^${TICKER_NAME} \(${TICKER}\) Latest Stock News.*Yahoo Finance$. - Redirect to
/lookup/?s=${TICKER}→ ticker not found. The page renders "No results for '${TICKER}'" inside.mainContent. Emit{ "success": false, "reason": "ticker_not_found", "ticker": "${TICKER}" }and stop. Detect vialocation.href.includes('/lookup/')after navigation.
- 200 OK + News page → proceed. Page title matches
-
Wait for the stream to render. The headline list inside
.mainContentis server-rendered for the first ~25 items, but story-tile metadata (publisher, age, related-ticker chips) fills in progressively. Wait 2–3 seconds after navigation, or untildocument.querySelectorAll('.mainContent a[data-ylk*="elm:hdln"]').length >= 20. -
(Optional) Filter to news-only. The default
Alltab inside.mainContentincludes sponsored slots (ct:ad) interleaved with stories. To filter to editorial news only, click the[title="News"]filter tab on the news page (it's an<a class="tabBtn">withdata-ylkcontainingsubsec:news). This is a different[title="News"]element from the one in the quote-nav (see gotchas). Alternative tabs:[title="Earnings Calls"],[title="Press Releases"],[title="SEC Filings"]. Skipping this step and filtering client-side byct === "story"is equivalent and saves a click. -
Extract headlines from
.mainContent. Each story tile contains two anchors with the same href — one wrapping the thumbnail image (empty text), one wrapping the headline text. Dedupe by href, keep the text-bearing anchor:const mc = document.querySelector('.mainContent'); const headlines = Array.from(mc.querySelectorAll('a[data-ylk*="elm:hdln"]')); const seen = new Set(); const items = []; for (const a of headlines) { if (seen.has(a.href) || !a.textContent.trim()) continue; seen.add(a.href); const dy = a.getAttribute('data-ylk') || ''; const ct = (dy.match(/ct:(\w+)/) || [])[1] || null; // story | video | ad const container = a.closest('section, li, article, div'); const m = (container?.innerText || '').match(/\n([^\n]+)\n•\n([^\n]+)/); items.push({ title: a.textContent.trim(), url: a.href, ct, publisher: m?.[1] || null, age: m?.[2] || null, }); }The
data-ylkattribute is Yahoo's per-element tracking payload;elm:hdlnis the stable, site-wide marker for "this anchor is a headline link" and survives the various visual layouts (story tile, video tile, press release). -
(Optional) Paginate via scroll. The page lazy-loads on scroll. Each
window.scrollTo(0, document.body.scrollHeight)followed by a 2-second wait fetches the next ~20 headlines. Loop until the headline count stops growing or you hit a desired N. Verified iter-1: 25 → 45 after one scroll on AAPL. -
Skim the AI summary (optional, top-of-stream). At the top of
.mainContent, a "News headlines" block under heading text "Updated Xm ago · Powered by Yahoo Scout" contains a 2–3-sentence machine-generated synopsis of recent coverage. Useful as a one-line answer when the user asks for a summary, not a list. Text content can be read viamc.querySelector('section[data-testid*="summary"], section:has(> div:contains("Yahoo Scout"))')?.innerText— selector varies; safer is to grab the first non-nav text block in.mainContent.
Browser fallback — via the user-requested UI flow
Use this path when the direct deep-link is blocked or when validating the searchbox flow itself.
- Open
https://finance.yahoo.com/. - Fill the search box inside
form#ybar-sf— the input isinput#ybar-sbq(namep):browse fill --remote --session "$SID" "#ybar-sbq" "${TICKER}" - Wait ~1 second for the typeahead
ul[role="listbox"]to render. Confirm the topli[role="option"]text begins with${TICKER}followed by the company name and the tagequity/etf/index. If the top option does not match (e.g. user passed a partial / wrong ticker), abort. - Press Enter — the typeahead intercepts the form submission and navigates to
https://finance.yahoo.com/quote/${TICKER}/, not the form's stated action of/lookup/. This is undocumented behavior and is what makes the searchbox flow work as a ticker shortcut. (If you instead submit the form bypassing the typeahead — e.g. by POSTing to the action URL — you land on the lookup results page, which is wrong.)browse press --remote --session "$SID" Enter - On the quote page, locate
[title="News"]— there are two matches: an<a class="item yf-8g9x85" href=".../news/">in the quote-nav bar (this is the one to click) and a<button class="tabBtn l1 yf-zzox3x">in some headers that is a non-navigating UI duplicate. Click the anchor specifically:browse eval --remote --session "$SID" 'document.querySelector("a[title=\"News\"]").click()' - Wait 2–3 seconds, then continue from step 3 of the optimal workflow above (extract from
.mainContent).
Site-Specific Gotchas
- Two
[title="News"]elements coexist on the quote overview page. The<a class="item yf-8g9x85">is the navigation link (goes to/quote/${TICKER}/news/). The<button class="tabBtn l1 yf-zzox3x">next to it is a visual duplicate that does not navigate. A naïvedocument.querySelector('[title="News"]')picks up the anchor first, which works — but[title="News"]is not a unique selector. Prefera[title="News"]to be explicit. On the news page itself,[title="News"]is a third element — the filter sub-tab — which switches the stream fromAllto news-only. - Typeahead hijacks the search form's submit.
form#ybar-sfdeclaresaction="https://finance.yahoo.com/lookup/"and would normally GET/lookup/?p=${TICKER}. But when the typeaheadul[role="listbox"]is open and its top option is an equity match, pressing Enter triggers the typeahead'soption.click()handler instead of the form submit, navigating to/quote/${TICKER}/. If the typeahead has not rendered yet (the box value was set without a synthetic input event, or you submitted within ~200ms of filling), Enter falls through to the form and you land on/lookup/?p=${TICKER}— a different page with no.mainContentnews stream. Always wait for the listbox before pressing Enter, or click the firstli[role="option"]explicitly. - Invalid ticker → silent redirect to
/lookup/?s=${TICKER}. Yahoo does not return 404 for unknown tickers on the/quote/...path. The URL silently rewrites to the lookup search results, which renders inside the same.mainContentcontainer but with the textSymbols similar to '${TICKER}' ... No results for '${TICKER}'. Always checklocation.hrefafter navigation; do not assume the request URL is the rendered URL. - Ticker casing and special characters matter.
aapl(lowercase) → 301 to uppercase URL, fine.BRK.B→ works.BRK-B→ silent rewrite to lookup.^GSPC,=X,=F,-USDsuffixes are preserved verbatim by/quote/. URL-encode^as%5Efor safety inside templated URLs. .mainContentis shared across the entire/quote/${TICKER}/*route. It contains the quote header, sticky price ticker, ad slots, and the news stream. Do NOT grabmc.innerTextand treat it all as news — the first ~10 lines are nav tabs ("Summary | News | Research | Chart | …") and the price block. Scope queries tomc a[data-ylk*="elm:hdln"]instead.- Each story tile renders the same anchor twice (image + headline). Without deduping by
href, every headline appears as one empty-text entry plus one text entry. Dedupe by href and drop emptytextContent.trim(). data-ylkis the stable extraction surface, not CSS classes. Yahoo ships utility classes likeyf-8g9x85,yf-ew9gqf,yf-zzox3xthat rotate with every redeploy. Stable hooks:data-ylk*="elm:hdln"(headline link),data-ylk*="ct:story"/ct:video/ct:ad(content type),data-ylk*="elm:tab"(filter tab),data-testid="quote-nav-bar"/quote-sticky-hdr/quote-hdr. Build selectors on these, not on the hashed class names.- Stream is lazy-loaded on scroll. Initial render gives ~25 headlines; subsequent scrolls fetch ~20 more each. There is no "Load more" button —
window.scrollTo(0, document.body.scrollHeight)is the only trigger. After ~5 scrolls the stream tapers off (~100 headlines is the practical ceiling per ticker). - Sponsored slots (
ct:ad) are interleaved with editorial. Filter them out client-side viact !== "ad", or click the[title="News"]sub-tab to get the editorial-only feed. Press releases live under[title="Press Releases"], earnings transcripts under[title="Earnings Calls"], and SEC filings under[title="SEC Filings"](this last one is actually a dropdown, not a tab —data-ylkcontainselm:dropdown). - No anti-bot wall observed. Iter-1 ran with
--verified --proxiesand saw clean 200s end-to-end on/,/quote/AAPL/,/quote/AAPL/news/,/quote/TSLA/news/,/lookup/?s=XYZNOTREAL. No Akamai 403s, no GDPR consent banner (US IP), no rate-limit responses. Stealth is not required but was used as a safe default; a bare session is likely fine for low-volume use. Cookies: ~10 set on first load (Yahoo session, A1/A3 trackers); not required for content access. form#ybar-sfis the unified Yahoo search box — same form/id on news.yahoo.com, sports.yahoo.com, etc. Onfinance.yahoo.com, the typeahead is finance-scoped (equities, ETFs, indices, futures, currencies, crypto). On other Yahoo properties, the same form id produces a generic web/news typeahead, which will not route to a quote page. Always start fromfinance.yahoo.com/.- AI summary block ("Powered by Yahoo Scout") is opt-in machine output. It appears above the headline list, paraphrases the top stories, and is regenerated every few minutes. Useful for
summarymode but should never be quoted verbatim as if it were a primary source — emit it under a distinct field (ai_summary) and never confuse it with a headline.
Expected Output
Three distinct outcome shapes:
// 1. Headlines returned (most common)
{
"success": true,
"ticker": "AAPL",
"company_name": "Apple Inc.",
"page_url": "https://finance.yahoo.com/quote/AAPL/news/",
"ai_summary": "Recent developments indicate Apple is strategically enhancing its market position through premium content offerings and competitive analysis in tech sectors. The company's engagement in healthcare APIs and application processors highlights its focus on growth in emerging tech markets.",
"headlines": [
{
"title": "Analyze Apple stock on Yahoo Finance's new AI platform AlphaSpace",
"url": "https://finance.yahoo.com/video/analyze-apple-stock-on-yahoo-finances-new-ai-platform-alphaspace-194037185.html",
"ct": "video",
"publisher": "Yahoo Finance Video",
"age": "19h ago",
"related_tickers": ["AAPL"]
},
{
"title": "150 Years of Market History Predicts Trump's Bull Market Is Almost Over",
"url": "https://finance.yahoo.com/markets/stocks/articles/150-years-market-history-predicts-145544565.html",
"ct": "story",
"publisher": "24/7 Wall St.",
"age": "21m ago",
"related_tickers": ["NVDA", "^GSPC", "MSFT"]
},
{
"title": "Healthcare API Strategic Business Research Report 2026, Competitive Analysis of Apple, Athenahealth, eClinical Works, Google, Greenway Health, Microsoft, Oracle, Postman, Practice Fusion, Salesforce",
"url": "https://finance.yahoo.com/sectors/healthcare/articles/healthcare-api-strategic-business-research-131500629.html",
"ct": "story",
"publisher": "GlobeNewswire",
"age": "2h ago",
"related_tickers": ["AAPL", "GOOG", "MSFT"]
}
],
"headline_count": 25,
"filter_applied": "all"
}
// 2. Ticker not found (silently redirected to /lookup/)
{
"success": false,
"reason": "ticker_not_found",
"ticker": "XYZNOTREAL",
"redirected_to": "https://finance.yahoo.com/lookup/?s=XYZNOTREAL",
"message": "No results for 'XYZNOTREAL'"
}
// 3. Page reached but news stream empty (rare — newly-listed ticker or thin-coverage symbol)
{
"success": true,
"ticker": "BRK.B",
"company_name": "Berkshire Hathaway Inc.",
"page_url": "https://finance.yahoo.com/quote/BRK.B/news/",
"ai_summary": null,
"headlines": [],
"headline_count": 0,
"filter_applied": "all",
"note": "Page rendered with zero headlines in .mainContent — distinct from ticker_not_found."
}