Financial news sentiment is a leading indicator for stock price movements. This tutorial builds a pipeline that searches for news about specific stocks using the Scavio API, performs keyword-based sentiment analysis on the snippets, and generates a sentiment score that correlates with market direction. The pipeline processes multiple tickers in parallel, stores historical sentiment data, and identifies divergences between sentiment and price. Each ticker costs $0.005 to analyze.
Prerequisites
- Python 3.9+ installed
- requests library installed
- A Scavio API key from scavio.dev
- A list of stock tickers to monitor
Walkthrough
Step 1: Search for financial news per ticker
For each stock ticker, search for recent news and extract headline sentiment signals from titles and snippets.
import os, requests, json, time, re
from datetime import datetime
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
URL = 'https://api.scavio.dev/api/v1/search'
TICKERS = ['AAPL', 'NVDA', 'TSLA', 'MSFT', 'GOOGL']
def get_stock_news(ticker: str, num: int = 10) -> list:
resp = requests.post(URL, headers=H,
json={'query': f'{ticker} stock news', 'country_code': 'us', 'num_results': num})
return [{'title': r['title'], 'snippet': r.get('snippet', ''), 'url': r['link']}
for r in resp.json().get('organic_results', [])]
news = get_stock_news('NVDA')
print(f'NVDA news: {len(news)} articles')
for n in news[:3]:
print(f' {n["title"][:60]}')Step 2: Analyze sentiment from news snippets
Score each article's sentiment using keyword matching. Aggregate scores per ticker to produce an overall sentiment signal.
BULLISH_WORDS = ['surge', 'soar', 'beat', 'record', 'growth', 'strong', 'upgrade',
'outperform', 'buy', 'bullish', 'rally', 'gains', 'profit', 'revenue up']
BEARISH_WORDS = ['drop', 'fall', 'miss', 'decline', 'weak', 'downgrade', 'sell',
'bearish', 'crash', 'loss', 'layoff', 'warning', 'revenue down', 'lawsuit']
def analyze_sentiment(articles: list) -> dict:
scores = []
for article in articles:
text = f"{article['title']} {article['snippet']}".lower()
bull = sum(1 for w in BULLISH_WORDS if w in text)
bear = sum(1 for w in BEARISH_WORDS if w in text)
score = (bull - bear) / max(bull + bear, 1)
scores.append({'title': article['title'][:50], 'bull': bull, 'bear': bear, 'score': score})
avg_score = sum(s['score'] for s in scores) / len(scores) if scores else 0
signal = 'BULLISH' if avg_score > 0.2 else 'BEARISH' if avg_score < -0.2 else 'NEUTRAL'
return {
'avg_score': round(avg_score, 3),
'signal': signal,
'articles_analyzed': len(scores),
'bullish_articles': sum(1 for s in scores if s['score'] > 0),
'bearish_articles': sum(1 for s in scores if s['score'] < 0),
'details': scores[:5],
}
sentiment = analyze_sentiment(news)
print(f'NVDA Sentiment: {sentiment["signal"]} (score: {sentiment["avg_score"]})')Step 3: Run the full sentiment pipeline across tickers
Process all tickers and generate a market sentiment dashboard. Save results for historical tracking.
def sentiment_pipeline(tickers: list) -> dict:
results = []
for ticker in tickers:
news = get_stock_news(ticker)
sentiment = analyze_sentiment(news)
sentiment['ticker'] = ticker
sentiment['timestamp'] = datetime.now().isoformat()
results.append(sentiment)
print(f' {ticker:5s} | {sentiment["signal"]:8s} | score: {sentiment["avg_score"]:+.3f} | '
f'{sentiment["bullish_articles"]} bull / {sentiment["bearish_articles"]} bear')
time.sleep(0.3)
# Market overview
avg_market = sum(r['avg_score'] for r in results) / len(results)
market_signal = 'BULLISH' if avg_market > 0.1 else 'BEARISH' if avg_market < -0.1 else 'MIXED'
print(f'\nMarket Sentiment: {market_signal} (avg: {avg_market:+.3f})')
print(f'Cost: ${len(tickers) * 0.005:.3f} ({len(tickers)} tickers)')
# Save for historical tracking
with open('sentiment_history.jsonl', 'a') as f:
for r in results:
f.write(json.dumps(r) + '\n')
return {'results': results, 'market_signal': market_signal}
print('Financial News Sentiment Dashboard')
print('=' * 65)
sentiment_pipeline(TICKERS)Python Example
import os, requests, time
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
BULL = ['surge', 'beat', 'growth', 'strong', 'rally', 'profit']
BEAR = ['drop', 'miss', 'decline', 'weak', 'crash', 'loss']
def stock_sentiment(ticker):
resp = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
json={'query': f'{ticker} stock news', 'country_code': 'us', 'num_results': 10})
articles = resp.json().get('organic_results', [])
scores = []
for a in articles:
text = f"{a['title']} {a.get('snippet', '')}".lower()
b = sum(1 for w in BULL if w in text)
r = sum(1 for w in BEAR if w in text)
scores.append((b - r) / max(b + r, 1))
avg = sum(scores) / len(scores) if scores else 0
signal = 'BULL' if avg > 0.2 else 'BEAR' if avg < -0.2 else 'NEUTRAL'
print(f'{ticker}: {signal} ({avg:+.2f})')
for t in ['AAPL', 'NVDA', 'TSLA']:
stock_sentiment(t)
time.sleep(0.3)JavaScript Example
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
const BULL = ['surge', 'beat', 'growth', 'strong', 'rally'];
const BEAR = ['drop', 'miss', 'decline', 'weak', 'crash'];
async function stockSentiment(ticker) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: `${ticker} stock news`, country_code: 'us', num_results: 10 })
});
const articles = (await resp.json()).organic_results || [];
let totalScore = 0;
for (const a of articles) {
const text = `${a.title} ${a.snippet || ''}`.toLowerCase();
const b = BULL.filter(w => text.includes(w)).length;
const r = BEAR.filter(w => text.includes(w)).length;
totalScore += (b - r) / Math.max(b + r, 1);
}
const avg = articles.length ? totalScore / articles.length : 0;
console.log(`${ticker}: ${avg > 0.2 ? 'BULL' : avg < -0.2 ? 'BEAR' : 'NEUTRAL'} (${avg.toFixed(2)})`);
}
(async () => { for (const t of ['AAPL', 'NVDA']) await stockSentiment(t); })();Expected Output
Financial News Sentiment Dashboard
=================================================================
AAPL | BULLISH | score: +0.312 | 6 bull / 2 bear
NVDA | BULLISH | score: +0.445 | 8 bull / 1 bear
TSLA | NEUTRAL | score: +0.089 | 4 bull / 3 bear
MSFT | BULLISH | score: +0.267 | 5 bull / 2 bear
GOOGL | NEUTRAL | score: -0.034 | 3 bull / 4 bear
Market Sentiment: BULLISH (avg: +0.216)
Cost: $0.025 (5 tickers)