Stock News API
Retrieve news articles with AI-powered sentiment scores for any stock ticker. Each article includes the headline, source, URL, and an optional sentiment score ranging from -1 (bearish) to 1 (bullish).
Endpoint
Section titled “Endpoint”GET /v2/news/{symbol}Authentication
Section titled “Authentication”Supports multiple authentication methods (in order of preference):
| Method | Example |
|---|---|
| Bearer token (recommended) | Authorization: Bearer YOUR_API_KEY |
| X-API-Key header | X-API-Key: YOUR_API_KEY |
| Query parameter | ?apiKey=YOUR_API_KEY |
| Legacy query parameter | ?token=YOUR_API_KEY |
Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
symbol | string | Yes | Stock ticker symbol (e.g., AAPL, MSFT) |
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
apiKey | string | No | Your API key (if not using header auth) |
startDate | string | No | Start date (YYYY-MM-DD) |
endDate | string | No | End date (YYYY-MM-DD) |
limit | integer | No | Maximum number of articles to return |
Request
Section titled “Request”from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
df = fb.news.ticker("AAPL", date_from="2025-01-01", date_to="2025-06-30", as_dataframe=True)print(df)# Get news articlescurl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.finbrain.tech/v2/news/AAPL"
# With date range and limitcurl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.finbrain.tech/v2/news/AAPL?startDate=2026-01-01&endDate=2026-01-31&limit=50"import requests
headers = {"Authorization": "Bearer YOUR_API_KEY"}
# Get news articlesresponse = requests.get( "https://api.finbrain.tech/v2/news/AAPL", headers=headers)result = response.json()
# With date range and limitresponse = requests.get( "https://api.finbrain.tech/v2/news/AAPL", headers=headers, params={"startDate": "2026-01-01", "endDate": "2026-01-31", "limit": 50})result = response.json()#include <iostream>#include <string>#include <curl/curl.h>#include <nlohmann/json.hpp>
using json = nlohmann::json;
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) { userp->append((char*)contents, size * nmemb); return size * nmemb;}
json get_news(const std::string& symbol, const std::string& api_key) { CURL* curl = curl_easy_init(); std::string response;
if (curl) { std::string url = "https://api.finbrain.tech/v2/news/" + symbol;
struct curl_slist* headers = nullptr; std::string auth_header = "Authorization: Bearer " + api_key; headers = curl_slist_append(headers, auth_header.c_str());
curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); curl_easy_perform(curl); curl_slist_free_all(headers); curl_easy_cleanup(curl); }
return json::parse(response);}
int main() { auto result = get_news("AAPL", "YOUR_API_KEY"); auto data = result["data"];
std::cout << "Symbol: " << data["symbol"].get<std::string>() << " (" << data["name"].get<std::string>() << ")" << std::endl;
for (auto& article : data["articles"]) { std::cout << article["date"].get<std::string>() << " | " << article["headline"].get<std::string>() << " (" << article["source"].get<std::string>() << ")" << std::endl; }
return 0;}use reqwest::blocking::Client;use reqwest::header::{AUTHORIZATION, HeaderValue};use serde::Deserialize;use std::error::Error;
#[derive(Debug, Deserialize)]struct ApiResponse { success: bool, data: NewsData,}
#[derive(Debug, Deserialize)]struct NewsData { symbol: String, name: String, articles: Vec<NewsArticle>,}
#[derive(Debug, Deserialize)]struct NewsArticle { date: String, headline: String, source: String, url: String, sentiment: Option<f64>,}
fn get_news(symbol: &str, api_key: &str) -> Result<ApiResponse, Box<dyn Error>> { let url = format!( "https://api.finbrain.tech/v2/news/{}", symbol );
let client = Client::new(); let response: ApiResponse = client .get(&url) .header(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", api_key))?) .send()? .json()?;
Ok(response)}
fn main() -> Result<(), Box<dyn Error>> { let result = get_news("AAPL", "YOUR_API_KEY")?; let data = result.data;
println!("Symbol: {} ({})", data.symbol, data.name); for article in &data.articles { let sentiment_str = match article.sentiment { Some(s) => format!("{:.3}", s), None => "N/A".to_string(), }; println!("{} | {} ({}) [sentiment: {}]", article.date, article.headline, article.source, sentiment_str); }
Ok(())}const response = await fetch( "https://api.finbrain.tech/v2/news/AAPL", { headers: { "Authorization": "Bearer YOUR_API_KEY" } });const result = await response.json();console.log(result.data);Response
Section titled “Response”Success Response (200 OK)
Section titled “Success Response (200 OK)”{ "success": true, "data": { "symbol": "AAPL", "name": "Apple Inc.", "articles": [ { "date": "2026-01-19", "headline": "My Forever Portfolio: 5 Stocks I Don't Plan on Ever Selling", "source": "Motley Fool", "url": "https://www.fool.com/investing/2026/01/19/my-forever-portfolio", "sentiment": null }, { "date": "2026-01-19", "headline": "Prediction: These 5 Unstoppable Stocks Could Join the $5 Trillion Club in 2026", "source": "Motley Fool", "url": "https://www.fool.com/investing/2026/01/19/5-trillion-club", "sentiment": 0.1027 }, { "date": "2026-01-19", "headline": "SEC Approves Expanded Option Expirations for Magnificent Seven Stocks", "source": "GuruFocus.com", "url": "https://finance.yahoo.com/news/sec-approves-expanded-option-expirations", "sentiment": 0.765 } ] }, "meta": { "timestamp": "2026-01-19T15:06:22.295Z" }}Response Fields
Section titled “Response Fields”| Field | Type | Description |
|---|---|---|
success | boolean | Whether the request was successful |
data | object | News data container |
meta | object | Response metadata |
Data Object Fields
Section titled “Data Object Fields”| Field | Type | Description |
|---|---|---|
symbol | string | Stock ticker symbol |
name | string | Company name |
articles | array | Array of news article objects |
Article Fields
Section titled “Article Fields”Each item in the articles array contains:
| Field | Type | Description |
|---|---|---|
date | string | Publication date (YYYY-MM-DD) |
headline | string | Article headline |
source | string | News source name |
url | string | Link to the full article |
sentiment | number or null | Sentiment score from -1 (bearish) to 1 (bullish), or null if not available |
Sentiment Score Interpretation
Section titled “Sentiment Score Interpretation”| Score Range | Interpretation |
|---|---|
| 0.5 to 1.0 | Strong bullish sentiment |
| 0.2 to 0.5 | Moderate bullish sentiment |
| -0.2 to 0.2 | Neutral sentiment |
| -0.5 to -0.2 | Moderate bearish sentiment |
| -1.0 to -0.5 | Strong bearish sentiment |
null | Sentiment not available for this article |
Usage Examples
Section titled “Usage Examples”Filter News by Date Range
Section titled “Filter News by Date Range”from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
df = fb.news.ticker("AAPL", date_from="2026-01-15", date_to="2026-01-19", as_dataframe=True)
for _, article in df.iterrows(): sentiment = article["sentiment"] sentiment_str = f"{sentiment:.3f}" if sentiment is not None else "N/A" print(f"{article['date']} [{sentiment_str}] {article['headline']}")Analyze Sentiment Distribution
Section titled “Analyze Sentiment Distribution”from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
df = fb.news.ticker("TSLA", as_dataframe=True)
# Filter articles that have sentiment scoresscored = df[df["sentiment"].notna()]
if scored.empty: print("No scored articles found")else: avg_sentiment = scored["sentiment"].mean() bullish = (scored["sentiment"] > 0.2).sum() bearish = (scored["sentiment"] < -0.2).sum() neutral = len(scored) - bullish - bearish
print(f"TSLA News Sentiment Summary ({len(scored)} scored articles)") print(f" Average sentiment: {avg_sentiment:.3f}") print(f" Bullish articles: {bullish} ({bullish/len(scored)*100:.0f}%)") print(f" Neutral articles: {neutral} ({neutral/len(scored)*100:.0f}%)") print(f" Bearish articles: {bearish} ({bearish/len(scored)*100:.0f}%)")Find High-Sentiment Headlines
Section titled “Find High-Sentiment Headlines”from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
df = fb.news.ticker("NVDA", as_dataframe=True)
# Find articles with strong sentiment (positive or negative)scored = df[df["sentiment"].notna()].copy()strong = scored[scored["sentiment"].abs() > 0.5]
for _, article in strong.sort_values("sentiment", ascending=False).iterrows(): direction = "Bullish" if article["sentiment"] > 0 else "Bearish" print(f"[{direction} {article['sentiment']:+.3f}] {article['headline']}") print(f" Source: {article['source']} | Date: {article['date']}")Errors
Section titled “Errors”| Code | Error | Description |
|---|---|---|
| 400 | Bad Request | Invalid symbol or query parameters |
| 401 | Unauthorized | Invalid or missing API key |
| 404 | Not Found | Ticker not found |
| 500 | Internal Server Error | Server-side error |
Related
Section titled “Related”- News Sentiment Dataset - Use cases and analysis examples
- Sentiments API - Get aggregated sentiment scores
- Ticker Predictions - Get price predictions