CEOInterviews API
The largest verified dataset of C-suite interview transcripts on the internet. We track 20,000+ executives (CEO, CFO, COO, founders) at public and private companies spanning the S&P 500, Nasdaq, Russell, AI labs, and emerging markets, plus key politicians and policymakers. Every podcast, conference talk, and interview an executive gives is captured and turned into structured, queryable data: full transcripts, speaker-attributed dialogue, extracted quotes, and rich metadata.
The feed is signal, not noise. Every record passes a multi-stage AI pipeline backed by a human QA team before it reaches the API, so what you query is genuine first-person executive content. No duplicates, no reshares, no clipped highlight reels, no third-party commentary, no name-collision mistakes.
New: the API now also serves Substack newsletter data: daily articles from a curated registry of the most market-relevant finance and tech publications (Ray Dalio, Michael Burry, SemiAnalysis, The Bear Cave, and more), with AI-extracted, ticker-mapped company and executive mentions.
What makes the data different
- Speaker-attributed. Every appearance ships with
enhanced_transcript, a diarized transcript that labels exactly which sentences the executive said versus the host or other panelists. - Verified, not just scraped. Combined AI and human review confirms the named executive is actually on camera and speaking firsthand before a record is included.
- Deduplicated. When one interview is reposted across dozens of channels, we keep only the authoritative source and drop the rest.
- Accurately dated.
appearance_date_v2resolves when a statement was actually made, which often differs from the upload date. - Mapped to tickers and SEC CIKs. Executives join to companies, stock tickers and SEC Central Index Keys (CIK), so you can pull by person, by company name, by ticker, or by CIK, and join straight to EDGAR filings.
enhanced_transcript: who said what, down to the sentence
Most of our customers, from equity research desks to academic researchers, care most about one field: enhanced_transcript. A raw transcript blurs every speaker into one block of text. Our enhanced transcript is speaker-attributed, so you can isolate exactly what the executive said and drop the interviewer, host, and other panelists.
Each segment carries the resolved speaker_name, a speaker_id that links back to our entity database when the executive is the one talking (null for hosts and others), the timestamp, and the cleaned content.
import requests
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
# Pull an Amazon appearance and keep only the sentences Andrew Jassy said
resp = requests.get(
"https://ceointerviews.ai/api/get_feed/",
params={"entity_name": "Andrew Jassy", "page_size": 1},
headers=HEADERS,
)
item = resp.json()["results"][0]
ceo_only = [seg for seg in item["enhanced_transcript"] if seg["speaker_id"] == item["entity_id"]]
for seg in ceo_only:
print(f'[{seg["timestamp"]}] {seg["content"]}')Example enhanced_transcript from the response:
"enhanced_transcript": [
{"speaker_name": "Liz", "speaker_id": None, "timestamp": "00:02:48", "content": "What have you seen from your overall customer at Amazon?"},
{"speaker_name": "Andrew Jassy", "speaker_id": 22, "timestamp": "00:02:52", "content": "Consumers are still quite resilient. We see customers spending. For many months now, customers are aware that there is some uncertainty. They are being careful about how much they spend..."},
{"speaker_name": "Liz", "speaker_id": None, "timestamp": "00:03:51", "content": "There is a report that Amazon is building a phone that could be heavily embedded with Alexa."},
{"speaker_name": "Andrew Jassy", "speaker_id": 22, "timestamp": "00:04:00", "content": "Liz, I'm not going to comment on those rumors."}
]The raw, SRT-style transcript ships in the same response when you want the verbatim source text. Both fields come back from /api/get_feed/ for every appearance.
Let's learn about inflation from the decision makers, verified in seconds
Pull first‑person, AI and human verified remarks on a topic from specific leaders.
import requests
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
leaders = ["Jerome Powell", "Jamie Dimon"]
for name in leaders:
# Directly fetch conversations about inflation for each leader
resp = requests.get(
"https://ceointerviews.ai/api/get_feed/",
params={"entity_name": name, "keyword": "inflation", "page_size": 3},
headers=HEADERS,
)
for it in resp.json()["results"]:
# Entity identity is included in each conversation
print(f"{it['entity_name']}, {it.get('entity_title', '')} ({it.get('institution', '')})")
# Transcript metadata and excerpt
print(it.get("item_title", ""), it.get("publish_date", ""))
print(it.get("transcript", "")[:400])
print()Example output:
Jerome Powell, Chair (Federal Reserve)
FOMC Press Conference 2025-09-18T18:30:00+00:00
[excerpt] Inflation has eased but remains elevated; we’ll keep policy restrictive until we’re confident it’s moving sustainably to 2 percent.
Jamie Dimon, CEO (JPMorgan Chase)
CNBC interview: Inflation, Rates, Growth 2025-07-10T13:05:00+00:00
[excerpt] We’re preparing for rates to be higher for longer; consumers are still spending, but excess savings are running down and risks are building.Every transcript contains transparent why‑it‑passed metadata for compliance‑grade analysis and model training.
NEW: Substack newsletter data, mapped to the executives and tickers you track
Executives talk on podcasts. Investors, founders, and the sharpest independent analysts write. Increasingly, they write on Substack. Alongside our verified interview transcripts, the API now serves a structured feed of Substack newsletter articles from a curated registry of the most market-relevant publications: Ray Dalio's Principled Perspectives, Michael Burry's Cassandra Unchained, SemiAnalysis, The Bear Cave, Net Interest, Import AI, The Pomp Letter, Chamath Palihapitiya, Reid Hoffman, and thousands more across equities, credit, macro, semiconductors, AI, and crypto.
This is not RSS scraping. Every article runs through the same entity-resolution machinery as our transcripts, so newsletter content arrives already joined to the executives, companies, and stock tickers you track, queryable through one endpoint, /api/get_substack_posts/, with the same API key you already use.
What makes the newsletter data different
- Curated, ranked registry. Publications are scored and ranked for market relevance: founder-written letters, professional research shops, and the long tail of niche sector specialists. The most market-relevant publications are polled many times a day, so new articles land in the API within hours of publishing.
- Ticker-mapped mentions. An AI pass reads every article and extracts which public companies, tickers, and named executives it substantively covers, resolved against our entity database. Ask for "every newsletter article discussing NVDA this week" the same way you ask for interview transcripts.
- Author and subject resolution. When a tracked executive writes the newsletter (Dalio, Burry, Hoffman) or is the clear subject of the article, the record is anchored to their
entity_id, the same ID you use in/api/get_feed/. - Verbatim, addressable excerpts. Key passages about linked companies and executives are extracted as quote objects, verified verbatim against the article body.
- Full article text. Free posts ship with complete cleaned body text. Paywalled posts include the publisher's preview and are flagged
is_preview_onlyso your pipeline can tell them apart.
Why it matters: newsletter authors publish their theses between the disclosure cycle, often weeks before the same argument shows up in sell-side notes or the financial press. Combined with our interview transcripts, you can watch an idea move from an executive's mouth to the analyst community to the market, all keyed to the same entity and company IDs.
import requests
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
# Every newsletter article substantively covering Nvidia, newest first
resp = requests.get(
"https://ceointerviews.ai/api/get_substack_posts/",
params={"company_name": "NVDA", "page_size": 5},
headers=HEADERS,
)
for article in resp.json()["results"]:
pub = article["publication"]
tickers = [m["ticker"] for m in article["mentions"] if m["ticker"]]
print(f'{pub["name"]}: {article["item_title"]} {tickers}')Example output:
SemiAnalysis: Cerebras's Next Generation CS-4: Fast Just Got Faster ['NVDA', 'AMD', 'TSM']
Fabricated Knowledge: The State of the Semiconductor Cycle ['NVDA', 'MU', 'AVGO']
The Chip Letter: Blackwell, Rubin and the Inference Buildout ['NVDA', 'TSM']Full parameter and response documentation: GET /api/get_substack_posts/. Substack access is governed by your API plan or enterprise data license; contact us if your key does not yet include it.
Authentication
The API is plain HTTP. Every call is a GET with your API key in the X-API-Key header and filters passed as query params; responses come back as JSON. Any HTTP client works, the examples here use Python requests. Subscribe to the API plan to get a key.
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://ceointerviews.ai"
HEADERS = {"X-API-Key": API_KEY}
# Every endpoint follows the same shape
resp = requests.get(
f"{BASE_URL}/api/get_feed/",
params={"entity_name": "elon musk", "page_size": 10},
headers=HEADERS,
)
resp.raise_for_status()
data = resp.json() # {"results": [...], "page_has_next": ..., "page_num": ...}
results = data["results"]
Get API access now
Quick Start: High‑quality transcripts for an entity
Fetch an entity's conversations and read the transcript plus AI reasons/scores that justify quality.
# 1) Fetch high‑quality conversations directly using entity_name
resp = requests.get(
f"{BASE_URL}/api/get_feed/",
params={"entity_name": "elon musk", "page_size": 10},
headers=HEADERS,
)
feed = resp.json()["results"]
# 2) Inspect transcript and AI review metadata
item = feed[0]
print(f"Transcript Title: {item['item_title']}")
print(f"Length of transcript: {len(item['transcript'])} words")
# Quality reasons/scores live in the extra dict (we expose many; two important ones below)
extra = item.get("extra", {})
print(f"Transcript detection reason: {extra.get('transcript_first_person')}")
print(f"Transcript detection score: {extra.get('transcript_first_person_score')}")Example output:
Transcript Title: Elon Musk on Q4 Autonomy Progress: 2025 Earnings Call Length of transcript: 23489 words Transcript detection reason: First-person speech confirmed via pronoun usage and diarization; original audio, no dubbing Transcript detection score: 0.97
Core Concepts
The CEOInterviews API is built around several key resources:
Entities
An Entity represents an influential individual such as a CEO, politician, or executive. Each entity has an ID, name, title, and associated organization information.
Feed Items (transcripts of public appearances and metadata)
Transcribed interviews, podcasts, and appearances where entities have spoken. Each feed item includes a title, the raw transcript, the speaker-attributed enhanced_transcript, attached Quote objects extracted from the transcript, and rich metadata such as publish dates, source links, and quality review scores.
Quotes
Notable statements extracted from feed items. These can be filtered by attributes like "controversial" or "financial policy." Each Quote is attached to both an Entity and a Feed Item.
Companies
Organizations associated with entities, including both public companies (with tickers) and private institutions. Each Company has a list of Entities linked to it, usually the C-suite. Companies that file with the SEC also carry their 10-digit SEC CIK in the cik field.
Common Workflows
Company Conversations
# Fetch conversations from Microsoft executives directly
resp = requests.get(
f"{BASE_URL}/api/get_feed/",
params={"company_name": "MSFT", "page_size": 25},
headers=HEADERS,
)
for convo in resp.json()["results"]:
print(f"Video/Transcript Title: {convo['item_title']}")
print(f"Microsoft Executive Name: {convo.get('entity_name')} ({convo.get('entity_title')})")
print("---")
Example output:
Video/Transcript Title: Satya Nadella on AI Cloud Economics and Azure Earnings Highlights
Microsoft Executive Name: Satya Nadella (Chairman & CEO)
---
Video/Transcript Title: Amy Hood Breaks Down AI CapEx and Margins Q&A Session
Microsoft Executive Name: Amy Hood (EVP & CFO)
---
Video/Transcript Title: Kevin Scott on Copilot Roadmap and Inference Efficiency
Microsoft Executive Name: Kevin Scott (CTO & EVP, AI)
---
Note: Each conversation object returned by get_feed contains not only conversation/transcript data, but also the associated entity metadata, such as entity_name, entity_title, and institution, making it easy to display context alongside conversation content.
Quotes
Search quotes for an entity and stack filters like is_controversial and keyword search.
# Get controversial quotes from Elon Musk about bitcoin
resp = requests.get(
f"{BASE_URL}/api/get_quotes/",
params={
"entity_name": "elon musk",
"keyword": "bitcoin",
"is_controversial": "true",
"page_size": 25,
},
headers=HEADERS,
)
for q in resp.json()["results"]:
print(f"{q['text'][:80]}...")Unscripted commentary only (event_type)
Every conversation carries an event_type. Pass a comma-separated list to keep only podcasts and conference sessions, or list the other four values to exclude earnings calls.
# Podcasts and conference sessions from Nvidia executives, newest first
resp = requests.get(
f"{BASE_URL}/api/get_feed/",
params={"company_name": "NVDA", "event_type": "podcast,conference", "page_size": 25},
headers=HEADERS,
)
for convo in resp.json()["results"]:
print(f"{convo['event_type']:16} {convo['publish_date'][:10]} {convo['item_title']}")Endpoints
Returns feed data for "conversations" (such as podcasts or interviews) for an entity, company, or keyword. All parameters are optional. If no filters are provided, returns all conversations ordered by recency.
| Parameter | Type | Description |
|---|---|---|
| entity_name | string | Optional. Search by entity name (e.g. "elon musk", "jerome powell") |
| company_name | string | Optional. Search by company name or ticker (e.g. "tesla", "TSLA", "JPMorgan") |
| keyword | string | Optional. Search conversation data by keywords. Returns conversations where your keyword has been spoken in the dialogue or where the keyword is in the title. Results are relevance ranked, and only conversations that actually contain the keyword are returned, so a keyword with no matches returns an empty list rather than loosely related rows. Relevance ranking is depth limited to 5,000 conversations per query. To walk a larger match set exhaustively, page through it in date windows with filter_before_dt and filter_after_dt, which bound the ranking rather than truncating it. |
| entity_id | integer | Optional. Fetch all recent conversations of a specific entity by ID. Get this ID from /api/get_entities. Alternative to entity_name (takes precedence if both provided). |
| company_id | integer | Optional. Filter for conversations where the key speaker is an executive or leader from the company at company_id. Get this ID using /api/get_companies. Alternative to company_name (takes precedence if both provided). |
| ticker | string | Optional. Strict filter by exact stock ticker, e.g. CAT, BRK.B. Unlike company_name it never falls back to a partial name match; unknown tickers return 404. company_id takes precedence if both provided. |
| cik | string | Optional. Strict filter by SEC Central Index Key, with or without leading zeros, e.g. 0000320193 or 320193. Pass the institution_cik from a feed item or the cik from /api/get_companies. Unknown CIKs return 404. Anything that is not a number returns 400, including null, so check for a missing CIK before you send it. Companies that do not file with the SEC have no CIK and cannot be queried this way. company_id takes precedence if both provided. |
| filter_before_dt | string | Optional. Only return conversations published on or before this date. ISO 8601 format (e.g. 2025-06-01 or 2025-06-01T00:00:00+00:00). |
| filter_after_dt | string | Optional. Only return conversations published on or after this date. ISO 8601 format. Combine with filter_before_dt to query a closed date window. |
| filter_updated_after_dt | string | Optional. Only return conversations whose update_time is at or after this instant. ISO 8601 format. Built for incremental syncs: watermark on the max update_time you've seen, then poll with this parameter to pick up new rows and re-saved rows (engagement refreshes, transcript edits, speaker re-attribution) in one pass. Combine with include_deleted=true to also observe source-deleted tombstones. |
| include_deleted | boolean | Optional. Default false. When true, conversations whose source video/podcast has since been removed from the source platform are included instead of filtered out; such rows carry is_deleted_on_source: true and act as soft-delete tombstones (the transcript remains in our archive). Use with filter_updated_after_dt so your sync sees deletions rather than rows silently disappearing. |
| event_type | string | Optional. Only return conversations of these kinds. Comma-separated subset of earnings_call, conference, podcast, news_appearance, other. For example event_type=podcast,conference keeps unscripted executive commentary, and listing the other four excludes earnings calls. Unknown values return 400. |
| page_num | integer | Optional. Page number for pagination (default: 1) |
| page_size | integer | Optional. Number of results per page (default: 10, max: 500). |
| before_feed_item_id | integer | Optional. For keyset-style pagination. Only return feed items with IDs less than this value. Use last_seen_id from the previous response. Cannot be combined with keyword. |
Responses include transcript text and AI review metadata (e.g. transcript_first_person, transcript_first_person_score under review/extra).
With the power of the /api/get_feed API, you can search all of Elon Musk's recent podcast appearances or
Jerome Powell's interviews by first fetching their entity_id from the /api/get_entities endpoint and
then using it here. Paginate through the entire feed and use as needed. Each conversation object contains the entire
transcript of the convo, the quotes extracted from it, and other important metadata like publish_date.
Simple usage
Get Donald Trump's conversation feed and page through it.
# Directly fetch Trump's conversations using entity_name
resp = requests.get(
f"{BASE_URL}/api/get_feed/",
params={"entity_name": "donald trump"},
headers=HEADERS,
).json()
if resp["page_has_next"]:
page_2 = requests.get(
f"{BASE_URL}/api/get_feed/",
params={"entity_name": "donald trump", "page_num": 2},
headers=HEADERS,
).json()Get Donald Trump's conversations where "israel" is mentioned.
resp = requests.get(
f"{BASE_URL}/api/get_feed/",
params={"entity_name": "donald trump", "keyword": "israel"},
headers=HEADERS,
).json()
print(f"Num results: {len(resp['results'])}")Response Format
Every endpoint returns a JSON object: a top-level schema_version (currently 1.5.0; we bump it whenever the API changes substantially, so pin or log it in your sync), the matching rows under results, plus pagination fields page_has_previous, page_has_next, and page_num. get_feed and get_quotes also include last_seen_id for keyset pagination, and get_quotes adds num_results.
# resp.json() returns a dict:
{
"results": [
{
"item_title": "Trump points finger at Newsom for CA wildfires: 'He is the blame for this'",
"transcript": """[\"1\\n00:00:0,733 --> 00:00:4,870\\nPAST THE BURNED OUT HOME AFTER\", \"2\\n00:00:2,702 --> 00:00:4,870\\n \", \"3\\n00:00:2,769 --> 00:00:6,739\\nBURNED OUT HOMES. THIS DISASTER\""",
"enhanced_transcript": [
{"speaker_name": "Reporter", "speaker_id": None, "timestamp": "00:00:02", "start_seconds": 2, "content": "Mr. President, what is your reaction to the wildfires devastating California right now?"},
{"speaker_name": "Donald Trump", "speaker_id": 5099, "timestamp": "00:02:41", "start_seconds": 161, "content": "He is the blame for this. They were warned, the water was there, and the state refused to use it."},
],
"language": "en",
"word_count": 1893,
"categories": ["California politics", "wildfires"],
"is_explicit": False,
"event_type": "news_appearance",
"participants": [
{"name": "Donald Trump", "entity_id": 5099, "is_primary": True, "is_speaker_in_transcript": True},
{"name": "Reporter", "entity_id": None, "is_primary": False, "is_speaker_in_transcript": True},
],
"source_url": "https://www.youtube.com/watch?v=P5iLxW9AoMU",
"entity_name": "Donald Trump",
"institution": "Trump Media & Technology Group",
"entity_title": "45th U.S. President",
"thumbnail_image_url": "https://i.ytimg.com/vi_webp/P5iLxW9AoMU/maxresdefault.webp",
"is_politician": True,
"is_company_leader": False,
"publish_date": "2025-01-09T07:48:59+00:00",
"entity_img": "https://ceointerviews.ai/static/images/entity_pfps/5099.png",
"feed_item_id": 58827,
"entity_id": 5099,
"company_id": 1289,
"institution_ticker": "DJT",
"institution_cik": "0001849635",
"share_link": "https://ceointerviews.ai/interview/1163320/",
"appearance_date_v2": "2025-01-08",
"server_created_at": "2025-01-10T04:22:11+00:00",
"update_time": "2025-02-03T09:15:47+00:00",
"is_deleted_on_source": False,
"quotes": [
{
"quote_pk": 91230,
"quote_text": "He is the blame for this...",
"source_url": "https://www.youtube.com/watch?v=P5iLxW9AoMU",
"source_title": "Trump points finger at Newsom for CA wildfires",
"is_notable": True,
"is_controversial": True,
"is_financial_policy": False,
"source_created_at": "2025-01-09T07:48:59+00:00",
"timestamp_from_tx": "00:02:41",
"timestamp_seconds": 161,
"timestamp_end_from_tx": "00:03:05",
"timestamp_end_seconds": 185,
"topics": ["wildfires", "California politics"],
"entities": ["Gavin Newsom"],
"companies": [],
"quote_url": "/quote/trump-newsom-wildfires-91230/",
"interview_url": "/interview/91007/",
},
],
"extra": {
"transcript_first_person": "First-person speech confirmed via diarization",
"transcript_first_person_score": 0.97,
"view_count": 182034,
"like_count": 4101,
"duration_secs": 312,
"channel_name": "Fox News",
},
},
# ... more results here ...
# ...
],
"schema_version": "1.5.0",
"page_has_previous": False,
"page_has_next": False,
"page_num": 1,
"last_seen_id": 58827,
}Response Params
| Parameter | Type | Description |
|---|---|---|
| feed_item_id | integer | Unique ID representing this conversation (or feed item). |
| company_id | integer | The company ID of the company that the key entity which is associated with this feed item (transcript) |
| entity_id | integer | Entity ID of the key entity that is speaking in the conversation. |
| source_url | string | Original URL of the underlying conversation. Can be a Youtube or Podcast link. |
| item_title | string | Title of the conversation, this can be a YouTube video title or podcast episode title. |
| transcript | string | The raw string transcript of the conversation. Extracted from the underlying podcast or video. Often SRT formatted (numbered blocks with HH:MM:SS,mmm --> HH:MM:SS,mmm timing lines). |
| enhanced_transcript | array | AI-enhanced, speaker-attributed transcript. An ordered list of segment objects, each with speaker_name (the resolved speaker, such as the linked entity, host, interviewer, analyst, or co-panelist), speaker_id (the CEOInterviews entity_id when that segment is spoken by the linked entity, otherwise null), timestamp (the HH:MM:SS start time of the segment), start_seconds (the same start time as a numeric offset in seconds from the start of the media, null if unparseable), and content (the cleaned, consolidated text of that speaker turn). Consecutive captions from the same speaker are merged into a single segment, so the speaker diarization and the dialogue read cleanly.
Host vs. guest / "exec-only" filtering: segments where speaker_id == entity_id are spoken by the verified tracked executive (the person being interviewed); segments with speaker_id: null are hosts, interviewers, co-panelists, narrators, or ad reads. Filtering to speaker_id == entity_id isolates exactly what the executive said. |
| quotes | array | Quote objects extracted from this conversation. Each contains quote_pk, quote_text, source_url, source_title, is_notable, is_controversial, is_financial_policy, source_created_at, timestamp_from_tx, timestamp_seconds (numeric seconds-from-start variant of timestamp_from_tx, null if unparseable), timestamp_end_from_tx / timestamp_end_seconds (offset of the last caption segment in the quote, so the quote spans start..end; null on quotes extracted before September 2026), topics, entities, companies, quote_url, and interview_url (the parent interview's full speaker-attributed transcript page). The same quotes are queryable directly via /api/get_quotes using feed_item_id. |
| language | string | Language code of the conversation's transcript when explicitly recorded (e.g. en), otherwise null. Most rows are null today; treat null as unknown, not non-English. The corpus is predominantly English and non-English content is gated/translated at ingest. |
| word_count | integer | Approximate spoken-word count of the transcript, so you can sanity-check a row without parsing every segment. Counted from the speaker-attributed enhanced_transcript when present, otherwise from the raw transcript with SRT numbering/timing lines stripped. |
| categories | array | Topic and company tags for the conversation, rolled up from its extracted quotes' topics and companies (deduped, normalized, sorted). E.g. ["interest rates", "monetary policy", "Tesla, Inc."]. Empty array = quotes were extracted but carried no tags; null = tagging has not run for this row yet. |
| is_explicit | boolean | True when the transcript contains strong profanity (the podcast-industry "explicit" content flag). Computed by a conservative word-boundary regex over the English-enforced enhanced transcript — business vocabulary can't false-positive. null = not yet computed (e.g. no enhanced transcript). |
| event_type | string | What kind of event the recording is. One of earnings_call, conference (industry conferences, analyst days, keynotes, fireside chats), podcast, news_appearance (TV and news interviews), or other (commencement speeches, testimony, shareholder meetings, town halls, company-produced content, and anything else). Assigned to every conversation at ingest and backfilled across the archive; null only where no enhanced transcript exists yet. The classifier's 0 to 1 confidence is returned in extra.event_type_confidence. Filter with the event_type parameter. |
| participants | array | Episode-level credits: every unique person in the conversation, deduped. Each entry has name (resolved entity name, or the verbatim diarized label such as "Host"/"Interviewer" when unresolved), entity_id (the CEOInterviews id when the person is a tracked executive, otherwise null), is_primary (true for the verified tracked executive this record is about), and is_speaker_in_transcript (whether they appear in enhanced_transcript). Includes co-appearing tracked executives from the same source video (panels/joint interviews) with resolved entity_ids, even when they anchor their own separate feed items. Order: primary first, then co-tracked executives, then remaining speakers by first appearance. |
| thumbnail_image_url | string | Main image URL representing this conversation, can be a YouTube thumbnail. |
| extra | object |
Additional metadata, including quality control fields that explain why this item passed AI quality filters, plus source engagement stats. Common keys: transcript_first_person, transcript_first_person_score, combined_classifier_reason, combined_classifier_score, appearance_date_confidence, appearance_date_granularity, appearance_date_source, duration_secs, view_count, like_count, channel_name, channel_url, channel_subscriber_count. Keys vary by source platform; treat all as optional.
|
| entity_name | string | Normalized display name of the key entity speaking in the conversation. |
| institution | string | Display name of the company or organization the key entity belongs to. |
| institution_ticker | string | Stock ticker of the entity's institution if it is publicly listed, otherwise null. |
| institution_cik | string | SEC Central Index Key (10 digits, zero-padded) of the entity's institution, or null when it does not file with the SEC. Same value as the company's cik in /api/get_companies (schema 1.4.0). |
| entity_title | string | The title (job title and institution) of the key entity that is speaking in the conversation. |
| is_politician | boolean | Boolean for is the key entity in this conversation a politician. |
| is_company_leader | boolean | Boolean for is the key entity in this conversation a company leader (defined as CEO, President, or Board). |
| entity_img | string | URL of the entity's profile picture. |
| publish_date | string | ISO 8601 timestamp of when the video/podcast was published on the source platform (e.g., YouTube upload date). |
| appearance_date_v2 | string | ISO 8601 date (YYYY-MM-DD) representing when the entity actually appeared or spoke, which may differ from the publish date. For example, a video uploaded on 2025-01-15 may contain an interview recorded on 2025-01-10. This field is AI-inferred from transcript content, video metadata, and web search. Returns null if not determined. Use this for accurate temporal analysis of when statements were made. |
| server_created_at | string | ISO 8601 timestamp of when CEOInterviews first scraped and ingested this conversation into our database. This is a server-side bookkeeping timestamp. For source-platform timing use publish_date (when the media was published) and appearance_date_v2 (when the entity actually spoke). Use this field to gauge how recently a record entered our system. |
| update_time | string | ISO 8601 timestamp of the last time this record was re-saved on our servers. Independent of server_created_at (first ingest, never changes). It moves whenever the record changes: engagement refreshes (view_count, like_count), transcript edits, speaker re-attribution, and enrichment passes that add new fields to existing records. This makes it the modified-at watermark for incremental syncs: poll with filter_updated_after_dt set to the max update_time you have already stored. Because enrichment passes also move it, treat it as a record-change timestamp, not a pure engagement-metrics refresh time. |
| share_link | string | Permalink to the hosted transcript page on ceointerviews.ai for this conversation. |
| is_deleted_on_source | boolean | True if the underlying video/podcast has since been removed from the source platform. Deleted items are excluded from feed responses by default; pass include_deleted=true to receive them as soft-delete tombstones (the transcript remains in our archive). |
| head_image / head_image_type | string | Display image URL for the item and its kind (video or image). Prefer thumbnail_image_url for thumbnails. |
| type_str | string | Internal record type discriminator. Always "Post" for feed items. |
| description | string | Source-provided description of the video/podcast when available, otherwise null. |
| last_seen_id | integer | Top-level response field (sibling of results, not inside each item). The ID of the last feed item on this page. Pass it as before_feed_item_id on your next request for keyset pagination. |
| thumbnail_advanced_review_v2, transcript_advanced_review_v2 | boolean | Quality-gate flags. The thumbnail review confirms the named executive is actually shown on screen, and the transcript review confirms the executive is the one speaking firsthand. Both are true on every feed item, because the feed only returns records that have cleared both the AI pipeline and human QA. They are surfaced so you have an explicit verification marker on each row. |
| is_speaking_thumbnail_review, is_advanced_evaluated | boolean | Deprecated. Always null. Kept only so the response shape stays backward compatible. |
How to get all Conversations from the DB
Similar to the example above of paginating through all Entities in the DB you
can do the same with Conversation objects.
all_convos = []
for page_num in range(1, 100):
resp = requests.get(
f"{BASE_URL}/api/get_feed/",
params={"page_num": page_num, "page_size": 500},
headers=HEADERS,
).json()
all_convos.extend(resp["results"])
if not resp["page_has_next"]:
break
for convo in all_convos[:5]:
print(f"Title: {convo['item_title']} publish date {convo['publish_date']}")Search company data by keyword, ticker or SEC CIK. Returns comprehensive company information including all index classifications (S&P 500, NASDAQ, startup status, geographic location, etc.). All parameters are optional. If no filters are provided, returns all companies ordered by number of conversations.
| Parameter | Type | Description |
|---|---|---|
| keyword | string | Optional. Search by company name or ticker. Exact name/ticker matches are ranked first, then partial name matches. If omitted, returns all companies ordered by number of associated conversations. Note: keyword searches return all matches in a single response and pagination is disabled. |
| ticker | string | Optional. Exact stock ticker lookup, e.g. CAT, BRK.B. Returns only that company; unknown tickers return 404. |
| cik | string | Optional. Exact SEC Central Index Key lookup, with or without leading zeros, e.g. 0000320193 or 320193. Returns only that company; unknown CIKs return 404 and non-numeric values return 400. |
| is_public_company | boolean | Optional. Filter by public status. True returns companies in the S&P 500, S&P 1500, NASDAQ 100 or Russell 3000, or listed on NASDAQ; false returns companies not in those sets. |
| is_snp500 | boolean | Optional. Filter by S&P 500 membership. |
| is_nasdaq | boolean | Optional. Filter by NASDAQ listing. |
| is_snp1500 | boolean | Optional. Filter by S&P 1500 membership. |
| is_nasdaq100 | boolean | Optional. Filter by NASDAQ 100 membership. |
| is_ai_startup | boolean | Optional. Filter for AI startups. |
| is_top_startup | boolean | Optional. Filter for top startups. |
| is_usa_based | boolean | Optional. Filter for USA-based companies. |
| is_china_based | boolean | Optional. Filter for China-based companies. |
| is_europe_based | boolean | Optional. Filter for Europe-based companies. |
| is_russell3000 | boolean | Optional. Filter by Russell 3000 membership. |
| page_num | integer | Optional. Page number for pagination (default: 1) |
| page_size | integer | Optional. Number of results per page (default: 10, max: 500). |
Simple usage
Look up a company by name, ticker or SEC CIK.
resp = requests.get(
f"{BASE_URL}/api/get_companies/",
params={"keyword": "tesla"},
headers=HEADERS,
).json()
assert resp["results"][0]["ticker"] == "TSLA"Response Format
Every endpoint returns a JSON object: a top-level schema_version (currently 1.5.0; we bump it whenever the API changes substantially, so pin or log it in your sync), the matching rows under results, plus pagination fields page_has_previous, page_has_next, and page_num. get_feed and get_quotes also include last_seen_id for keyset pagination, and get_quotes adds num_results.
# resp.json() returns a dict:
{
"results": [
{
"ticker": "TSLA",
"cik": "0001318605",
"name": "Tesla, Inc.",
"normalized_name": "Tesla",
"logo_url": "https://ceointerviews.ai/static/images/company_logos/TSLA.png",
"company_id": 51,
"is_snp500": True,
"is_nasdaq": True,
"is_snp1500": True,
"is_nasdaq100": True,
"is_ai_startup": False,
"is_top_startup": False,
"is_usa_based": True,
"is_china_based": False,
"is_europe_based": False,
"is_russell3000": True
},
# ... more results here ...
# ...
],
"page_has_previous": False,
"page_has_next": False,
"page_num": 1,
}Response Params
| Parameter | Type | Description |
|---|---|---|
| ticker | string | Stock ticker for the company if it's publicly listed. Private companies, non-corporate institutions, or other entities will not have this value set. |
| cik | string | SEC Central Index Key: the 10-digit, zero-padded ID the SEC assigns to every EDGAR filer (e.g. 0001318605 for Tesla). Use it to join our data to SEC filings and other CIK-keyed datasets. null when the company does not file with the SEC, such as private companies and most non-US local listings. We follow the SEC's current mapping, so a spin-off or holding-company reorganization that creates a new registrant updates this value. |
| name | string | Official name of the institution or company. |
| normalized_name | string | Instead of the official name, such as "Adobe Inc." this field will contain the simplified name, such as "Adobe". |
| logo_url | string | Logo URL for the institution or company. |
| company_id | integer | Unique ID for this institution or company. Save this ID to use later for fetching
conversation data for executives or leaders at this company at /api/get_feed |
| is_snp500 | boolean | Whether the company is part of the S&P 500 index. |
| is_nasdaq | boolean | Whether the company's shares (or ADRs) are listed on the NASDAQ exchange, per the SEC's exchange data. Covers all NASDAQ listings, not just the NASDAQ 100. |
| is_snp1500 | boolean | Whether the company is part of the S&P 1500 index. |
| is_nasdaq100 | boolean | Whether the company is currently a NASDAQ 100 constituent. |
| is_ai_startup | boolean | Whether the company is classified as an AI startup. |
| is_top_startup | boolean | Whether the company is classified as a top startup. |
| is_usa_based | boolean | Whether the company is based in the USA. |
| is_china_based | boolean | Whether the company is based in China. |
| is_europe_based | boolean | Whether the company is based in Europe. |
| is_russell3000 | boolean | Whether the company is currently a Russell 3000 constituent. |
Entity is a key concept in the CEOInterviews API. It represents a powerful individual, politician, CEO, or
company executive. This endpoint returns Entity metadata based on search params.
Search for entities via keyword. Keywords are fuzzy-matched across the entity's name, title, company name, and stock ticker, and you can combine terms in any order. All parameters are optional. If no filters are provided, returns all entities ordered by number of conversations.
id's returned in the response here are important. You will use them to fetch conversation dialogue data later.
| Parameter | Type | Description |
|---|---|---|
| keyword | string | Optional. Fuzzy search across the entity's name, title, company name, and stock ticker.
Supports partial names, tickers, and multi-term queries in any order. Not case sensitive.
If omitted, returns all entities ordered by number of associated conversations.
Examples: ["Tim Cook", "Elon", "AAPL", "CEO Tesla", "devinder kumar amd", "federal reserve", "coinbase"]
|
| company_id | integer | Optional. Filter entities by company ID. Get this ID using /api/get_companies. |
| ticker | string | Optional. Strict filter by exact stock ticker, e.g. CAT, BRK.B. Returns the executives of that one company, like company_id. Unknown tickers return 404. Use this rather than keyword when you have tickers, since keyword is a fuzzy name search. |
| gender | string | Optional. Filter entities by gender. Values: M (Male), F (Female), O (Other). Not case sensitive. |
| is_company_leader | boolean | Optional. True returns only company leaders — CEOs, founders, and chairs. False returns everyone else (politicians, hosts, analysts, non-leader executives). Combine with page_size=500 and paginate to pull the full CEO universe. |
| is_public_company | boolean | Optional. Filter entities by whether their associated company is public. True returns entities attached to companies in the S&P 500, S&P 1500, NASDAQ 100 or Russell 3000, or listed on NASDAQ; false returns entities not in those sets. |
| is_snp500 | boolean | Optional. Filter by associated company's S&P 500 membership. |
| is_nasdaq | boolean | Optional. Filter by associated company's NASDAQ listing. |
| is_snp1500 | boolean | Optional. Filter by associated company's S&P 1500 membership. |
| is_nasdaq100 | boolean | Optional. Filter by associated company's NASDAQ 100 membership. |
| is_ai_startup | boolean | Optional. Filter for entities at AI startups. |
| is_top_startup | boolean | Optional. Filter for entities at top startups. |
| is_usa_based | boolean | Optional. Filter for entities at USA-based companies. |
| is_china_based | boolean | Optional. Filter for entities at China-based companies. |
| is_europe_based | boolean | Optional. Filter for entities at Europe-based companies. |
| is_russell3000 | boolean | Optional. Filter by associated company's Russell 3000 membership. |
| page_num | integer | Optional. Page number for pagination (default: 1) |
| page_size | integer | Optional. Number of results per page (default: 10, max: 500). |
Simple usage
Fetch Donald Trump's entity, or a list of McDonald's C-suite executives. A tiny helper keeps the repeated calls readable.
def get_entities(**params):
return requests.get(
f"{BASE_URL}/api/get_entities/", params=params, headers=HEADERS
).json()
trump = get_entities(keyword="donald trump")
mcdonalds_csuite_execs = get_entities(keyword="mcdonalds")This API function returns a list of results. Sometimes you need to narrow down to the precise correct entities.
# Fetch Malaysian politicians, then filter down for the Prime Minister.
malay_politicians = get_entities(keyword="malaysia")["results"]
prime_minister = [e for e in malay_politicians if "prime minister" in e["title"].lower()]
# Get Mark Zuckerberg
meta_resp = get_entities(keyword="meta")
meta_execs = meta_resp["results"]
mark_zuckerberg = [e for e in meta_execs if "zuckerberg" in e["simple_name"].lower()]
# Paginate Meta executives
if meta_resp["page_has_next"]:
page_2_meta = get_entities(keyword="meta", page_num=2)
Response Params
| Parameter | Type | Description |
|---|---|---|
| id | integer | Unique identifier for the entity. |
| name | string | Full name of the entity. |
| simple_name | string | Normalized name without titles, suffixes, or credentials. |
| title | string | Job title or position of the entity. |
| institution | string | Company or organization the entity is associated with. |
| description | string | Brief description of the entity. |
| gender | string | Gender of the entity (M/F/O) for Male/Female/Other. |
| is_top_influencer | boolean | Whether the entity is classified as a top influencer. |
| is_politician | boolean | Whether the entity is a politician. |
| is_company_leader | boolean | Whether the entity is a company leader (CEO, President, etc.). |
| profile_pic_url | string | URL to the entity's profile picture. |
| linkedin_url | string | LinkedIn profile URL of the entity when known, otherwise null. Useful as an external join key for matching against your own people directory without fuzzy name matching. |
| twitter_url | string | X.com (Twitter) profile URL of the entity when known, otherwise null. Useful as an external join key alongside linkedin_url. |
| company | object | Company info if the Entity is associated with a Company:
|
Response Format
Every endpoint returns a JSON object: a top-level schema_version (currently 1.5.0; we bump it whenever the API changes substantially, so pin or log it in your sync), the matching rows under results, plus pagination fields page_has_previous, page_has_next, and page_num. get_feed and get_quotes also include last_seen_id for keyset pagination, and get_quotes adds num_results.
# Shape returned by every endpoint (data = resp.json())
{
"schema_version": "1.5.0", # bumped on substantial API changes
"results": [...], # list of matching rows
"page_has_previous": bool,
"page_has_next": bool,
"page_num": int,
# get_feed and get_quotes also include "last_seen_id" (int)
# get_quotes also includes "num_results" (int)
}Example response.
# resp.json() returns a dict:
{
"results": [
{
"id": 120427,
"name": "David O. Sacks",
"simple_name": "David Sacks",
"title": "Founder of Craft Ventures",
"institution": "Craft Ventures",
"description": None,
"gender": "M",
"is_politician": False,
"is_company_leader": False,
"profile_pic_url": "https://ceointerviews.ai/static/images/entity_pfps/120427.png",
"linkedin_url": "https://www.linkedin.com/in/davidoliversacks/",
"twitter_url": "https://x.com/DavidSacks",
"company": {
"id": 670,
"ticker": "None-Craft Ventures",
"cik": None,
"full_name": "Craft Ventures",
"logo_url": "/static/images/company_logos/None-Craft Ventures.png",
"is_snp500": False,
"is_nasdaq": False,
"is_snp1500": False,
"is_nasdaq100": False,
"is_ai_startup": False,
"is_top_startup": True,
"is_usa_based": True,
"is_china_based": False,
"is_europe_based": False,
"is_russell3000": False
}
}
# ... more results here ...
# ...
],
"page_has_previous": False,
"page_has_next": False,
"page_num": 1,
}How to get all Entities from the DB
As a paid API customer you may be interested in saving all our entity data in your server for later use, e.g. to extract transcript data from the entities you care about.
You can do this by removing the keyword param and paginating through the entire db. See the following example code.
all_entities = []
for page_num in range(1, 500):
resp = requests.get(
f"{BASE_URL}/api/get_entities/",
params={"page_num": page_num, "page_size": 100},
headers=HEADERS,
).json()
all_entities.extend(resp["results"])
if not resp["page_has_next"]:
break
# Fetch some example feed items for the first 5 entities
for entity in all_entities[:5]:
feed = requests.get(
f"{BASE_URL}/api/get_feed/",
params={"entity_id": entity["id"]},
headers=HEADERS,
).json()
print(f"Entity: {entity['name']} has {len(feed['results'])} posts")Get notable quotes from entities with various filtering options. This endpoint allows you to access key statements made by executives, politicians, and other influential figures. All parameters are optional. If no filters are provided, returns all quotes ordered by recency.
| Parameter | Type | Description |
|---|---|---|
| entity_name | string | Optional. Search by entity name (e.g. "elon musk", "jerome powell"). Automatically resolves to entity_id internally. Recommended for AI agents. |
| company_name | string | Optional. Search by company name or ticker (e.g. "tesla", "TSLA", "JPMorgan"). Automatically resolves to company_id internally. Returns quotes from all entities at this company. Recommended for AI agents. |
| keyword | string | Optional. Search quotes by keyword. This searches within the quote text using semantic similarity. Note: keyword searches return the top 50 matches in a single response and pagination is disabled (page_has_next is always false). Narrow with entity, company, or date filters to surface more specific matches. |
| entity_id | integer | Optional. Filter quotes by entity ID. Get this ID from /api/get_entities. Alternative to entity_name (takes precedence if both provided). |
| company_id | integer | Optional. Filter quotes by company ID. Returns quotes from all entities associated with this company. Get this ID from /api/get_companies. Alternative to company_name (takes precedence if both provided). |
| ticker | string | Optional. Strict filter by exact stock ticker, e.g. CAT, BRK.B. Unlike company_name it never falls back to a partial name match; unknown tickers return 404. company_id takes precedence if both provided. |
| feed_item_id | integer | Optional. Filter quotes by the feed item it was associated with (podcast/video). Get this ID from /api/get_feed. |
| filter_before_dt | string | Optional. Only return quotes from conversations published on or before this date. ISO 8601 format (e.g. 2025-06-01 or 2025-06-01T00:00:00+00:00). |
| filter_after_dt | string | Optional. Only return quotes from conversations published on or after this date. ISO 8601 format. Combine with filter_before_dt to query a closed date window. |
| filter_updated_after_dt | string | Optional. Only return quotes whose update_time is at or after this instant. ISO 8601 format. Built for incremental syncs: same contract as get_feed. New quotes are extracted in the hours after a conversation is ingested, so a quote can appear days after its conversation's publish_date; watermark on this, not on filter_after_dt. |
| is_notable | boolean | Optional. Filter for quotes marked as notable (true/false). |
| is_controversial | boolean | Optional. Filter for quotes marked as controversial (true/false). |
| is_financial_policy | boolean | Optional. Filter for quotes related to financial policy (true/false). |
| event_type | string | Optional. Only return quotes from conversations of these kinds. Comma-separated subset of earnings_call, conference, podcast, news_appearance, other. For example event_type=podcast,conference keeps unscripted executive commentary, and listing the other four excludes earnings calls. Unknown values return 400. |
| before_quote_id | integer | Optional. For keyset-style pagination. Only return quotes with IDs less than this value. Use last_seen_id from the previous response. |
| page_num | integer | Optional. Page number for pagination (default: 1) |
| page_size | integer | Optional. Number of results per page (default: 10, max: 500). |
Simple usage
# Get controversial quotes from Elon Musk directly
resp = requests.get(
f"{BASE_URL}/api/get_quotes/",
params={"entity_name": "elon musk", "is_controversial": "true"},
headers=HEADERS,
).json()
quotes = resp["results"]Response Format
Every endpoint returns a JSON object: a top-level schema_version (currently 1.5.0; we bump it whenever the API changes substantially, so pin or log it in your sync), the matching rows under results, plus pagination fields page_has_previous, page_has_next, and page_num. get_feed and get_quotes also include last_seen_id for keyset pagination, and get_quotes adds num_results.
# resp.json() returns a dict:
{
"results": [
{
"id": 12345,
"text": "AI is the biggest risk to civilization that I've ever come across.",
"source_url": "https://www.youtube.com/watch?v=abc123",
"source_title": "Elon Musk Interview on AI Safety",
"is_notable": true,
"is_controversial": true,
"is_financial_policy": false,
"topics": ["artificial intelligence", "technology risk", "regulation"],
"entities": ["OpenAI", "Tesla"],
"companies": ["Tesla, Inc."],
"created_at": "2023-05-15T14:32:45+00:00",
"server_created_at": "2023-05-15T14:32:45+00:00",
"update_time": "2023-05-15T14:32:45+00:00",
"timestamp_from_tx": "00:15:23",
"timestamp_seconds": 923,
"timestamp_end_from_tx": "00:16:12",
"timestamp_end_seconds": 972,
"entity": {
"id": 5099,
"name": "Elon Musk",
"simple_name": "Elon Musk",
"title": "CEO of Tesla and SpaceX",
"institution": "Tesla",
"profile_pic_url": "https://ceointerviews.ai/static/images/entity_pfps/5099.png",
"is_politician": false,
"is_company_leader": true,
"company": {
"id": 51,
"ticker": "TSLA",
"cik": "0001318605",
"full_name": "Tesla, Inc.",
"logo_url": "https://ceointerviews.ai/static/images/company_logos/TSLA.png"
}
},
"feed_item": {
"id": 58827,
"title": "Elon Musk Interview on AI Safety",
"source_url": "https://www.youtube.com/watch?v=abc123",
"source_created_at": "2023-05-15T12:30:00+00:00",
"server_created_at": "2023-05-16T02:11:09+00:00",
"thumbnail_url": "https://i.ytimg.com/vi_webp/abc123/maxresdefault.webp",
"event_type": "podcast"
}
},
# ... more results here ...
# ...
],
"page_has_previous": False,
"page_has_next": True,
"page_num": 1,
"num_results": 10,
"last_seen_id": 12336,
}Response Params
| Parameter | Type | Description |
|---|---|---|
| id | integer | Unique identifier for the quote. |
| text | string | The actual quote text. |
| source_url | string | URL of the source where the quote was found. |
| source_title | string | Title of the source content. |
| is_notable | boolean | Whether the quote is marked as notable. |
| is_controversial | boolean | Whether the quote is marked as controversial. |
| is_financial_policy | boolean | Whether the quote is related to financial policy. |
| topics | array | List of topics mentioned in the quote. |
| entities | array | List of entities mentioned in the quote. |
| companies | array | List of companies mentioned in the quote. |
| created_at | string | ISO 8601 timestamp when the quote was created in our system. |
| server_created_at | string | ISO 8601 timestamp of when this quote was first scraped/ingested into our database server-side. Currently identical to created_at; provided under this name for consistency with the server_created_at field on feed items. The nested feed_item object also carries its own server_created_at (the parent conversation's ingest time). |
| update_time | string | ISO 8601 timestamp of the last time this quote record was re-saved on our servers (schema 1.3.0). Equals created_at for quotes that have never been modified. Filterable via filter_updated_after_dt. |
| timestamp_from_tx | string | Timestamp within the source content where the quote appears (HH:MM:SS offset into the video/podcast). May be null when the position could not be located. |
| timestamp_seconds | integer | The same offset as timestamp_from_tx expressed as numeric seconds from the start of the media, so no clock-string parsing is needed. null when timestamp_from_tx is null or unparseable. |
| timestamp_end_from_tx | string | HH:MM:SS offset of the last caption segment in the quote, so the quote spans timestamp_from_tx..timestamp_end_from_tx in the media and in the enhanced transcript. Use it to slice the exact transcript window a quote came from. null on quotes extracted before September 2026. |
| timestamp_end_seconds | integer | The same end offset as numeric seconds from the start of the media. null when timestamp_end_from_tx is null or unparseable. |
| last_seen_id | integer | Top-level response field (sibling of results, not inside each quote). The ID of the last quote on this page. Pass it as before_quote_id on your next request for keyset pagination. |
| entity | object | Information about the entity who made the quote. |
| feed_item | object | Information about the feed item (podcast/video) where the quote was found: id, title, source_url, source_created_at, server_created_at, thumbnail_url, and event_type (the kind of event the parent conversation is; filterable with the event_type parameter). |
How to get all Quotes from the DB
As a paid API customer you may be interested in retrieving all quotes from our database for your own analysis or application. You can do this by paginating through all quotes without specifying any filters.
all_quotes = []
for page_num in range(1, 500):
resp = requests.get(
f"{BASE_URL}/api/get_quotes/",
params={"page_num": page_num, "page_size": 100},
headers=HEADERS,
).json()
all_quotes.extend(resp["results"])
if not resp["page_has_next"]:
breakReturns Substack newsletter articles from our curated registry of the most market-relevant finance and tech publications, with AI-extracted, ticker-mapped company and executive mentions.
All parameters are optional. If no filters are provided, returns all articles ordered by recency. Substack data lives on this dedicated endpoint only: /api/get_feed/ and /api/get_quotes/ continue to return verified interview transcript data exclusively, so existing integrations are unaffected.
| Parameter | Type | Description |
|---|---|---|
| company_name | string | Optional. Company name or ticker (e.g. "nvidia", "NVDA", "JPMorgan"). Returns articles that substantively cover the company (via AI mention extraction) or are anchored to one of its executives. |
| company_id | integer | Optional. Same as company_name, by ID from /api/get_companies. Takes precedence if both provided. |
| ticker | string | Optional. Strict filter by exact stock ticker, e.g. NVDA, BRK.B. Returns the same articles as company_name for that company, but never falls back to a partial name match; unknown tickers return 404. company_id takes precedence if both provided. |
| entity_name | string | Optional. Executive name (e.g. "jensen huang"). Returns articles written by, about, or substantively mentioning the executive. |
| entity_id | integer | Optional. Same as entity_name, by ID from /api/get_entities. Takes precedence if both provided. |
| publication | string | Optional. Filter by publication name, partial match, case-insensitive (e.g. "SemiAnalysis", "bear cave"). |
| author | string | Optional. Filter by newsletter author name, partial match, case-insensitive (e.g. "dalio"). |
| keyword | string | Optional. Full-text search over article titles and body text (websearch syntax: tariffs "supply chain" -china). Results stay ordered by recency. Works standalone or combined with any other filter. |
| filter_before_dt | string | Optional. Only return articles published on or before this date. ISO 8601 format. |
| filter_after_dt | string | Optional. Only return articles published on or after this date. ISO 8601 format. Combine with filter_before_dt for a closed window. |
| filter_updated_after_dt | string | Optional. Only return articles whose update_time is at or after this instant. ISO 8601 format. Built for incremental syncs: watermark on the max update_time you've seen, then poll with this parameter to pick up new articles and re-saved ones (for example, when mention extraction adds ticker and executive links after ingest) without re-downloading the archive. |
| language | string | Optional. Language code as reported by Substack (e.g. en, en-gb). Exact match, case-insensitive. The corpus is English-only: articles Substack reports as non-English are not ingested. |
| min_word_count | integer | Optional. Only return articles whose stored text has at least this many words. Paywalled articles store the publisher's preview, so their word_count is the preview length. min_word_count=300 drops notes, announcements and stubs. |
| exclude_preview | boolean | Optional. true to drop paywalled articles (is_preview_only), for which only the preview text is available. |
| primary_only | boolean | Optional. With an entity or company filter: only return articles where that executive/company is the primary subject (a deep dive, earnings review, thesis or profile) rather than one of several topics. Roundups and link digests never carry primary mentions. |
| tier | string | Optional. Comma-separated publication tiers to keep, for example tier=1,2,3. Every publication in the registry has a tier from 1 to 5. Tier 1 is the core set, including the letters written by tracked executives and investors. Tiers 4 and 5 are the long tail. We check tiers 1 to 3 for new articles every day, tier 4 every 3 days and tier 5 every 7 days. tier=1,2,3 returns about a third of all articles. Any value outside 1 to 5 returns a 400. |
| page_num | integer | Optional. Page number for pagination (default: 1) |
| page_size | integer | Optional. Number of results per page (default: 10, max: 500). |
| before_feed_item_id | integer | Optional. For keyset-style pagination. Only return articles with IDs less than this value. Use last_seen_id from the previous response. |
Simple usage
Everything the newsletter ecosystem wrote about Tesla in the last week:
from datetime import date, timedelta
resp = requests.get(
f"{BASE_URL}/api/get_substack_posts/",
params={
"company_name": "TSLA",
"filter_after_dt": (date.today() - timedelta(days=7)).isoformat(),
"page_size": 50,
},
headers=HEADERS,
).json()
for a in resp["results"]:
print(a["publication"]["name"], "|", a["item_title"])Every article Ray Dalio has published, with full text:
resp = requests.get(
f"{BASE_URL}/api/get_substack_posts/",
params={"author": "dalio", "page_size": 25},
headers=HEADERS,
).json()
for a in resp["results"]:
print(a["item_title"], f'({a["word_count"]} words)')
print(a["article_text"][:400])Response Format
Same envelope as every other endpoint: rows under results plus page_has_previous, page_has_next, page_num, num_results, and last_seen_id for keyset pagination.
# resp.json() returns a dict:
{
"results": [
{
"feed_item_id": 1230617,
"item_title": "Cerebras's Next Generation CS-4: Fast Just Got Faster",
"description": "Wafer-scale inference economics, and what it means for the merchant GPU market.",
"article_text": "Cerebras announced the CS-4 this morning, and the headline number...",
"is_preview_only": False,
"word_count": 284,
"reported_word_count": 284,
"post_type": "newsletter",
"language": "en",
"source_url": "https://newsletter.semianalysis.com/p/cerebras-cs4",
"publish_date": "2026-08-18T14:02:11+00:00",
"server_created_at": "2026-08-19T05:41:22+00:00",
"update_time": "2026-08-19T05:44:07+00:00",
"like_count": 412,
"restack_count": 38,
"cover_image_url": "https://substackcdn.com/image/fetch/....jpg",
"publication": {
"name": "SemiAnalysis",
"url": "https://newsletter.semianalysis.com",
"author": "Dylan Patel",
"tier": 1
},
"mentions": [
{"name": "Cerebras", "kind": "company", "ticker": None, "org": None, "centrality": "primary", "company_id": 3417, "entity_id": None},
{"name": "Nvidia", "kind": "company", "ticker": "NVDA", "org": None, "centrality": "secondary", "company_id": 43, "entity_id": None},
{"name": "Jensen Huang", "kind": "executive", "ticker": None, "org": "Nvidia", "centrality": "secondary", "company_id": None, "entity_id": 64}
],
"quotes": [
{
"quote_pk": 991230,
"quote_text": "The CS-4's SRAM-first architecture sidesteps the HBM supply constraint that has gated every GPU roadmap since 2024.",
"is_notable": True,
"entities": [],
"companies": ["Cerebras"],
"linked_company_id": 3417,
"linked_entity_id": None,
"centrality": "primary"
}
],
"entity": None,
"entity_role": None
},
# ... more results ...
],
"page_has_previous": False,
"page_has_next": True,
"page_num": 1,
"num_results": 10,
"last_seen_id": 1230617,
}Response Params
| Parameter | Type | Description |
|---|---|---|
| feed_item_id | integer | Unique ID for this article. Shares the ID space with get_feed feed items. |
| item_title | string | Article headline. |
| description | string | Article subtitle and/or publisher description, when available. |
| article_text | string | Cleaned plain-text article body. Complete for free posts; for paywalled posts this is the publisher's preview (see is_preview_only). |
| is_preview_only | boolean | True when the article is behind the publication's paywall and article_text contains only the public preview. |
| word_count | integer | Word count of article_text. |
| language | string | Publication language code (e.g. en, de), when known. |
| source_url | string | Canonical URL of the article on Substack. |
| publish_date | string | ISO 8601 timestamp of when the article was published. For articles, the publish date IS the statement date; there is no separate appearance date like video content. |
| server_created_at / update_time | string | ISO 8601 timestamps of first ingest and last re-save on our servers, same semantics as get_feed. |
| like_count / restack_count | integer | Substack engagement counts (reactions and restacks) captured at ingest time. |
| cover_image_url | string | Article cover image, when the publisher set one. |
| publication | object | The newsletter this article belongs to: name, url, author (the publication's primary writer), and tier, an integer from 1 (the core set) to 5 (the deep long tail). Filter on it with the tier parameter. |
| mentions | array | AI-extracted companies and executives the article substantively covers. Each mention has name, kind (company or executive), ticker, org, centrality (primary = the article is about it, secondary = substantively discussed), and, when resolved against our database, company_id / entity_id join keys. Unresolved mentions keep name and ticker with null IDs. |
| quotes | array | Verbatim key passages about linked companies/executives, verified word-for-word against the article body. Each has quote_pk, quote_text, is_notable, entities/companies name lists, linked_company_id/linked_entity_id join keys, and centrality. |
| entity | object | The tracked executive this article is anchored to, or null. Populated when the newsletter is written by a tracked executive, or when exactly one tracked executive is the article's unambiguous primary subject. Same shape as entity objects elsewhere in the API, with a nested company when applicable. |
| entity_role | string | "author" when the anchored executive wrote the article (e.g. Ray Dalio's posts), "subject" when the article is about them, null when no entity is anchored. |
cURL and other HTTP clients
The API is language-agnostic. Send your key in the X-API-Key header and pass filters as query params. The examples below use curl; swap in your language's HTTP client the same way.
# Notable quotes about artificial intelligence
curl -G "https://ceointerviews.ai/api/get_quotes/" \
-H "X-API-Key: YOUR_API_KEY" \
--data-urlencode "keyword=artificial intelligence" \
--data-urlencode "is_notable=true" \
--data-urlencode "page_size=25"Fetch Elon Musk's conversation feed directly:
curl -G "https://ceointerviews.ai/api/get_feed/" \
-H "X-API-Key: YOUR_API_KEY" \
--data-urlencode "entity_name=elon musk" \
--data-urlencode "page_size=25"Fetch notable quotes from Elon Musk directly:
curl -G "https://ceointerviews.ai/api/get_quotes/" \
-H "X-API-Key: YOUR_API_KEY" \
--data-urlencode "entity_name=elon musk" \
--data-urlencode "is_notable=true" \
--data-urlencode "page_size=25"Or if you need entity metadata first, fetch entity data for Elon Musk:
curl -G "https://ceointerviews.ai/api/get_entities/" \
-H "X-API-Key: YOUR_API_KEY" \
--data-urlencode "keyword=elon musk"Fetch Substack newsletter articles covering Nvidia:
curl -G "https://ceointerviews.ai/api/get_substack_posts/" \
-H "X-API-Key: YOUR_API_KEY" \
--data-urlencode "company_name=NVDA" \
--data-urlencode "page_size=25"Rate Limits
Free Tier
- • There is no free tier available, API access is for paid customers only.
API Tier
- • 1000 requests per minute
- • 30,000 transcripts (
get_feedrows) per month per key, resetting on the day of the month the key was created - • Comprehensive access to our DB. We offer API users an endpoint to paginate
through all
Entityand conversation dialogue data. - • Get our API access now at the API Settings Page.
Custom Tier: please schedule a call
- • Unlimited requests
- • Custom rate limits based on needs
- • The CEOInterviews team will manually deliver the data you need to your application or servers.
- • Discuss pricing in a video call with our founder click here.
Error Handling
The API uses standard HTTP response codes:
- • 200: Success
- • 400: Bad Request
- • 401: Unauthorized
- • 403: Forbidden (Rate limit exceeded)
- • 404: Not Found
- • 500: Internal Server Error