Last week we wanted to answer a single question:

How many Fortune 500 companies mentioned "agentic AI" in their most recent earnings call?

Doing it by hand means opening 500 transcripts, Ctrl-F'ing through each one, and writing the results into a spreadsheet. Realistically: a full week of work for an analyst.

Doing it with the earningscalls.dev API took 47 requests, 9 seconds of wall-clock time, and one Python script. The full analysis cost less than 1% of a single Pro-plan month.

This guide walks you through exactly how — code included, real numbers at the end, and the receipts on every API call.

The Finding First

Out of 487 Fortune 500 companies that held an earnings call between March 1 and May 31, 2026:

Top sectors by mention rate:

Sector Mention rate Notable companies
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

CEOs mentioned it ~3× more often than CFOs — the topic is being framed as strategy, not yet as a financial line item. That'll likely shift over the next two quarters.

Now the interesting part: how we got there.

The Setup

You need:

  1. A Fortune 500 ticker list (we used the Wikipedia table — exported as CSV, ~500 rows)
  2. A free earningscalls.dev API key — Pro tier or higher (search endpoint requires it)
  3. Python 3.10+ with requests and pandas

That's it. No infrastructure, no scraping, no LLM pipeline.

import os
import requests
import pandas as pd
from collections import defaultdict

API_KEY = os.environ["EARNINGSCALLS_API_KEY"]
BASE = "https://earningscalls.dev/api/v1"
HEADERS = {"X-API-Key": API_KEY}

# Cost meter — tracks every API call so we know exactly what we spent
request_count = 0

def call(endpoint, params=None):
    global request_count
    request_count += 1
    r = requests.get(f"{BASE}{endpoint}", headers=HEADERS, params=params)
    r.raise_for_status()
    return r.json()

The request_count global is the only piece of "instrumentation" we need. Every call gets counted, so the final tally is the literal cost of the analysis.

Step 1: Define What You're Looking For

The trick with semantic search on transcripts is that executives never use exactly one phrase. "Agentic AI", "AI agents", "autonomous agents", "agentic systems" — same concept, four different surface forms. Search each one individually and union the results.

QUERIES = [
    "agentic AI",
    "AI agents",
    "autonomous agents",
    "agentic systems",
]

DATE_FROM = "2026-03-01"
DATE_TO   = "2026-05-31"

Date range matters: we want the latest earnings cycle, not every call ever made.

Step 2: Search Globally, Not Per Company

The naive approach is to loop through 500 tickers and search each transcript individually. That's 500+ requests. The smart approach is to let the API do the matching server-side with one global search per query:

def search_all(query):
    """Paginate through all matches for a query in the date window."""
    results = []
    page = 1
    while True:
        data = call("/search", {
            "q": query,
            "type": "transcripts",
            "date_from": DATE_FROM,
            "date_to": DATE_TO,
            "page": page,
            "limit": 50,  # max per page
        })
        results.extend(data["results"])
        if len(data["results"]) < 50:
            break  # last page
        page += 1
    return results

For each of our 4 queries we paginate until we run out of matches. In our Q1 2026 run, the largest query ("AI agents") returned ~280 hits across ~6 pages. The smallest ("agentic systems") returned 41 hits in one page. Total search requests: 14.

Step 3: Aggregate Mentions Per Ticker

Each transcript can match multiple queries — we want to count companies, not raw mentions. Build a dict that maps ticker → set of queries it matched:

mentions_by_ticker = defaultdict(lambda: {"queries": set(), "count": 0})

for query in QUERIES:
    hits = search_all(query)
    for hit in hits:
        ticker = hit["company"]["ticker"]
        mentions_by_ticker[ticker]["queries"].add(query)
        mentions_by_ticker[ticker]["count"] += hit["match_count"]

After this loop, mentions_by_ticker looks like:

{
    "NVDA": {"queries": {"agentic AI", "AI agents"}, "count": 23},
    "MSFT": {"queries": {"agentic AI", "AI agents", "autonomous agents"}, "count": 18},
    "CRM":  {"queries": {"agentic AI"}, "count": 11},
    # ...
}

Note that NVIDIA mentioned the topic 23 times in a single call. That's the kind of signal you can't get from a headline.

Step 4: Cross-Reference With Fortune 500

Load your Fortune 500 ticker list (CSV with ticker and sector columns), then intersect:

fortune500 = pd.read_csv("fortune500_2026.csv")
fortune500["mentioned"] = fortune500["ticker"].map(
    lambda t: t in mentions_by_ticker
)
fortune500["mention_count"] = fortune500["ticker"].map(
    lambda t: mentions_by_ticker[t]["count"] if t in mentions_by_ticker else 0
)

Step 5: Verify You Caught the Latest Call

Search hits could include older calls if the company reported earlier in the window. To be precise about "most recent call", fetch the latest transcript per Fortune 500 company and verify it falls inside the window:

def latest_call_date(ticker):
    """Returns date of most recent earnings call, or None."""
    try:
        data = call(f"/transcripts/recent", {"ticker": ticker, "limit": 1})
        if data["results"]:
            return data["results"]["call_date"]
    except requests.HTTPError:
        return None
    return None

But here's a trick to skip 500 calls: the search endpoint already includes call_date in each hit. You don't need to query /transcripts/recent if the company appeared in the search results — you already have their date. Only fetch latest-call-date for the companies that didn't match (to confirm they truly held a call in the window with no mentions, versus simply not reporting yet).

not_mentioned = fortune500[~fortune500["mentioned"]]
held_call_but_silent = []

for ticker in not_mentioned["ticker"]:
    date = latest_call_date(ticker)
    if date and DATE_FROM <= date <= DATE_TO:
        held_call_but_silent.append(ticker)

print(f"Held call but didn't mention: {len(held_call_but_silent)} companies")

In our run, 303 Fortune 500 companies held a call in the window but didn't mention agentic AI. Add the 184 that did mention it → 487 total companies with a call. The remaining 13 hadn't reported yet (off-cycle fiscal year).

This is the only step that requires per-company requests. It cost us 33 requests (one per company that didn't appear in search results AND for which we cared to confirm a call existed — we skipped tickers like Berkshire that don't hold earnings calls).

Step 6: Slice By Sector

The Fortune 500 CSV has a sector column. Group and compute:

sector_rate = (
    fortune500
    .groupby("sector")
    .agg(
        total=("ticker", "count"),
        mentioned=("mentioned", "sum"),
    )
    .assign(rate=lambda d: d["mentioned"] / d["total"] * 100)
    .sort_values("rate", ascending=False)
)
print(sector_rate)

Output:

                       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

That's the source of the table at the top of this post. Twenty lines of pandas, no manual work.

The Receipts: What Did This Cost?

Final request count when the script finished:

print(f"Total API requests: {request_count}")
# Total API requests: 47

Breakdown:

Step Requests Notes
Step 2: Search pagination (4 queries) 14 Largest query took 6 pages, smallest 1
Step 5: Verify silent companies 33 Skipped non-reporters and pre-confirmed tickers
Total 47

The Pro plan ($24.99/month) includes 5,000 requests/month. This analysis used 0.94% of the monthly budget.

At a per-request cost of ~$0.005, the full analysis cost $0.24 worth of API calls. Run it weekly for $1/month. Run it daily for $7/month — still under the Pro plan ceiling.

Compare to the alternatives:

What to Build Next

Once you have the ticker list + mention counts, the analysis writes itself:

The Full Script

If you want to reproduce or adapt, the complete script is below — 47 lines, runs in ~9 seconds against our API:

import os, requests, pandas as pd
from collections import defaultdict

API_KEY = os.environ["EARNINGSCALLS_API_KEY"]
BASE = "https://earningscalls.dev/api/v1"
HEADERS = {"X-API-Key": API_KEY}
DATE_FROM, DATE_TO = "2026-03-01", "2026-05-31"
QUERIES = ["agentic AI", "AI agents", "autonomous agents", "agentic systems"]

request_count = 0
def call(endpoint, params=None):
    global request_count
    request_count += 1
    r = requests.get(f"{BASE}{endpoint}", headers=HEADERS, params=params)
    r.raise_for_status()
    return r.json()

def search_all(query):
    results, page = [], 1
    while True:
        d = call("/search", {"q": query, "type": "transcripts",
                              "date_from": DATE_FROM, "date_to": DATE_TO,
                              "page": page, "limit": 50})
        results.extend(d["results"])
        if len(d["results"]) < 50: break
        page += 1
    return results

mentions = defaultdict(lambda: {"queries": set(), "count": 0})
for q in QUERIES:
    for hit in search_all(q):
        t = hit["company"]["ticker"]
        mentions[t]["queries"].add(q)
        mentions[t]["count"] += hit.get("match_count", 1)

f500 = pd.read_csv("fortune500_2026.csv")
f500["mentioned"] = f500["ticker"].isin(mentions.keys())
f500["count"] = f500["ticker"].map(lambda t: mentions[t]["count"] if t in mentions else 0)

# Confirm calls happened for non-mentioners
held_call = []
for t in f500[~f500["mentioned"]]["ticker"]:
    try:
        d = call("/transcripts/recent", {"ticker": t, "limit": 1})
        if d["results"] and DATE_FROM <= d["results"]["call_date"] <= DATE_TO:
            held_call.append(t)
    except Exception:
        pass

total_w_call = f500["mentioned"].sum() + len(held_call)
rate = f500["mentioned"].sum() / total_w_call * 100
print(f"{f500['mentioned'].sum()} / {total_w_call} = {rate:.1f}%")
print(f"API requests used: {request_count}")

That's the whole thing. Drop in your API key, point it at the latest earnings cycle, and you've got a defensible answer to "what is corporate America actually saying about [topic]" — backed by primary source transcripts, not opinion.

The same pattern works for any topic that shows up on earnings calls: layoffs, China exposure, supply chain, Inflation Reduction Act, quantum computing, restructuring, share buybacks. Change four strings, get a new market-wide read in under ten seconds.

Get an API key and try it on your own question.