Tutorial

How to Build a Financial News Sentiment Pipeline

Build a financial news sentiment analysis pipeline using search API data. Collect stock news, analyze sentiment, and generate trading signals.

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.

Python
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.

Python
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.

Python
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

Python
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

JavaScript
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

JSON
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)

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.9+ installed. requests library installed. A Scavio API key from scavio.dev. A list of stock tickers to monitor. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Build a financial news sentiment analysis pipeline using search API data. Collect stock news, analyze sentiment, and generate trading signals.