The EODHD Financial News API returns news articles for any ticker or topic, with the full text, the symbols each article mentions, topic tags and a sentiment score. Two related endpoints add daily sentiment per ticker and the most weighted words in a ticker’s coverage.
This guide shows how to use the financial news API from Python: filtering by ticker, topic and date, paging through a period without duplicates, turning the results into a clean pandas table, and comparing article sentiment with daily sentiment. At the end, everything comes together in a script that builds a seven-day news digest for one company.
I ran every example on November 3, 2026. For Apple, the week of October 27 to November 2 returned 263 articles. After removing duplicates, 255 remained, and 112 of those named Apple or AAPL in the headline.
Quick jump:
- 1 How to get your EODHD API key
- 2 Financial news API quick start with the Python library
- 3 Filtering the financial news API by ticker, topic and date
- 4 Paging through the financial news API without duplicates
- 5 Turning news into a clean pandas table
- 6 Article sentiment vs daily sentiment
- 7 Which words drove the coverage
- 8 Complete script: a seven-day digest with the financial news API
- 9 Reading the results with care
- 10 Standard news tags
- 11 Conclusion
- 12 FAQs
How to get your EODHD API key
1. Use the “demo” API key to test our data from a limited set of tickers without registering:
AAPL.US | TSLA.US | VTI.US | AMZN.US | BTC-USD | EUR-USD
Real-Time Data and all of the APIs (except Bulk) are included without API calls limitations with these tickers and the demo key.
2. Register to get your free API key (limited to 20 API calls per day) with access to:
End-Of-Day Historical Data with only the past year for any ticker, and the List of tickers per Exchange.
3. To unlock your API key, we recommend to choose the subscription plan which covers your needs.
For the news endpoints specifically, the demo key covers ticker queries, daily sentiment and word weights for the demo tickers. Queries by topic alone (the t parameter without s) need your own key. News, sentiment and word weights are included in every plan, the free one too, and each news request costs 5 API calls.
The scripts below read the key from an environment variable and fall back to the demo key:
import os
API_KEY = os.environ.get("EODHD_API_KEY", "demo")
Financial news API quick start with the Python library
The official Python library wraps the news endpoint in one method. Install it with pip install eodhd (version 1.0.32 at the time of writing):
import pandas as pd
from eodhd import APIClient
api = APIClient("demo") # replace with your API key
news = api.financial_news(s="AAPL.US", from_date="2026-10-27", to_date="2026-11-02", limit=5)
df = pd.DataFrame(news)[["date", "title"]]
print(df.to_string())
The first rows of the output:
date title
0 2026-11-02T23:34:07+00:00 Apple beats sales and profit expectations but AI costs weigh on holiday sales
1 2026-11-02T23:25:32+00:00 Apple says it’s ‘hard to predict’ when iPhone Duo constraints will improve
2 2026-11-02T23:17:54+00:00 The Reaction to Apple Earnings Reveals a Sensitive Market
The method accepts s, t, from_date, to_date, limit and offset. For the direction parameter and the word weights endpoint, which the library doesn’t cover, the rest of this guide calls the API directly with requests.
Filtering the financial news API by ticker, topic and date
All requests go to https://eodhd.com/api/news. These are the parameters:
| Parameter | What it does | Notes |
|---|---|---|
| s | Ticker, for example AAPL.US | Use s, t or both |
| t | Topic tag, for example earnings release | Matching is case-insensitive |
| from, to | Date range, YYYY-MM-DD, inclusive | |
| limit | Articles per request | Default 50, range 1-1000 |
| offset | Articles to skip | offset + limit up to 10,000 |
| direction | 0 newest first (default), 1 in database order | With 1, from and to are required, window up to 31 days |
In addition, each article comes back with date, title, content, link, symbols, tags and sentiment (polarity, neg, neu, pos).
Filtering by topic returns news across all companies. Here are ten articles tagged earnings release on November 2, 2026:
import os
import requests
import pandas as pd
API_KEY = os.environ.get("EODHD_API_KEY", "demo")
params = {"t": "earnings release", "from": "2026-11-02", "to": "2026-11-02",
"limit": 10, "api_token": API_KEY, "fmt": "json"}
news = requests.get("https://eodhd.com/api/news", params=params, timeout=60).json()
df = pd.DataFrame(news)
df["us_tickers"] = df["symbols"].apply(lambda s: ", ".join(x for x in s if x.endswith(".US"))[:30])
print(df[["date", "title", "us_tickers"]].assign(title=df["title"].str[:60], date=df["date"].str[11:16]).to_string(index=False))
date title us_tickers
23:10 Voya Financial (VOYA) Misses Q3 Earnings Estimates RGA.US, VOYA-PB.US, VOYA.US
23:10 Tetra Technologies (TTI) Q3 Earnings and Revenues Beat Estim KNTK.US, TTI.US
23:10 Vornado (VNO) Tops Q3 FFO Estimates CLPR.US, VNO-PL.US, VNO-PM.US,
23:10 UMH Properties (UMH) Q3 FFO Meet Estimates BRT.US, UMH.US
23:10 Inspire Medical Systems (INSP) Q3 Earnings and Revenues Beat AMWL.US, INSP.US
Passing s and t together narrows the feed to one company and one topic. With s=AAPL.US and t=earnings release, all 100 articles in my sample carried the EARNINGS RELEASE tag and listed AAPL.US.
Also, tags come in two kinds. There is a standard list (at the end of this guide), and the feed also adds AI-detected tags, so results carry values beyond that list, such as ARTIFICIAL-INTELLIGENCE, PRODUCT-LAUNCH or SUPPLY-CHAIN. Tags are returned in upper case. To find the exact spelling of a tag before you filter on it, pull a few articles for a ticker and look at their tags field.
Paging through the financial news API without duplicates
With the default order (newest first), new articles arriving while you page can shift results between pages. For a complete pull of a past period, set direction=1 with from and to, then raise offset by limit on every request. Articles then come in the order they entered the database:
BASE = "https://eodhd.com/api"
def fetch_news(ticker, date_from, date_to, page_size=100):
"""Page forward through a period (max 31 days) without duplicates."""
articles, offset = [], 0
while True:
params = {
"s": ticker, "from": date_from, "to": date_to,
"direction": 1, "limit": page_size, "offset": offset,
"api_token": API_KEY, "fmt": "json",
}
resp = requests.get(f"{BASE}/news", params=params, timeout=60)
resp.raise_for_status()
page = resp.json()
articles.extend(page)
if len(page) < page_size:
return articles
offset += page_size
For AAPL.US between October 27 and November 2, 2026, this took three requests and returned 263 articles. For longer periods, however, split the range into windows of up to 31 days. If a request has an invalid combination of parameters, the API answers with HTTP 422 and names the parameter, for example “The window between from and to must not exceed 31 days when direction=1.”
Turning news into a clean pandas table
Three steps make the raw list usable: keep the fields you need, pull the polarity score out of the sentiment object, and remove duplicates. The same story often appears more than once, from different feeds or with a different link, so the second pass compares normalised titles:
def to_table(articles):
df = pd.DataFrame(articles)
df["date"] = pd.to_datetime(df["date"], utc=True)
df["polarity"] = df["sentiment"].apply(lambda s: (s or {}).get("polarity"))
df["n_symbols"] = df["symbols"].apply(len)
return df[["date", "title", "link", "symbols", "n_symbols", "tags", "polarity"]]
def deduplicate(df):
before = len(df)
df = df.drop_duplicates(subset="link")
key = df["title"].str.lower().str.replace(r"[^a-z0-9 ]", "", regex=True).str.strip()
df = df.loc[~key.duplicated()]
return df, before - len(df)
On the Apple week, deduplication removed 8 of the 263 articles. The n_symbols column matters more than it looks. An article returned for AAPL.US can be about Apple, or it can mention Apple among dozens of companies. In this sample the median article listed 15 symbols (this count includes Apple’s own listings on other exchanges, such as AAPL.MX and APC.F), and 32 articles listed exactly 50 (none listed more).
For example, a simple way to separate the two is to check whether the company or its ticker appears in the headline:
pattern = r"\b(?:Apple|AAPL)\b"
df["in_headline"] = df["title"].str.contains(pattern, case=False, regex=True)
date title n_symbols polarity in_headline
2026-10-27 05:12:39+00:00 Apple responds to early screen flaws on $1,999 foldable iPhone Duo 2 -0.961 True
2026-10-27 07:00:00+00:00 Bull of the Day: Taiwan Semiconductor Manufacturing Company (TSM) 28 0.998 False
2026-10-27 09:11:06+00:00 Bending Spoons proposes $1.5B senior secured notes offering due 2032 3 0.896 False
2026-10-27 11:21:36+00:00 Apple customers say their folding iPhones are cracking 11 -0.945 True
112 of the 255 articles named Apple or AAPL in the headline. The other 143 are still useful for context, for example sector or supplier news, but they shouldn’t weigh as much in a company digest. Finally, exporting is one line; join the list columns first so they read well in a spreadsheet:
export = df.copy()
export["symbols"] = export["symbols"].str.join(" ")
export["tags"] = export["tags"].str.join(" ")
export.to_csv("aapl_news.csv", index=False)
Article sentiment vs daily sentiment
Each article has a polarity score from -1 (negative) to 1 (positive). The Sentiment Data API at https://eodhd.com/api/sentiments returns one aggregated score per ticker and day, together with the number of articles behind it:
def fetch_daily_sentiment(ticker, date_from, date_to):
params = {"s": ticker, "from": date_from, "to": date_to,
"api_token": API_KEY, "fmt": "json"}
resp = requests.get(f"{BASE}/sentiments", params=params, timeout=60)
resp.raise_for_status()
rows = resp.json().get(ticker, [])
return pd.DataFrame(rows).rename(columns={"count": "api_count", "normalized": "api_sentiment"})
To see how the two relate, I averaged the article polarity per day (after deduplication) and put it next to the daily score:
| Day | Articles (deduplicated) | Average article polarity | Daily sentiment API | Articles counted by the API |
|---|---|---|---|---|
| 2026-10-27 | 30 | 0.733 | 0.7335 | 30 |
| 2026-10-28 | 30 | 0.673 | 0.6837 | 31 |
| 2026-10-29 | 10 | 0.658 | 0.6889 | 11 |
| 2026-10-30 | 29 | 0.484 | 0.5142 | 31 |
| 2026-10-31 | 10 | 0.892 | 0.9013 | 11 |
| 2026-11-01 | 14 | 0.180 | 0.1799 | 13 |
| 2026-11-02 | 132 | 0.536 | 0.5441 | 135 |
The two track each other closely: on every day the scores differ by less than 0.035, and the article counts by three at most. In practice, the daily endpoint is the quick way to chart sentiment over months for one or several tickers in a single request (s accepts a comma-separated list). The article-level score is what you need to explain a day: which stories pulled it up or down, and whether they were about the company at all.
For example, take November 1. The daily score fell to 0.18 on 13 articles, and the article table shows which headlines produced that drop.
Which words drove the coverage
The News Word Weights API at https://eodhd.com/api/news-word-weights returns the most relevant words in a ticker’s news over a date range. It processes hundreds of articles with AI, so give the request a long timeout:
params = {"s": "AAPL.US", "filter[date_from]": "2026-10-27", "filter[date_to]": "2026-11-02",
"page[limit]": 15, "api_token": API_KEY}
# The endpoint processes hundreds of articles, so allow a long timeout and retry once
for attempt in range(2):
resp = requests.get("https://eodhd.com/api/news-word-weights", params=params, timeout=180)
if resp.ok and resp.text.strip():
break
data = resp.json()
print(f"Processed {data['meta']['news_processed']} of {data['meta']['news_found']} articles")
for word, weight in data["data"].items():
print(f"{word:<10} {weight:.5f}")
Processed 263 of 1677 articles
apple 0.01387
inc 0.01238
stock 0.01015
etf 0.00853
ai 0.00813
market 0.00735
billion 0.00700
companies 0.00691
earn 0.00674
report 0.00637
year 0.00607
share 0.00556
m 0.00551
revenue 0.00510
hold 0.00509
The request took about 30 seconds. For a quick read of what a week’s coverage was about, filter out generic words (inc, stock, market, companies) and look at what remains: here ai, billion, earn, report and revenue point to an earnings week with an AI angle. If a request takes too long, narrow the date range.
Complete script: a seven-day digest with the financial news API
This script puts everything together. It pulls a week of news for one ticker, removes duplicates, flags the articles that name the company in the headline, compares article and daily sentiment, saves a CSV and prints the most positive and most negative headlines about the company.
"""Seven-day news digest for one ticker with the EODHD Financial News API.
Usage: python news_digest.py AAPL.US Apple 2026-10-27 2026-11-02
Reads the key from EODHD_API_KEY; falls back to the demo key.
"""
import os
import re
import sys
import pandas as pd
import requests
API_KEY = os.environ.get("EODHD_API_KEY", "demo")
BASE = "https://eodhd.com/api"
def fetch_news(ticker, date_from, date_to, page_size=100):
"""Page forward through a period (max 31 days) without duplicates."""
articles, offset = [], 0
while True:
params = {
"s": ticker, "from": date_from, "to": date_to,
"direction": 1, "limit": page_size, "offset": offset,
"api_token": API_KEY, "fmt": "json",
}
resp = requests.get(f"{BASE}/news", params=params, timeout=60)
resp.raise_for_status()
page = resp.json()
articles.extend(page)
if len(page) < page_size:
return articles
offset += page_size
def fetch_daily_sentiment(ticker, date_from, date_to):
params = {"s": ticker, "from": date_from, "to": date_to,
"api_token": API_KEY, "fmt": "json"}
resp = requests.get(f"{BASE}/sentiments", params=params, timeout=60)
resp.raise_for_status()
rows = resp.json().get(ticker, [])
return pd.DataFrame(rows).rename(columns={"count": "api_count", "normalized": "api_sentiment"})
def to_table(articles):
df = pd.DataFrame(articles)
df["date"] = pd.to_datetime(df["date"], utc=True)
df["polarity"] = df["sentiment"].apply(lambda s: (s or {}).get("polarity"))
df["n_symbols"] = df["symbols"].apply(len)
return df[["date", "title", "link", "symbols", "n_symbols", "tags", "polarity"]]
def deduplicate(df):
before = len(df)
df = df.drop_duplicates(subset="link")
key = df["title"].str.lower().str.replace(r"[^a-z0-9 ]", "", regex=True).str.strip()
df = df.loc[~key.duplicated()]
return df, before - len(df)
def main(ticker, company, date_from, date_to):
raw = fetch_news(ticker, date_from, date_to)
df = to_table(raw)
df, dropped = deduplicate(df)
# Headline relevance: is the company the subject, or one name in a long list?
symbol = ticker.split(".")[0]
pattern = rf"\b(?:{re.escape(company)}|{re.escape(symbol)})\b"
df["in_headline"] = df["title"].str.contains(pattern, case=False, regex=True)
df["day"] = df["date"].dt.strftime("%Y-%m-%d")
daily = df.groupby("day").agg(
articles=("title", "size"),
in_headline=("in_headline", "sum"),
avg_polarity=("polarity", "mean"),
).round(3)
sentiment = fetch_daily_sentiment(ticker, date_from, date_to)
daily = daily.join(sentiment.set_index("date"), how="left")
out = f"{symbol.lower()}_news_{date_from}_{date_to}.csv"
export = df.copy()
export["symbols"] = export["symbols"].str.join(" ")
export["tags"] = export["tags"].str.join(" ")
export.drop(columns="day").to_csv(out, index=False)
print(f"{ticker} news, {date_from} to {date_to}")
print(f"Fetched {len(raw)} articles, removed {dropped} duplicates, kept {len(df)}")
print(f"{int(df['in_headline'].sum())} of {len(df)} name {company} or {symbol} in the headline\n")
print(daily.to_string(), "\n")
print("Most positive and most negative headlines naming the company:")
named = df[df["in_headline"]].dropna(subset=["polarity"]).sort_values("polarity")
for _, row in pd.concat([named.tail(3).iloc[::-1], named.head(3)]).iterrows():
print(f" {row['polarity']:+.2f} {row['day']} {row['title'][:90]}")
print(f"\nSaved {out}")
if __name__ == "__main__":
main(*sys.argv[1:5])
Run it with python news_digest.py AAPL.US Apple 2026-10-27 2026-11-02. The output:
AAPL.US news, 2026-10-27 to 2026-11-02
Fetched 263 articles, removed 8 duplicates, kept 255
112 of 255 name Apple or AAPL in the headline
articles in_headline avg_polarity api_count api_sentiment
day
2026-10-27 30 16 0.733 30 0.7335
2026-10-28 30 17 0.673 31 0.6837
2026-10-29 10 2 0.658 11 0.6889
2026-10-30 29 11 0.484 31 0.5142
2026-10-31 10 1 0.892 11 0.9013
2026-11-01 14 5 0.180 13 0.1799
2026-11-02 132 60 0.536 135 0.5441
Most positive and most negative headlines naming the company:
+1.00 2026-11-02 Apple Sales Meet Estimates, China Is a Weak Spot
+1.00 2026-10-28 Apple unveils groundbreaking MacBook Pro with PROLED XDR Display and Touch for the first t
+1.00 2026-10-27 Apple (AAPL) vs. MSFT and GOOGL: Is the Post-Earnings Selloff Creating a Long-Term Buying
-0.96 2026-10-27 Apple responds to early screen flaws on $1,999 foldable iPhone Duo
-0.94 2026-10-27 Apple customers say their folding iPhones are cracking
-0.72 2026-10-28 Apple in spotlight as Monness, Crespi, Hardt ups price target on new products
Saved aapl_news_2026-10-27_2026-11-02.csv
The digest used four requests: three pages of news and one sentiment call. It works with the demo key for the demo tickers; for any other ticker, set EODHD_API_KEY to your own key.
Reading the results with care
A few points help you read a digest like this one correctly.
First, each score describes an article, not just its headline. When a score drives a decision, read the article behind it.
Second, relevance needs a filter of its own. A ticker query returns every article that mentions the ticker. For a company digest, the in_headline flag (or a minimum share of mentions in the content) separates news about the company from news that only mentions it.
Third, volume moves with events. November 2 had 132 articles, more than four times any other day of the week, as coverage of Apple’s quarterly results arrived (headlines such as “Apple: Fiscal Q4 Earnings Snapshot”). A daily average over 10 articles and one over 132 articles carry different weight, so keep the article count next to the score.
Finally, sentiment describes the tone of the coverage. It is a useful input for monitoring and research, not a trading signal on its own.
Besides the AI-detected tags, the API supports this standard list of topic values for the t parameter:
balance sheet, capital employed, class action, company announcement, consensus eps estimate, consensus estimate, credit rating, discounted cash flow, dividend payments, earnings estimate, earnings growth, earnings per share, earnings release, earnings report, earnings results, earnings surprise, estimate revisions, european regulatory news, financial results, fourth quarter, free cash flow, future cash flows, growth rate, initial public offering, insider ownership, insider transactions, institutional investors, institutional ownership, intrinsic value, market research reports, net income, operating income, present value, press releases, price target, quarterly earnings, quarterly results, ratings, research analysis and reports, return on equity, revenue estimates, revenue growth, roce, roe, share price, shareholder rights, shareholder, shares outstanding, split, strong buy, total revenue, zacks investment research, zacks rank
For more detail on every parameter and field, see the Financial News API documentation. To measure how news moves prices, continue with Analyzing News Impact on Stocks with Python.
Conclusion
The EODHD Financial News API covers the full path from raw headlines to a usable dataset: ticker and topic filters, a paging mode that avoids duplicates, sentiment per article and per day, and word weights for a quick view of what the coverage was about. With about a hundred lines of Python you get a weekly digest that you can run for any ticker, extend to a watchlist, or feed into your own analysis.
First, try the demo key on AAPL, TSLA or AMZN, then switch to your own key for any other ticker or for topic queries.
FAQs
Q: Is there a free financial news API?
Yes. News, sentiment and word weights are included in every EODHD plan, including the free one. Each news request costs 5 API calls, so the free plan’s 20 daily calls cover four news requests. The demo key works without registration for AAPL.US, TSLA.US, VTI.US, AMZN.US, BTC-USD and EUR-USD.
Q: How do I get stock news for a specific ticker in Python?
Call https://eodhd.com/api/news with s set to the ticker in TICKER.EXCHANGE format (for example AAPL.US), or use api.financial_news(s=”AAPL.US”) from the eodhd Python library. Add from and to to limit the dates.
Q: How do I get more than 1,000 articles?
Use offset to page through results. The sum of offset and limit can go up to 10,000. For a complete pull of a period, set direction=1 with from and to (up to 31 days) and raise offset by limit on each request.
Q: What does the news sentiment score mean?
Each article has a polarity from -1 (negative) to 1 (positive), plus the neg, neu and pos components. The Sentiment Data API returns one normalized score per ticker and day on the same scale, with the number of articles used.
Q: Can I filter news by topic instead of ticker?
Yes. Use the t parameter with a standard tag such as earnings release or an AI-detected tag such as ARTIFICIAL-INTELLIGENCE. Topic queries without a ticker need your own API key.