Authentication

All API requests require an API key passed via the X-API-Key header or api_key query parameter.

Required Header
X-API-Key: YOUR_API_KEY
Get your API Key
  1. Subscribe to a plan on earningscalls.dev
  2. Your API key is shown after checkout and sent to your email
  3. Log in to your Dashboard to view or regenerate your key

Also available via RapidAPI with RapidAPI headers.

Base URL

Direct API
https://earningscalls.dev/api/v1

All endpoints are prefixed with /api/v1. Responses are JSON with consistent pagination.

RapidAPI users: use https://earnings-call-transcripts1.p.rapidapi.com/api/v1 with RapidAPI headers instead.

Rate Limits

TierPriceRequests/MonthFull Access
Free$050Preview only
Pro$24.995,000
Ultra$39.9925,000
Enterprise$299100,000

Managing Your Subscription

How you change tier or cancel depends on where you signed up:

If you subscribed on earningscalls.dev

  • Open your Dashboard to view your API key, regenerate it, see your usage, or cancel your plan.
  • Billing runs through Paddle. Invoices arrive automatically with every payment and are issued in the name of Dreaverr Digital Solutions LLP (the legal entity behind earningscalls.dev). Update your business name, address or VAT/tax ID from the "Update billing details" link inside any Paddle email.
  • To switch tier (Pro ↔ Ultra ↔ Enterprise): cancel the current plan from your Dashboard first, then subscribe to the new tier on the pricing section. The new tier becomes active immediately; the old plan keeps running in parallel until the end of its billing period — no double charge, just a short overlap.

If you subscribed on RapidAPI

  • Sign in to your RapidAPI account and open the API page.
  • Use RapidAPI's "Subscribe" or "Switch Plan" button to change tier, or "Cancel Subscription" to end it. Plan changes typically take effect at the next billing cycle.
  • Billing and invoices come from RapidAPI directly — not from us. If you need a different billing address or tax ID, update it in your RapidAPI account settings.

Stuck? Email [email protected] with your invoice number or RapidAPI username and we'll sort it out.

Error Handling

The API uses standard HTTP status codes. Error responses include a message:

Error Response
{
  "error": "Forbidden",
  "message": "Full transcripts require a Pro or Enterprise plan"
}
200 Success
400 Bad Request - Invalid parameters
401 Unauthorized - Missing or invalid API key
403 Forbidden - Insufficient tier for this endpoint
404 Not Found - Resource doesn't exist
429 Rate Limited - Daily quota exceeded
500 Server Error - Something went wrong on our end

Recipes

Practical patterns for common workflows. Pick the one closest to your use case — they all use the same authentication and base URL from the Getting Started sections above.

Search Syntax

The q parameter accepts Google-style operators. PostgreSQL handles stemming automatically, so guidance matches guidance, guidances, guided.

Search operators
# Implicit AND between words (both must appear)
GET /api/v1/search?q=agentic+AI

# Exact phrase — words must appear in this order
GET /api/v1/search?q=%22raised+guidance%22

# Boolean OR
GET /api/v1/search?q=agentic+OR+autonomous

# Negation (exclude word)
GET /api/v1/search?q=agentic+-human

# Combine — phrase + negation
GET /api/v1/search?q=%22raised+guidance%22+-macro

Get the latest call for a ticker

Two endpoints depending on what you need. /companies/ticker/:ticker/latest returns just the most recent call metadata. /companies/ticker/:ticker returns the full call history.

curl — single latest call
curl "https://earningscalls.dev/api/v1/companies/ticker/NVDA/latest" \
  -H "X-API-Key: $EARNINGSCALLS_API_KEY"
Python — full history
import requests, os

H = {"X-API-Key": os.environ["EARNINGSCALLS_API_KEY"]}
r = requests.get(
    "https://earningscalls.dev/api/v1/companies/ticker/NVDA",
    headers=H,
)
data = r.json()["data"]
print(f"{data['company_name']} — {len(data['earnings_calls'])} calls available")
for call in data["earnings_calls"][:5]:
    print(f"  {call['event_date_time'][:10]}  {call['transcript_title']}")

Cross-company aggregate — how many of [N tickers] mentioned X

The /search/by_ticker endpoint runs one SQL query that groups results per ticker. Pass a comma-separated tickers list to scope to a known universe (Fortune 500, S&P 500, Mag 7).

How many F500 mentioned "agentic AI" in Q1 2026?
GET /api/v1/search/by_ticker
  ?q=agentic+AI
  &tickers=AAPL,MSFT,NVDA,GOOGL,META,AMZN,TSLA,JPM,V,...
  &date_from=2026-01-01
  &date_to=2026-04-30
  &limit=500

# response
{
  "tickers_scoped": 500,
  "tickers_with_mentions": 184,
  "total_calls_matched": 192,
  "results": [
    { "ticker": "NOW", "company_name": "ServiceNow", "sector": "Technology",
      "calls_matched": 1, "last_match_date": "2026-04-23",
      "earnings_ids": ["..."] },
    ...
  ]
}

Returns up to 500 tickers ranked by calls_matched. One request instead of 500 — built for cross-sectional NLP analyses.

Incremental sync (cursor-based polling)

For daily ETL jobs that pick up new calls without duplicates. The /transcripts/recent endpoint returns a next_after_id cursor — pass it back in subsequent requests.

Python ETL pattern
import requests, json, pathlib

STATE = pathlib.Path("./.last_id.json")
H = {"X-API-Key": os.environ["EARNINGSCALLS_API_KEY"]}

# Load last seen cursor
last = json.loads(STATE.read_text()) if STATE.exists() else {}
after_id = last.get("after_id", 0)

while True:
    r = requests.get(
        "https://earningscalls.dev/api/v1/transcripts/recent",
        params={"after_id": after_id, "limit": 100},
        headers=H,
    ).json()
    for call in r["data"]:
        process(call)  # → push to your DB / vector store / queue
    if not r["pagination"]["has_more"]:
        break
    after_id = r["pagination"]["next_after_id"]

STATE.write_text(json.dumps({"after_id": after_id}))

Idempotent — safe to re-run. The cursor advances only on successful pages, so a crash mid-sync resumes from the right place.

Compare a company's last N quarters

Use get_company_info's earnings_calls array (sorted newest first) to get the last N call IDs, then fetch summaries in parallel.

Compare last 4 quarters of guidance language
import requests, os
from concurrent.futures import ThreadPoolExecutor

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

# 1. Get all calls for ticker (sorted desc)
calls = requests.get(f"{BASE}/companies/ticker/NVDA", headers=H).json()
last4 = calls["data"]["earnings_calls"][:4]

# 2. Fetch summaries in parallel
def summary(call):
    r = requests.get(f"{BASE}/transcripts/{call['id']}/summary", headers=H)
    return r.json()

with ThreadPoolExecutor(max_workers=4) as ex:
    summaries = list(ex.map(summary, last4))

for c, s in zip(last4, summaries):
    print(f"\n=== {c['event_date_time'][:10]} ===")
    print(s.get("data", {}).get("summary", "")[:300])

RAG ingest — speaker-segmented chunks

Speaker segments map 1:1 to embedding chunks. No splitting heuristics needed. The pattern below ingests 100 newest calls — repeat with cursor-based polling for ongoing sync.

Python — bulk embed into vector DB
import requests, os
from openai import OpenAI

H = {"X-API-Key": os.environ["EARNINGSCALLS_API_KEY"]}
BASE = "https://earningscalls.dev/api/v1"
client = OpenAI()

calls = requests.get(f"{BASE}/transcripts/recent?limit=100", headers=H).json()["data"]

for call in calls:
    segs = requests.get(
        f"{BASE}/speakers/{call['earnings_call_id']}",
        headers=H,
    ).json()["data"]
    for seg in segs:
        emb = client.embeddings.create(
            model="text-embedding-3-small",
            input=seg["text_content"],
        ).data[0].embedding

        upsert_to_vector_db(
            id=f"{call['earnings_call_id']}-{seg['component_order']}",
            vector=emb,
            metadata={
                "ticker": call["company_ticker"],
                "company": call["company_name"],
                "date": call["event_date_time"],
                "speaker": seg["speaker_name"],
                "role": seg["speaker_type"],  # Executives / Analysts / Operator
                "sector": call["sector"],
            },
            text=seg["text_content"],
        )

Connect Claude Desktop via MCP

Skip writing API client code entirely — connect via our MCP server and ask Claude in natural language. Generate a personal connector URL from your dashboard, then paste into claude_desktop_config.json:

claude_desktop_config.json
{
  "mcpServers": {
    "earningscalls": {
      "url": "https://earningscalls.dev/u/mct_xxx_your_token_xxx/mcp"
    }
  }
}

13 tools available: search_transcripts, count_mentions_by_ticker, get_latest_call, get_speakers, get_call_summary, list_earnings, and 7 more. Full setup guide →