Creator intelligence and data analytics for Substack, Bluesky, Medium, podcasts and Snapchat

Subalytics API Documentation

Build powerful tools and reports with our creator, content and podcast APIs
Get full access

Overview

The Subalytics API is a read-only JSON API over HTTPS. It covers creators and publications across Substack, Medium and Bluesky, their subscriber history and posts, podcasts with their episodes and full transcripts, and company mentions found in Substack posts, Bluesky posts and podcasts.

EndpointReturns
/api/v1/influencers/Influencers
/api/v1/creators-historical/Creators (historical)
/api/v1/subscribers-historical/Subscribers (historical)
/api/v1/substack-posts/Substack posts
/api/v1/podcasts/Podcasts
/api/v1/podcast-episodes/Podcast episodes
/api/v1/podcast-transcripts/Podcast transcripts
/api/v1/entity-companies/Companies
/api/v1/entity-mentions/Mentions
/api/v1/entity-mention-stats/Mention stats
/api/v1/entity-trending/Trending companies
/api/v1/entity-substack-posts/Substack posts with mentions
/api/v1/entity-bluesky-posts/Bluesky posts with mentions
/api/v1/entity-podcast-episodes/Podcast episodes with mentions

All endpoints use GET, filter with query parameters, and return the same response envelope. Endpoint paths end with a trailing /.

Base URL & authentication

Base URL

https://subalytics.com/api/v1/

Authentication

Send your API key in the X-API-key header with every request:

X-API-key: <your-api-key>

Each API key is enabled for specific endpoints. A request without a valid key, or to an endpoint your key isn't enabled for, returns 401. Get access to request a key or add endpoints to an existing one.

Podcast endpoints return only the podcasts your key includes, with their episodes and transcripts. Requesting any other podcast by sub_id, or one of its episodes by episode_sub_id, returns 403.

Entity mentions is a separate product with its own access: keys for other endpoints don't include the entity-* endpoints, and the reverse.

Example request

curl -H "X-API-key: <your-api-key>" \
  "https://subalytics.com/api/v1/podcasts/?limit=5"

Pagination

Every endpoint accepts limit: default 25, maximum 100. Endpoints page in one of two ways, shown next to each endpoint.

Cursor

Responses include next_cursor. Pass it back as cursor to get the next page, and stop when next_cursor is null. Cursors are opaque strings: don't parse or build them. If the total is an exact multiple of limit, the last page comes back empty with next_cursor: null.

Podcast and entity mentions endpoints return the newest-added records first. Records added while you are paging don't appear in that walk; your next sync picks them up. The exceptions are mention stats and trending companies, which have no cursor.

Offset

Pass offset (default 0) to skip that many records. Keep increasing it by limit until a page returns fewer than limit results.

Date filters

Dates are UTC, in the format YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS.

Creator endpoints

Creators and subscribers history accept from / to, applied to the start date of each weekly record.

Podcast and entity mentions endpoints

These date filters come in <name>_from / <name>_to pairs, named after the field they filter:

ParametersEndpointField
latest_episode_from / _topodcastslatest_published_episode_utc
published_from / _topodcast-episodespublished_utc
transcribed_from / _topodcast-episodestranscribed_at
updated_from / _topodcast-transcriptsupdated_at
published_from / _toentity-mentions, entity-*-posts, entity-podcast-episodespublished_utc (when the content was published)
updated_from / _toentity-mentions, entity-*-posts, entity-podcast-episodesupdated_at (when the mention record last changed; use for sync)
period_from / _toentity-mention-stats, entity-trendingperiod_start
  • Both bounds are inclusive. A date-only *_from starts at 00:00:00; a date-only *_to runs to the end of that day, so published_from=2026-09-25&published_to=2026-09-25 returns everything published on 25 September. A *_to with a time includes that whole second, so a timestamp returned by the API can be used as either bound.
  • *_to must not be earlier than *_from.
  • These endpoints don't accept the plain from / to parameters.
  • Their responses return dates as ISO 8601 UTC with a Z suffix, e.g. 2026-09-25T05:00:00Z, the same format the filters accept.

Responses & errors

Success

Cursor-paginated endpoints:

{
  "success": true,
  "results": [ ... ],
  "returned_results": 25,
  "limit": 25,
  "next_cursor": "6a7d976dc8173839e225382f"
}

Offset-paginated endpoints return "offset" in place of "next_cursor". An empty results list is a success, not an error.

Errors

{
  "success": false,
  "error_code": 400,
  "msg": "Unknown parameters: bad_param"
}
CodeMeaningWhen
400Bad requestUnknown query parameter, invalid date or date range, invalid limit, invalid min_confidence, or invalid cursor. The message says which.
401UnauthorizedMissing API key, or the key is not enabled for this endpoint.
403ForbiddenThe request names a podcast (or one of its episodes) that your key does not include.
500Server errorSomething went wrong on our side. Retry later.

Any query parameter not listed for an endpoint is rejected with 400, so check spelling if you get Unknown parameters: ....

Creator endpoints

Influencers

GET /api/v1/influencers/ Cursor pagination

The latest weekly snapshot of each creator: platform, subscriber counts, publication details and more.

ParameterTypeDescription
sub_idstringSubalytics ID of one creator
socialstringPlatform: substack, medium or bluesky
yearintYear of the snapshot
free_subscribersintMinimum number of free subscribers
paid_subscribersintMinimum number of paid subscribers
limitintResults per page (default 25, max 100)
cursorstringnext_cursor from the previous page

Example

GET /api/v1/influencers/?social=bluesky&free_subscribers=5000

Creators (historical)

GET /api/v1/creators-historical/ Offset pagination

Weekly creator records by Subalytics ID, platform, week/year or date range.

ParameterTypeDescription
sub_idstringSubalytics ID of one creator
socialstringPlatform: substack, medium or bluesky
week_numberintISO week number (1–53)
yearintYear, e.g. 2025
fromstringStart date of the week, from (YYYY-MM-DD)
tostringStart date of the week, to (YYYY-MM-DD)
limitintResults per page (default 25, max 100)
offsetintRecords to skip (default 0)

Example

GET /api/v1/creators-historical/?sub_id=714165582b&year=2025&limit=20

Subscribers (historical)

GET /api/v1/subscribers-historical/ Offset pagination

Weekly subscriber statistics by Subalytics ID, platform and date.

ParameterTypeDescription
sub_idstringSubalytics ID of one creator
socialstringPlatform: substack, medium or bluesky
week_numberintISO week number (1–53)
yearintYear, e.g. 2025
free_subscribersintMinimum number of free subscribers
paid_subscribersintMinimum number of paid subscribers
fromstringStart date of the week, from (YYYY-MM-DD)
tostringStart date of the week, to (YYYY-MM-DD)
limitintResults per page (default 25, max 100)
offsetintRecords to skip (default 0)

Example

GET /api/v1/subscribers-historical/?sub_id=f2ac9f1545&from=2025-03-01&to=2025-05-01&limit=50

Substack posts

GET /api/v1/substack-posts/ Cursor pagination

Search posts by creator, audience and engagement. By default returns post summaries: title, URL, date, description, a truncated body and engagement counts.

ParameterTypeDescription
sub_idstringSubalytics ID of one creator
audiencestringAudience, e.g. only_paid or everyone
wordcountintMinimum word count
comment_countintMinimum number of comments
reaction_countintMinimum number of reactions
fullstringtrue = return the complete post record
limitintResults per page (default 25, max 100)
cursorstringnext_cursor from the previous page

Example

GET /api/v1/substack-posts/?sub_id=803b198749&audience=only_paid

Podcast endpoints

Podcasts

GET /api/v1/podcasts/ Cursor pagination · newest first

The podcasts tracked by Subalytics. Podcasts, episodes and transcripts are linked by stable public IDs: a podcast's sub_id (10 hex characters) and an episode's episode_sub_id (16 hex characters). They never change, so they are safe to store as foreign keys. To fetch one item, filter by its ID.

Query parameters

ParameterTypeDescription
sub_idstringOne podcast
languagestringExact match, e.g. en
categorystringPodcasts whose categories include this value, e.g. Business
latest_episode_fromdateNewest episode published on or after (see Date filters)
latest_episode_todateNewest episode published on or before
limitintResults per page (default 25, max 100)
cursorstringnext_cursor from the previous page

Response fields

FieldTypeDescription
sub_idstringPublic podcast ID
podcast_namestringPodcast name
feed_titlestring | nullTitle as published in the podcast feed (may differ from podcast_name)
authorstring | nullAuthor as published in the feed
owner_namestring | nullOwner as published in the feed
owner_emailstring | nullOwner email as published in the feed
descriptionstring | nullPodcast description
categoriesstring[]Categories
languagestring | nullLanguage declared by the feed, e.g. en
explicitstring | nullExplicit flag as declared in the feed
image_urlstring | nullPodcast artwork
website_linkstringPodcast website; only present when the feed has one
latest_published_episode_utcdate | nullPublish date of the newest episode

Example

GET /api/v1/podcasts/?category=Business&limit=1
{
  "success": true,
  "results": [
    {
      "sub_id": "a1b2c3d4e5",
      "podcast_name": "Example Business Show",
      "feed_title": "Example Business Show",
      "author": "Example Media",
      "owner_name": "Example Media",
      "owner_email": "podcast@example.com",
      "description": "Weekly conversations about business, markets and culture.",
      "categories": ["Business", "News"],
      "language": "en",
      "explicit": null,
      "image_url": "https://example.com/artwork.jpg",
      "website_link": "https://example.com/podcast",
      "latest_published_episode_utc": "2026-09-24T16:38:09Z"
    }
  ],
  "returned_results": 1,
  "limit": 1,
  "next_cursor": "6a7cdd39fdbbf69e21a9ffbe"
}

Podcast episodes

GET /api/v1/podcast-episodes/ Cursor pagination · newest first

Episodes published since Subalytics started tracking each podcast; this is not the podcast's full back catalogue. Only episodes with a transcript are returned. Every episode listed here has a transcript at /api/v1/podcast-transcripts/?episode_sub_id=<episode_sub_id>. An episode that hasn't been transcribed yet returns an empty results list.

New episodes are transcribed daily and usually appear within a few hours of 05:00 UTC. Some take longer, and a few may never appear (for example, if the audio is unavailable).

Query parameters

ParameterTypeDescription
sub_idstringEpisodes of one podcast
episode_sub_idstringOne episode
published_fromdatePublished by the podcast on or after
published_todatePublished by the podcast on or before
transcribed_fromdateBecame available in the API on or after (use for sync)
transcribed_todateBecame available in the API on or before
limitintResults per page (default 25, max 100)
cursorstringnext_cursor from the previous page

Response fields

FieldTypeDescription
episode_sub_idstringPublic episode ID
sub_idstringThe podcast it belongs to
podcast_namestringPodcast name
guidstringThe episode's ID in its podcast feed (unique within that feed only)
titlestring | nullEpisode title
descriptionstring | nullAs published in the feed, often HTML
linkstring | nullEpisode web page
image_urlstring | nullEpisode artwork, if the feed has one
audio_urlstringOriginal audio file, hosted by the podcast
duration_secondsint | nullFrom the feed; null when the feed doesn't state it
explicitstring | nullExplicit flag as declared in the feed
published_utcdateWhen the podcast published the episode
transcribed_atdateWhen the transcript became available
created_atdateWhen Subalytics first found the episode
updated_atdateWhen the episode details were last refreshed

Example

GET /api/v1/podcast-episodes/?sub_id=a1b2c3d4e5&published_from=2026-09-20&limit=1
{
  "success": true,
  "results": [
    {
      "episode_sub_id": "c41f7a09e2b85d13",
      "sub_id": "a1b2c3d4e5",
      "podcast_name": "Example Business Show",
      "guid": "088561f8-cd50-429d-963c-8f74c8193baa",
      "title": "Rate Cuts, Robotaxis and the Week in Markets",
      "description": "The panel breaks down this week's biggest business stories.",
      "link": "https://example.com/podcast/episodes/248",
      "image_url": null,
      "audio_url": "https://example.com/audio/episode-248.mp3",
      "duration_seconds": 6875,
      "explicit": null,
      "published_utc": "2026-09-24T16:38:09Z",
      "transcribed_at": "2026-09-25T06:12:47Z",
      "created_at": "2026-09-25T05:01:12Z",
      "updated_at": "2026-09-26T05:01:09Z"
    }
  ],
  "returned_results": 1,
  "limit": 1,
  "next_cursor": "6a7d976dc8173839e225382f"
}

Podcast transcripts

GET /api/v1/podcast-transcripts/ Cursor pagination · newest first

By default returns transcript summaries without speakers or text, which are cheap to page through to find what's new. Add full=true to get the complete transcript.

Query parameters

ParameterTypeDescription
episode_sub_idstringThe transcript of one episode
sub_idstringTranscripts of one podcast
fullstringtrue = include speakers and segments; anything else or omitted = summary only
updated_fromdateLast changed on or after (use for sync)
updated_todateLast changed on or before
limitintResults per page (default 25, max 100). With full=true: default 10, max 10, since a full transcript can be 1–2 MB
cursorstringnext_cursor from the previous page

Response fields

FieldTypeDescription
episode_sub_idstringThe episode this transcript belongs to
podcastobject{sub_id, podcast_name}
episodeobject{episode_sub_id, episode_title, published_at}, for convenience; the episodes endpoint is the source of truth
languagestring | nullLanguage detected from the audio, e.g. en (may differ from the podcast's declared language)
duration_secondsnumber | nullMeasured audio length
speakers_identifiedbooleanfalse = speaker names haven't been identified (yet); labels still work, names are null
created_atdateWhen the transcript was first produced
updated_atdateLast change (re-transcription or speakers identified)
speakersobjectfull=true only. Speaker label → {name, role, title, company}
segmentsarrayfull=true only. {start, end, text, speaker} in order; start/end in seconds from the start of the audio

Speakers

Each segment's speaker (e.g. SPEAKER_00) is a key into speakers. Labels are per transcript: SPEAKER_00 in one episode is unrelated to SPEAKER_00 in another. Identification is automatic and best-effort: any of name, role, title and company may be null. role is the speaker's part in the conversation (host, co-host, guest, …). A segment's speaker may be missing where speakers couldn't be separated.

A transcript can change after it is first published, for example when speaker names are filled in later. Its updated_at changes, so an updated_from sync picks it up again.

Example: one full transcript

GET /api/v1/podcast-transcripts/?episode_sub_id=c41f7a09e2b85d13&full=true
{
  "success": true,
  "results": [
    {
      "episode_sub_id": "c41f7a09e2b85d13",
      "podcast": {
        "sub_id": "a1b2c3d4e5",
        "podcast_name": "Example Business Show"
      },
      "episode": {
        "episode_sub_id": "c41f7a09e2b85d13",
        "episode_title": "Rate Cuts, Robotaxis and the Week in Markets",
        "published_at": "2026-09-24T16:38:09Z"
      },
      "language": "en",
      "duration_seconds": 6875.2,
      "speakers_identified": true,
      "speakers": {
        "SPEAKER_00": { "name": "Jane Host", "role": "host", "title": "journalist", "company": "Example Media" },
        "SPEAKER_01": { "name": "Sam Guest", "role": "guest", "title": "economist", "company": null }
      },
      "segments": [
        { "start": 1.0, "end": 9.72, "text": "Welcome back to the show. Big week in markets.", "speaker": "SPEAKER_00" },
        { "start": 10.1, "end": 17.4, "text": "It really was. Let's start with the rate decision.", "speaker": "SPEAKER_01" }
      ],
      "created_at": "2026-09-25T06:12:47Z",
      "updated_at": "2026-09-27T05:48:31Z"
    }
  ],
  "returned_results": 1,
  "limit": 10,
  "next_cursor": null
}

Without full=true, each result has the same fields except speakers and segments.

Guide: daily podcast sync

For a first load, list podcasts, then each podcast's episodes, then fetch transcripts with full=true. After that, ask only for what changed since your last sync:

1. New or changed transcripts

GET /api/v1/podcast-transcripts/?updated_from=2026-09-26T05:00:00

Returns summaries of transcripts that are new, re-transcribed, or had speaker names filled in. Fetch the ones you need with ?episode_sub_id=<id>&full=true.

2. Newly available episodes

GET /api/v1/podcast-episodes/?transcribed_from=2026-09-26T05:00:00

Filters on when an episode became available, not when it was published, so an episode published three days ago but transcribed today is included.

3. New podcasts

Re-list /api/v1/podcasts/ occasionally to pick up newly added podcasts.

Start each *_from a few minutes before your previous sync. Receiving the same record twice is harmless.

Entity mentions

About entity mentions

Entity mentions finds references to public companies in Substack posts, Bluesky posts and podcast episodes. It is a separate product from the other endpoints, with its own access, and every endpoint name starts with entity-.

Only content that mentions a company is returned. A post or episode appears only if it has at least one company mention. Podcast endpoints return only the transcript passages that contain a mention, never the full transcript.

IDs

Resources are linked by stable public IDs that never change for the same item, so they are safe to store as foreign keys:

IDIdentifiesNotes
symbolCompanyStock ticker, e.g. META
mention_idMentionOne mention of one company in one piece of content
content_idContentThe post or episode: post_id (Substack), post_uri (Bluesky) or episode_sub_id (podcasts)
sub_idCreator / podcastThe same Subalytics ID used by the creator and podcast endpoints, so mentions join to data you already have

To fetch one item, filter by its ID, e.g. /api/v1/entity-mentions/?mention_id=9f2c41d07ab35e18.

Freshness

  • Bluesky: posts usually appear within minutes.
  • Substack: posts appear after each collection run.
  • Podcasts: episodes appear once they have been transcribed and matched, usually within a few hours of the daily 05:00 UTC run.

Typical workflow

  • List monitored companies: GET /api/v1/entity-companies/. This is the only endpoint with CIK, exchange, sector and industry; the others identify a company by symbol and company_name.
  • Get mentions of a company: GET /api/v1/entity-mentions/?symbol=META&published_from=2026-09-01. One row per mention from every source, each with a snippet and a link to the content.
  • Get the content: GET /api/v1/entity-bluesky-posts/?symbol=META, or the Substack or podcast equivalent. Each post or episode lists every company it mentions in entities[].
  • Track volume: GET /api/v1/entity-mention-stats/?symbol=META&interval=day for a time series, or GET /api/v1/entity-trending/?sort=change for the companies whose mention volume moved most.

Daily sync

Ask only for what was added or changed since your last sync:

GET /api/v1/entity-mentions/?updated_from=2026-09-28T05:00:00
GET /api/v1/entity-bluesky-posts/?updated_from=2026-09-28T05:00:00

updated_from filters on when a record last changed, not when the content was published: a podcast episode published three days ago but matched today is included. Start each updated_from a few minutes before your previous sync; receiving the same record twice is harmless.

Mentions can be withdrawn

Matching improves over time and past content is re-checked. That can add mentions, change their confidence, or remove a mention that turned out to be wrong. A post whose last mention is removed disappears from the content endpoints. Removals don't show up in an updated_from sync, so re-pull a recent window (e.g. the last 30 days) periodically if you need an exact match with the API.

Companies

GET /api/v1/entity-companies/ Cursor pagination · newest first

Every company monitored for mentions; a company not listed here is never matched. This is the reference for official company data. Other entity-* endpoints give only symbol and company_name, so join on symbol to get the rest. Newly monitored companies appear first.

Query parameters

ParameterTypeDescription
symbolstringOne company, exact match, e.g. META
cikstringOne company by SEC CIK
qstringCase-insensitive search on symbol and company name, e.g. q=meta
sectorstringExact match, e.g. Technology
exchangestringExact match, e.g. NASDAQ
limitintResults per page (default 25, max 100)
cursorstringnext_cursor from the previous page

Response fields

FieldTypeDescription
symbolstringTicker
company_namestringOfficial company name
cikstring | nullSEC CIK
exchangestring | nullListing exchange
sectorstring | nullSector
industrystring | nullIndustry

Example

GET /api/v1/entity-companies/?q=meta
{
  "success": true,
  "results": [
    {
      "symbol": "META",
      "company_name": "Meta Platforms, Inc.",
      "cik": "0001326801",
      "exchange": "NASDAQ",
      "sector": "Communication Services",
      "industry": "Internet Content & Information"
    }
  ],
  "returned_results": 1,
  "limit": 25,
  "next_cursor": null
}

Mentions

GET /api/v1/entity-mentions/ Cursor pagination · newest first

One row per company mention in one piece of content, across all three sources. Filter by symbol to see who is talking about a company, or by sub_id to see which companies a creator or podcast talks about.

Query parameters

ParameterTypeDescription
mention_idstringOne mention
symbolstringMentions of one company
sourcestringsubstack, bluesky or podcast; omit for all
sub_idstringMentions by one creator or in one podcast
content_idstringMentions in one post or episode (post_id, post_uri or episode_sub_id)
match_typestringcashtag, company_name, alias, product or ticker
min_confidencenumber0.5–1.0, default 0.7
published_fromdateContent published on or after
published_todateContent published on or before
updated_fromdateMention last changed on or after (use for sync)
updated_todateMention last changed on or before
limitintResults per page (default 25, max 100)
cursorstringnext_cursor from the previous page

Response fields

FieldTypeDescription
mention_idstringPublic mention ID
symbolstringCompany ticker
company_namestringOfficial company name
sourcestringsubstack, bluesky or podcast
content_idstringID of the post or episode on its content endpoint
sub_idstringThe creator or podcast
content_urlstring | nullLink to the original post or episode page
published_utcdateWhen the content was published
match_typestringWhat was matched: cashtag, company_name, alias, product or ticker
matched_textstringThe text as it appeared in the content
confidencenumber0–1
snippetstringThe sentence or passage around the mention
fieldstring | nullPosts only: where the match was found
start, endnumber | nullPodcasts only: seconds from the start of the audio
created_atdateWhen the mention was first found
updated_atdateWhen the mention last changed (e.g. re-scored)

Example

GET /api/v1/entity-mentions/?symbol=META&published_from=2026-09-20&limit=2
{
  "success": true,
  "results": [
    {
      "mention_id": "9f2c41d07ab35e18",
      "symbol": "META",
      "company_name": "Meta Platforms, Inc.",
      "source": "bluesky",
      "content_id": "at://did:plc:exampleaccount000000000/app.bsky.feed.post/3lqz7x2kfbc2s",
      "sub_id": "e81a3f90c2",
      "content_url": "https://bsky.app/profile/did:plc:exampleaccount000000000/post/3lqz7x2kfbc2s",
      "published_utc": "2026-09-27T14:02:11Z",
      "match_type": "cashtag",
      "matched_text": "$META",
      "confidence": 0.95,
      "snippet": "$META up 4% premarket after ad revenue beat.",
      "field": "text",
      "start": null,
      "end": null,
      "created_at": "2026-09-27T14:03:40Z",
      "updated_at": "2026-09-27T14:03:40Z"
    },
    {
      "mention_id": "c07e5b1a94d2f836",
      "symbol": "META",
      "company_name": "Meta Platforms, Inc.",
      "source": "podcast",
      "content_id": "c41f7a09e2b85d13",
      "sub_id": "a1b2c3d4e5",
      "content_url": "https://example.com/podcast/episodes/248",
      "published_utc": "2026-09-24T16:38:09Z",
      "match_type": "product",
      "matched_text": "instagram",
      "confidence": 0.78,
      "snippet": "and instagram is basically where every small brand does its marketing now",
      "field": null,
      "start": 1834.2,
      "end": 1841.9,
      "created_at": "2026-09-25T07:15:03Z",
      "updated_at": "2026-09-25T07:15:03Z"
    }
  ],
  "returned_results": 2,
  "limit": 2,
  "next_cursor": "6a7d9a11c8173839e2253ae4"
}

Mention stats

GET /api/v1/entity-mention-stats/ No pagination · up to 100 periods

Mention counts for one company over time: one row per day or week, oldest first. Counts include only mentions with confidence of 0.7 or above. Periods with no mentions are returned with zero counts, so a series has no gaps. To rank companies against each other, use trending companies.

This endpoint doesn't use cursors: limit caps the number of periods and next_cursor is always null. To cover a longer range, use interval=week or split the range into several requests.

Query parameters

ParameterTypeDescription
symbolstringRequired. The company
sourcestringsubstack, bluesky or podcast; omit for all sources combined
intervalstringday (default) or week (ISO weeks, starting Monday)
period_fromdateFirst period returned. Default: 30 days before today
period_todateLast period returned. Default: yesterday (the last full day)
limitintPeriods returned (default 100, max 100)

Response fields

FieldTypeDescription
symbolstringCompany ticker
company_namestringOfficial company name
period_startdateStart of the day or week
intervalstringday or week
mention_countintegerMentions in the period
content_countintegerDistinct posts or episodes with at least one mention
creator_countintegerDistinct creators and podcasts (sub_id)
by_sourceobject{substack, bluesky, podcast} mention counts; only when source is omitted

Example

GET /api/v1/entity-mention-stats/?symbol=META&period_from=2026-09-25&period_to=2026-09-27
{
  "success": true,
  "results": [
    { "symbol": "META", "company_name": "Meta Platforms, Inc.", "period_start": "2026-09-25T00:00:00Z", "interval": "day", "mention_count": 41, "content_count": 37, "creator_count": 33, "by_source": { "substack": 3, "bluesky": 36, "podcast": 2 } },
    { "symbol": "META", "company_name": "Meta Platforms, Inc.", "period_start": "2026-09-26T00:00:00Z", "interval": "day", "mention_count": 58, "content_count": 51, "creator_count": 44, "by_source": { "substack": 5, "bluesky": 52, "podcast": 1 } },
    { "symbol": "META", "company_name": "Meta Platforms, Inc.", "period_start": "2026-09-27T00:00:00Z", "interval": "day", "mention_count": 132, "content_count": 118, "creator_count": 97, "by_source": { "substack": 4, "bluesky": 125, "podcast": 3 } }
  ],
  "returned_results": 3,
  "limit": 100,
  "next_cursor": null
}

Substack posts with mentions

GET /api/v1/entity-substack-posts/ Cursor pagination · newest first

Substack posts that mention at least one monitored company, each with its entities[]. This is separate from /api/v1/substack-posts/, which returns all posts. Post bodies are not returned: only the title, subtitle, description and the mention snippets.

Query parameters

ParameterTypeDescription
post_idstringOne post
sub_idstringPosts by one creator
symbolstringPosts mentioning one company
min_confidencenumber0.5–1.0, default 0.7
published_fromdatePublished on or after
published_todatePublished on or before
updated_fromdateMentions last changed on or after (use for sync)
updated_todateMentions last changed on or before
limitintResults per page (default 25, max 100)
cursorstringnext_cursor from the previous page

Response fields

FieldTypeDescription
post_idstringPublic post ID; content_id in entity-mentions
sub_idstringThe creator
author_namestring | nullAuthor
titlestring | nullPost title
subtitlestring | nullPost subtitle
descriptionstring | nullPost description
canonical_urlstringLink to the post
published_utcdateWhen the post was published
entitiesarrayCompany mentions: mention_id, symbol, company_name, match_type, matched_text, confidence, snippet, field
created_atdateWhen the post first appeared in this product
updated_atdateWhen its mentions last changed

Example

GET /api/v1/entity-substack-posts/?symbol=META&limit=1
{
  "success": true,
  "results": [
    {
      "post_id": "173920451",
      "sub_id": "803b198749",
      "author_name": "Jane Writer",
      "title": "The Ad Market Reset",
      "subtitle": "Instagram now drives more ad revenue than the core app.",
      "description": "Why Meta and Google are fighting over the same small advertisers.",
      "canonical_url": "https://newsletter.example.com/p/the-ad-market-reset",
      "published_utc": "2026-09-20T11:00:00Z",
      "entities": [
        {
          "mention_id": "51b8e2d7f03a9c64",
          "symbol": "META",
          "company_name": "Meta Platforms, Inc.",
          "match_type": "product",
          "matched_text": "Instagram",
          "confidence": 0.78,
          "snippet": "Instagram now drives more ad revenue than the core app.",
          "field": "subtitle"
        },
        {
          "mention_id": "a3d90c6e18f2b475",
          "symbol": "GOOGL",
          "company_name": "Alphabet Inc.",
          "match_type": "alias",
          "matched_text": "Google",
          "confidence": 0.84,
          "snippet": "Why Meta and Google are fighting over the same small advertisers.",
          "field": "description"
        }
      ],
      "created_at": "2026-09-20T12:31:17Z",
      "updated_at": "2026-09-20T12:31:17Z"
    }
  ],
  "returned_results": 1,
  "limit": 1,
  "next_cursor": "6a7d9b02c8173839e2253b10"
}

Bluesky posts with mentions

GET /api/v1/entity-bluesky-posts/ Cursor pagination · newest first

Bluesky posts that mention at least one monitored company, each with its entities[]. Only top-level posts from tracked Bluesky accounts are included, not replies. Bluesky posts are short and public, so the full text is returned.

Query parameters

ParameterTypeDescription
post_uristringOne post
sub_idstringPosts by one creator
didstringPosts by one Bluesky account (DID)
symbolstringPosts mentioning one company
min_confidencenumber0.5–1.0, default 0.7
published_fromdatePublished on or after
published_todatePublished on or before
updated_fromdateMentions last changed on or after (use for sync)
updated_todateMentions last changed on or before
limitintResults per page (default 25, max 100)
cursorstringnext_cursor from the previous page

Response fields

FieldTypeDescription
post_uristringPublic post ID (AT Protocol URI); content_id in entity-mentions
sub_idstringThe creator
didstringThe Bluesky account
urlstringLink to the post on bsky.app
textstringFull post text
published_utcdateWhen the post was created
entitiesarrayCompany mentions: mention_id, symbol, company_name, match_type, matched_text, confidence, snippet, field
created_atdateWhen the post first appeared in this product
updated_atdateWhen its mentions last changed

Example

GET /api/v1/entity-bluesky-posts/?symbol=META&min_confidence=0.9&limit=1
{
  "success": true,
  "results": [
    {
      "post_uri": "at://did:plc:exampleaccount000000000/app.bsky.feed.post/3lqz7x2kfbc2s",
      "sub_id": "e81a3f90c2",
      "did": "did:plc:exampleaccount000000000",
      "url": "https://bsky.app/profile/did:plc:exampleaccount000000000/post/3lqz7x2kfbc2s",
      "text": "$META up 4% premarket after ad revenue beat. Instagram Reels doing the heavy lifting again.",
      "published_utc": "2026-09-27T14:02:11Z",
      "entities": [
        {
          "mention_id": "9f2c41d07ab35e18",
          "symbol": "META",
          "company_name": "Meta Platforms, Inc.",
          "match_type": "cashtag",
          "matched_text": "$META",
          "confidence": 0.95,
          "snippet": "$META up 4% premarket after ad revenue beat.",
          "field": "text"
        }
      ],
      "created_at": "2026-09-27T14:03:40Z",
      "updated_at": "2026-09-27T14:03:40Z"
    }
  ],
  "returned_results": 1,
  "limit": 1,
  "next_cursor": "6a7d9c77c8173839e2253c21"
}

The post also mentions "Instagram" below 0.9, so that entry is left out of entities[] because of min_confidence=0.9.

Podcast episodes with mentions

GET /api/v1/entity-podcast-episodes/ Cursor pagination · newest first

Podcast episodes that mention at least one monitored company, each with its entities[]. Each entity includes the transcript passage around the mention (snippet), its timestamps (start / end) and the speaker. The full transcript is not part of this product; with access to the podcast endpoints you can fetch it from /api/v1/podcast-transcripts/ with ?episode_sub_id=<episode_sub_id>&full=true.

Transcripts are automatic speech recognition output, so snippets are lower-case and loosely punctuated, and company names are occasionally misheard.

Query parameters

ParameterTypeDescription
episode_sub_idstringOne episode
sub_idstringEpisodes of one podcast
symbolstringEpisodes mentioning one company
min_confidencenumber0.5–1.0, default 0.7
published_fromdatePublished on or after
published_todatePublished on or before
updated_fromdateMentions last changed on or after (use for sync)
updated_todateMentions last changed on or before
limitintResults per page (default 25, max 100)
cursorstringnext_cursor from the previous page

Response fields

FieldTypeDescription
episode_sub_idstringPublic episode ID, same as on podcast-episodes; content_id in entity-mentions
sub_idstringThe podcast
podcast_namestringPodcast name
episode_titlestring | nullEpisode title
linkstring | nullEpisode web page
published_utcdateWhen the episode was published
entitiesarrayCompany mentions: mention_id, symbol, company_name, match_type, matched_text, confidence, snippet, start, end, speaker
created_atdateWhen the episode first appeared in this product
updated_atdateWhen its mentions last changed

Example

GET /api/v1/entity-podcast-episodes/?symbol=META&limit=1
{
  "success": true,
  "results": [
    {
      "episode_sub_id": "c41f7a09e2b85d13",
      "sub_id": "a1b2c3d4e5",
      "podcast_name": "Example Business Show",
      "episode_title": "Rate Cuts, Robotaxis and the Week in Markets",
      "link": "https://example.com/podcast/episodes/248",
      "published_utc": "2026-09-24T16:38:09Z",
      "entities": [
        {
          "mention_id": "4e71d0b93ca2f58e",
          "symbol": "TSLA",
          "company_name": "Tesla, Inc.",
          "match_type": "alias",
          "matched_text": "tesla",
          "confidence": 0.88,
          "snippet": "and tesla launching the robotaxi this week is a bigger deal than the market is pricing in",
          "start": 412.6,
          "end": 419.3,
          "speaker": { "label": "SPEAKER_01", "name": "Sam Guest", "role": "guest" }
        },
        {
          "mention_id": "c07e5b1a94d2f836",
          "symbol": "META",
          "company_name": "Meta Platforms, Inc.",
          "match_type": "product",
          "matched_text": "instagram",
          "confidence": 0.78,
          "snippet": "and instagram is basically where every small brand does its marketing now",
          "start": 1834.2,
          "end": 1841.9,
          "speaker": { "label": "SPEAKER_00", "name": "Jane Host", "role": "host" }
        }
      ],
      "created_at": "2026-09-25T07:15:03Z",
      "updated_at": "2026-09-25T07:15:03Z"
    }
  ],
  "returned_results": 1,
  "limit": 1,
  "next_cursor": "6a7d9d4ec8173839e2253d02"
}