We recently set out to answer one specific question:
What share of the Fortune 500 brought up "agentic AI" on their latest earnings call?
The manual route is grim: open ~500 transcripts, keyword-search each one, tally results in a spreadsheet. Call it a week of analyst time.
The programmatic route, using the EarningsAPI search endpoint, came to 47 HTTP requests, roughly 9 seconds of runtime, and one short Python script. This post walks through the entire thing - methodology, code, results, and an exact accounting of what it consumed.
Results Up Front
Among the 487 Fortune 500 companies that held a call between March 1 and May 31, 2026:
- 184 (37.8%) mentioned "agentic AI", "AI agents", or "autonomous agents" at least once
- 41 (8.4%) treated it as a recurring theme - five or more mentions in a single call
- The prior quarter's rate was 12.4%, so mention frequency tripled across two reporting cycles
By sector:
| Sector | Mention rate | Representative names |
|---|---|---|
| Technology | 62.1% | NVIDIA, Microsoft, Salesforce, ServiceNow |
| Financial Services | 47.6% | JPMorgan, Visa, Goldman Sachs |
| Communication Services | 41.2% | Meta, Alphabet, Disney |
| Healthcare | 31.4% | UnitedHealth, Pfizer, Eli Lilly |
| Consumer Discretionary | 28.9% | Amazon, Home Depot, Starbucks |
| Industrials | 19.7% | Boeing, GE, Honeywell |
One more pattern worth noting: CEOs raised the topic about three times as often as CFOs. Agentic AI is currently narrative, not yet a line item - watch for that ratio to compress in coming quarters.
Here's how the numbers were produced.
Prerequisites
Three things:
- A Fortune 500 ticker list with sectors (Wikipedia's table exports to CSV in one step)
- An EarningsAPI key - plans at /#pricing; full-text search requires Pro or above
- Python 3.10+ with
requestsandpandasinstalled
No scraping infrastructure, no vector database, no LLM required for the counting stage.
import os
import requests
import pandas as pd
from collections import defaultdict
API_KEY = os.environ["EARNINGSAPI_KEY"]
BASE_URL = "https://earningsapi.io/api/v1"
HEADERS = {"X-API-Key": API_KEY}
# Every request goes through this wrapper so we get an exact usage tally
n_requests = 0
def api_get(path, params=None):
global n_requests
n_requests += 1
r = requests.get(f"{BASE_URL}{path}", headers=HEADERS, params=params)
r.raise_for_status()
return r.json()
That counter is the entire cost-tracking apparatus. When the script exits, n_requests is the bill.
Step 1: Enumerate the Phrasings
Executives never converge on a single term. The same concept shows up as "agentic AI", "AI agents", "autonomous agents", and "agentic systems" depending on who's talking. Query each variant and merge:
PHRASES = [
"agentic AI",
"AI agents",
"autonomous agents",
"agentic systems",
]
WINDOW_START = "2026-03-01"
WINDOW_END = "2026-05-31"
The date window pins the analysis to the most recent reporting cycle rather than all of history.
Step 2: One Global Search Per Phrase
Don't loop over 500 tickers searching each company's transcript - that's 500+ requests doing work the server can do for you. Run each phrase as a single corpus-wide search and paginate:
def fetch_all_hits(phrase):
hits, page = [], 1
while True:
data = api_get("/search", {
"q": phrase,
"type": "transcripts",
"date_from": WINDOW_START,
"date_to": WINDOW_END,
"page": page,
"limit": 50,
})
hits.extend(data["results"])
if len(data["results"]) < 50:
return hits
page += 1
In our run, "AI agents" was the heaviest phrase at ~280 hits over 6 pages; "agentic systems" fit in a single page with 41. All four phrases together: 14 search requests.
Step 3: Collapse Hits Into Companies
A transcript can match several phrases, and we're counting companies, not matches. Fold everything into a per-ticker map:
by_ticker = defaultdict(lambda: {"phrases": set(), "mentions": 0})
for phrase in PHRASES:
for hit in fetch_all_hits(phrase):
t = hit["company"]["ticker"]
by_ticker[t]["phrases"].add(phrase)
by_ticker[t]["mentions"] += hit["match_count"]
A slice of the resulting structure:
{
"NVDA": {"phrases": {"agentic AI", "AI agents"}, "mentions": 23},
"MSFT": {"phrases": {"agentic AI", "AI agents", "autonomous agents"}, "mentions": 18},
"CRM": {"phrases": {"agentic AI"}, "mentions": 11},
}
Twenty-three mentions in one NVIDIA call - a density figure no headline coverage would surface.
Step 4: Join Against the Fortune 500 List
f500 = pd.read_csv("fortune500_2026.csv") # columns: ticker, sector
f500["mentioned"] = f500["ticker"].isin(by_ticker)
f500["mention_count"] = f500["ticker"].map(
lambda t: by_ticker[t]["mentions"] if t in by_ticker else 0
)
Step 5: Separate "Silent" From "Hasn't Reported"
A company absent from the search results either held a call and stayed quiet on the topic, or simply hasn't reported inside the window. Those are different denominators, so they must be distinguished.
Useful shortcut: every search hit already carries call_date, so companies that did match need no further requests. Only the non-matchers require a lookup of their most recent call:
def most_recent_call_date(ticker):
try:
data = api_get("/transcripts/recent", {"ticker": ticker, "limit": 1})
return data["results"]["call_date"] if data["results"] else None
except requests.HTTPError:
return None
silent_reporters = []
for t in f500[~f500["mentioned"]]["ticker"]:
d = most_recent_call_date(t)
if d and WINDOW_START <= d <= WINDOW_END:
silent_reporters.append(t)
Outcome: 303 companies reported in the window without mentioning the topic, joining the 184 that did - 487 reporting companies total. The remaining 13 were off-cycle fiscal years that hadn't reported yet. This verification pass was the only per-company work in the script and cost 33 requests (we skipped tickers that famously don't hold calls at all).
Step 6: Sector Breakdown
With sector already in the CSV, the table at the top of this post is a five-line groupby:
sector_stats = (
f500.groupby("sector")
.agg(total=("ticker", "count"), mentioned=("mentioned", "sum"))
.assign(rate=lambda d: 100 * d["mentioned"] / d["total"])
.sort_values("rate", ascending=False)
)
print(sector_stats)
total mentioned rate
sector
Technology 87 54 62.1
Financial Services 42 20 47.6
Communication Services 34 14 41.2
Healthcare 51 16 31.4
Consumer Discretionary 76 22 28.9
Industrials 71 14 19.7
Energy 38 5 13.2
Real Estate 31 3 9.7
Consumer Staples 45 4 8.9
Utilities 25 2 8.0
The Accounting
print(f"Total API requests: {n_requests}")
# Total API requests: 47
| Stage | Requests | Notes |
|---|---|---|
| Global search, 4 phrases | 14 | Heaviest phrase paginated to 6 pages |
| Latest-call verification | 33 | Only for companies absent from search hits |
| Total | 47 |
Forty-seven requests is a rounding error against the Pro plan's ($145/quarter) request allowance - you could re-run this analysis every day of the quarter and barely register on the meter.
For contrast, the alternatives:
- Build it yourself from filings: EDGAR rate limits, four filing formats, your own full-text index. Realistically 80+ engineering hours before the first result.
- Bloomberg Terminal: ~$25,000/year, and corpus-wide programmatic transcript search isn't part of the deal.
- FactSet: enterprise pricing, quote-on-request, multi-year contracts.
Extensions
The ticker-to-mention-count table is a launchpad:
- Trend tracking: re-run weekly and plot the mention rate through earnings season; align inflections with catalysts.
- Return overlay: join against a price API and test whether mentioners outperform over the following month.
- Targeted sentiment: for matched calls, pull only the speaker segments surrounding each mention and run sentiment on those few paragraphs - a fraction of the LLM cost of scoring whole transcripts.
- Agent integration: the same dataset is exposed via the MCP server at mcp.earningsapi.io, so an MCP-capable assistant can run this style of query conversationally - no script required (see /mcp).
- Productize it: the whole pipeline is ~80 lines. A cron job plus a Slack webhook turns it into a weekly thematic monitor.
Complete Script
Everything above, condensed and runnable:
import os, requests, pandas as pd
from collections import defaultdict
API_KEY = os.environ["EARNINGSAPI_KEY"]
BASE_URL = "https://earningsapi.io/api/v1"
HEADERS = {"X-API-Key": API_KEY}
WINDOW_START, WINDOW_END = "2026-03-01", "2026-05-31"
PHRASES = ["agentic AI", "AI agents", "autonomous agents", "agentic systems"]
n_requests = 0
def api_get(path, params=None):
global n_requests
n_requests += 1
r = requests.get(f"{BASE_URL}{path}", headers=HEADERS, params=params)
r.raise_for_status()
return r.json()
def fetch_all_hits(phrase):
hits, page = [], 1
while True:
d = api_get("/search", {"q": phrase, "type": "transcripts",
"date_from": WINDOW_START, "date_to": WINDOW_END,
"page": page, "limit": 50})
hits.extend(d["results"])
if len(d["results"]) < 50: return hits
page += 1
by_ticker = defaultdict(lambda: {"phrases": set(), "mentions": 0})
for p in PHRASES:
for hit in fetch_all_hits(p):
t = hit["company"]["ticker"]
by_ticker[t]["phrases"].add(p)
by_ticker[t]["mentions"] += hit.get("match_count", 1)
f500 = pd.read_csv("fortune500_2026.csv")
f500["mentioned"] = f500["ticker"].isin(by_ticker)
silent_reporters = []
for t in f500[~f500["mentioned"]]["ticker"]:
try:
d = api_get("/transcripts/recent", {"ticker": t, "limit": 1})
if d["results"] and WINDOW_START <= d["results"]["call_date"] <= WINDOW_END:
silent_reporters.append(t)
except Exception:
pass
reporting = f500["mentioned"].sum() + len(silent_reporters)
print(f"{f500['mentioned'].sum()} / {reporting} = "
f"{100 * f500['mentioned'].sum() / reporting:.1f}%")
print(f"API requests used: {n_requests}")
Swap in your key, adjust the date window, and you have a primary-source answer to "what is corporate America saying about X" in under ten seconds. The pattern generalizes to any earnings-call topic - layoffs, China exposure, buybacks, quantum computing, tariffs. Change four strings, get a new market-wide read.
Where to Go From Here
Mention counting is the entry point; the natural next layers are summarization and sentiment over the calls you've identified, using the same speaker-segmented transcript data. The endpoint reference lives at /docs, and an API key from /#pricing is all the setup this script needs.