Stock Analyst Ratings - Upgrades, Downgrades, Targets avatar

Stock Analyst Ratings - Upgrades, Downgrades, Targets

Pricing

from $0.40 / 1,000 results

Go to Apify Store
Stock Analyst Ratings - Upgrades, Downgrades, Targets

Stock Analyst Ratings - Upgrades, Downgrades, Targets

Daily analyst rating changes across US, UK, and Canadian markets: upgrades, downgrades, price targets, initiations, reiterations. Also per-ticker history with consensus snapshot and per-brokerage feeds with 12-month ROI. Built for AI agents, MCP pipelines, and portfolio monitoring.

Pricing

from $0.40 / 1,000 results

Rating

0.0

(0)

Developer

Michal Búci

Michal Búci

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

3 days ago

Last modified

Share

Stock Analyst Ratings Scraper - Upgrades, Downgrades & Price Targets

Get daily analyst rating changes for the US, UK, and Canadian markets in a single call. Upgrades, downgrades, initiations, reiterations, and price target moves — all in one structured dataset. Also supports per-ticker rating history with a consensus snapshot and per-brokerage feeds with 12-month ROI. Built for AI agents, MCP pipelines, and portfolio monitoring.

What does this actor do?

This actor extracts analyst rating activity from MarketBeat and returns it as clean JSON. Three modes, one unified schema:

ModeHow to triggerTypical output
Daily market feedDefault (no tickers/brokerages)~400 rows/day across US+UK+Canada
Ticker historySet tickersLast ~15 dated ratings per ticker + consensus snapshot
Brokerage feedSet brokerages~100 dated ratings per firm with 12-month ROI

Unlike per-ticker lookup tools, the daily feed gives you a market-wide view of every analyst action — no need to query ticker by ticker.

Why scrape analyst ratings from MarketBeat?

Analyst upgrades and downgrades move markets. Getting them structured and timely is hard: most sources only offer per-ticker views, paywalled APIs, or stale RSS. MarketBeat aggregates actions from 150+ research firms covering US, UK, and Canadian stocks and exposes them in plain HTML. This actor turns that into structured JSON, ready for LLMs, trading systems, dashboards, or alerting pipelines.

How to scrape analyst ratings

  1. Open the Stock Analyst Ratings Scraper actor page
  2. Pick a mode: leave everything blank for the daily feed, add tickers for per-ticker history, or add brokerages for per-firm feeds
  3. Click Start
  4. Download results as JSON, CSV, or Excel, or read them via the Apify API or MCP

Input

ParameterTypeDefaultDescription
tickersstringComma-separated ticker symbols (e.g. AAPL, TSLA, NVDA). Accepts EXCHANGE:TICKER to skip exchange lookup (NASDAQ:AAPL). Switches to ticker history mode.
brokeragesstringComma-separated brokerage names. Fuzzy-matched against ~150 tracked firms (JPMorgan, Goldman Sachs, Citi). Switches to brokerage mode.
marketstringallFilter the daily feed: all, us (NYSE/NASDAQ/AMEX), uk (LON), ca (TSE). Ignored when tickers/brokerages are set.
actionsarray[]Keep only these normalized actions: upgrade, downgrade, initiate, reiterate, target_raised, target_lowered, target_set. Empty = keep all.
daysBackinteger0Keep only ratings within the last N days (ticker/brokerage modes only, daily feed is always today). 0 = no filter.
includeConsensusbooleantrueIn ticker mode, attach consensus snapshot (rating, target, breakdown, 1m/3m/1y history) to each row. No extra requests.
maxItemsinteger0Hard cap on rows. 0 = unlimited.

Example inputs

Daily feed, US-only upgrades and downgrades:

{ "market": "us", "actions": ["upgrade", "downgrade"] }

Ticker history for a portfolio, with consensus:

{ "tickers": "AAPL, TSLA, NVDA, MSFT", "includeConsensus": true }

Recent JPMorgan and Goldman calls:

{ "brokerages": "JPMorgan, Goldman Sachs", "daysBack": 30 }

Output

Every row, regardless of mode, uses this schema. Null fields are omitted from each row (only populated fields are included).

{
"ticker": "ABNB",
"exchange": "NASDAQ",
"company": "Airbnb",
"action": "upgrade",
"brokerage": "Wells Fargo & Company",
"analyst": "Ken Gawrelski",
"priorRating": "Equal Weight",
"newRating": "Overweight",
"priorTarget": 136.0,
"newTarget": 178.0,
"targetCurrency": "USD",
"currentPrice": 144.18,
"stockPriceChangePct": 1.1,
"upsideToTargetPct": 23.45,
"ratingId": 2386270,
"sourceUrl": "https://www.marketbeat.com/ratings/"
}
FieldTypeDescription
tickerstringTicker symbol
exchangestringNYSE, NASDAQ, NYSEAMERICAN, LON, or TSE
companystringCompany name
actionstringNormalized enum: upgrade, downgrade, initiate, reiterate, target_raised, target_lowered, target_set
brokeragestringResearch firm / investment bank
analyststring/nullIndividual analyst. null when gated behind MarketBeat's paywall.
priorRatingstring/nullRating before this action
newRatingstring/nullRating after this action
priorTargetnumber/nullPrevious price target in quote currency
newTargetnumber/nullNew price target in quote currency
targetCurrencystring/nullUSD, GBX (British pence), CAD, etc.
currentPricenumberStock price at scrape time (daily + brokerage modes)
stockPriceChangePctnumberIntraday percent change of the underlying stock price
upsideToTargetPctnumberPercent upside from current stock price to the new price target
ratingDatestringISO YYYY-MM-DD date the rating was issued (ticker + brokerage modes only)
recommendationRoi12monumber12-month ROI on this specific recommendation (brokerage mode only)
ratingIdintegerMarketBeat's stable internal ID for this rating. Use as dedup key.
sourceUrlstringCanonical MarketBeat URL the row was scraped from
consensusobjectConsensus snapshot with 1m/3m/1y history (ticker mode with includeConsensus)

Consensus object (ticker mode)

{
"rating": "Moderate Buy",
"priceTarget": 303.06,
"upsideToTargetPct": 10.94,
"breakdown": { "strongBuy": 1, "buy": 22, "hold": 12, "sell": 1 },
"history": {
"current": { "target": 303.06, "rating": "Moderate Buy" },
"oneMonthAgo": { "target": 297.58, "rating": "Moderate Buy" },
"threeMonthsAgo": { "target": 281.70, "rating": "Moderate Buy" },
"oneYearAgo": { "target": 236.97, "rating": "Moderate Buy" }
}
}

Useful for detecting whether analysts are getting more bullish or bearish on a name over time — one request, four data points.

Use cases

Use caseDescriptionBest for
Daily rating digestPull the full market feed on a schedule for downstream filtering and alertingPortfolio managers, newsletters
Portfolio news monitorRun in ticker mode each day for a watchlist and flag consensus driftRetail investors, AI portfolio agents
Earnings-week monitoringFilter for upgrade/downgrade in the days after earningsEvent-driven strategies
LLM-fed researchFeed the daily feed into an MCP-enabled agent to summarize market sentimentChatGPT, Claude, custom agents
Brokerage trackingPull a specific firm's calls with their 12-month ROI to evaluate analyst qualityQuants, research desks
AlertingFilter downgrade or large target_lowered moves on a portfolioRisk-aware traders
Historical datasetRun daily and store; builds a clean analyst ratings databaseData science / backtest research

How to use with Python

from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("michael_b/stock-analyst-ratings").call(run_input={
"market": "us",
"actions": ["upgrade", "downgrade"],
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(f"[{item['ticker']}] {item['brokerage']}: {item['priorRating']} -> {item['newRating']}")

How to use with JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('michael_b/stock-analyst-ratings').call({
tickers: 'AAPL, TSLA, NVDA',
includeConsensus: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach(r => console.log(`${r.ticker}: ${r.brokerage} -> ${r.newRating} @ ${r.newTarget}`));

How much does it cost?

Raw HTTP requests with no browser keep costs minimal. Recommended memory: 512 MB.

ScenarioHTTP requestsOutput rowsTypical runtime
Daily feed1~400a few seconds
10 tickers + consensus10~150 + consensus10-15 seconds
5 brokerages6 (+1 index)~50010-15 seconds

Automate with Apify

  • Schedule daily for a rolling ratings archive
  • Use with MCP — fully compatible with Apify's MCP server for Claude, ChatGPT, and custom agents
  • Hook into Make, n8n, or Zapier for no-code alerting
  • Export as JSON, CSV, Excel, or stream to your database via the Apify API

FAQ

Is it legal to scrape MarketBeat? This actor only accesses publicly available pages. No private data is read, no authentication is bypassed, and every row links back to the source URL on marketbeat.com.

How often is the data updated? Live on every run. MarketBeat updates the main feed continuously through the trading day.

Why is analyst sometimes null? MarketBeat gates individual analyst names behind their All Access subscription on the main feed. Ticker history and brokerage pages tend to include analysts more consistently.

Why is ratingDate null on the daily feed? The main feed table doesn't show per-row dates, every row on it is from today. If you need exact timestamps, use ticker or brokerage mode.

How do I dedup across runs? Use ratingId. It's MarketBeat's stable internal identifier for each rating and won't collide between runs.

Why doesn't ticker mode show a current price? The per-ticker forecast page doesn't include intraday quote data. The daily feed does. Combine modes if you need both.

How are brokerage names matched? Fuzzy match against MarketBeat's directory (/ratings/by-issuer/). JPMorgan, JP Morgan, and J.P. Morgan all resolve. If no match is found, the actor logs a warning and skips that name.

Is Canada supported? Partially. The main feed includes Toronto (TSE) rows. Filter with market: "ca". MarketBeat's dedicated Canadian endpoint redirects back to the main feed — we handle that automatically.

Support

Questions, feature requests, or bugs? Open an issue in the Issues tab.