TikTok Keyword & Brand Mention Monitor
Searches TikTok for posts matching any keyword, brand name, or handle, flagging caption text matches and listing formally tagged mentions, engagement, and author details.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
keywords | array | required | Terms to monitor. One per line. Accepts plain keywords and phrases ('nike', 'nike shoes'), hashtags ('#JustDoIt') and handles ('@gymshark'). Multi-word caption matches require a contiguous phrase; direct @handles require a clickable tag. |
matchMode | string | optional | 'Real mentions' keeps posts with a contiguous caption phrase, a matching hashtag slug, a clickable @mention or an explicit sound match. For direct @handle inputs only the clickable tag (or sound) counts. 'Every search result' also keeps weak search hits, labelled searchResult. |
datePosted | string | optional | Only keep posts published in this window. The actor asks TikTok for the matching publish window, merges a bounded unfiltered pass for recall, and applies the exact cutoff. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
keywords | array | required | Terms to monitor. One per line. Accepts plain keywords and phrases ('nike', 'nike shoes'), hashtags ('#JustDoIt') and handles ('@gymshark'). Multi-word caption matches require a contiguous phrase; direct @handles require a clickable tag. |
matchMode | string | optional | 'Real mentions' keeps posts with a contiguous caption phrase, a matching hashtag slug, a clickable @mention or an explicit sound match. For direct @handle inputs only the clickable tag (or sound) counts. 'Every search result' also keeps weak search hits, labelled searchResult. |
datePosted | string | optional | Only keep posts published in this window. The actor asks TikTok for the matching publish window, merges a bounded unfiltered pass for recall, and applies the exact cutoff. |
postedAfter | string | optional | Optional exact cut-off, e.g. 2026-10-01 or '3 days'. The tighter of this and 'Posted within' applies. |
sortBy | string | optional | Order of the final rows. 'Newest first' is the monitor-friendly order; TikTok's own newest ordering is used where available, with a client-side fallback. |
searchDepth | string | optional | 'Just this term' runs one search. 'Standard' adds the #hashtag and @handle for single-word terms. 'Deep' also reads photo/slideshow posts, related searches and case/spacing variants. A direct @handle stays tagged-only in mentionsOnly. |
maxResultsPerKeyword | integer | optional | Maximum rows kept for each keyword after all sources are merged (1-500). TikTok returns roughly 130-240 posts per source query; deep mode can find more across sources. |
maxItems | integer | optional | Optional global cap across all keywords and sources. |
minPlayCount | integer | optional | Drop posts with fewer plays/views. Leave empty for no minimum. |
searchCountry | string | optional | 'Any country' uses the fastest free connection. A specific country pins the search to that market's results, which is slower but shows what people there see. |
onlyNewMentions | boolean | optional | Emit only posts not seen by this monitor in previous runs. The first run backfills; 'new' means new to this monitor, not new on TikTok. |
monitorName | string | optional | Name for this watchlist's saved state. Use a different name for a separate set of keywords. |
soundUrls | array | optional | Optional TikTok sound URLs or IDs to read as explicit sources, e.g. https://www.tiktok.com/music/original-sound-123... . Sound evidence sets soundMatch; a sound-only row uses source=sound and keyword=sound:<musicId>. |
Download the published input schema
View complete JSON
{
"description": "Monitor TikTok for posts that mention keywords, brand names, #hashtags or @handles. Choose strict mention evidence, date windows, multi-source coverage and optional only-new delta state.",
"properties": {
"datePosted": {
"default": "anyTime",
"description": "Only keep posts published in this window. The actor asks TikTok for the matching publish window, merges a bounded unfiltered pass for recall, and applies the exact cutoff.",
"editor": "select",
"enum": [
"anyTime",
"past24Hours",
"pastWeek",
"pastMonth",
"past3Months",
"past6Months"
],
"enumTitles": [
"Any time",
"Past 24 hours",
"Past week",
"Past month",
"Past 3 months",
"Past 6 months"
],
"prefill": "anyTime",
"title": "Posted within",
"type": "string"
},
"keywords": {
"description": "Terms to monitor. One per line. Accepts plain keywords and phrases ('nike', 'nike shoes'), hashtags ('#JustDoIt') and handles ('@gymshark'). Multi-word caption matches require a contiguous phrase; direct @handles require a clickable tag.",
"editor": "stringList",
"example": [
"nike",
"#JustDoIt",
"@gymshark",
"قهوة"
],
"items": {
"type": "string"
},
"prefill": [
"coffee"
],
"title": "Keywords, brands, hashtags or handles",
"type": "array"
},
"matchMode": {
"default": "mentionsOnly",
"description": "'Real mentions' keeps posts with a contiguous caption phrase, a matching hashtag slug, a clickable @mention or an explicit sound match. For direct @handle inputs only the clickable tag (or sound) counts. 'Every search result' also keeps weak search hits, labelled searchResult.",
"editor": "select",
"enum": [
"mentionsOnly",
"allResults"
],
"enumTitles": [
"Real mentions only (caption / hashtag / tag)",
"Every search result (labelled)"
],
"prefill": "mentionsOnly",
"title": "What counts as a mention",
"type": "string"
},
"maxItems": {
"description": "Optional global cap across all keywords and sources.",
"maximum": 100000,
"minimum": 1,
"title": "Max posts in total",
"type": "integer"
},
"maxResultsPerKeyword": {
"default": 50,
"description": "Maximum rows kept for each keyword after all sources are merged (1-500). TikTok returns roughly 130-240 posts per source query; deep mode can find more across sources.",
"maximum": 500,
"minimum": 1,
"prefill": 3,
"title": "Max posts per keyword",
"type": "integer"
},
"minPlayCount": {
"description": "Drop posts with fewer plays/views. Leave empty for no minimum.",
"minimum": 0,
"title": "Minimum views",
"type": "integer"
},
"monitorName": {
"default": "default",
"description": "Name for this watchlist's saved state. Use a different name for a separate set of keywords.",
"editor": "textfield",
"prefill": "default",
"title": "Monitor name",
"type": "string"
},
"onlyNewMentions": {
"default": false,
"description": "Emit only posts not seen by this monitor in previous runs. The first run backfills; 'new' means new to this monitor, not new on TikTok.",
"prefill": false,
"title": "Only new mentions",
"type": "boolean"
},
"postedAfter": {
"dateType": "absoluteOrRelative",
"description": "Optional exact cut-off, e.g. 2026-10-01 or '3 days'. The tighter of this and 'Posted within' applies.",
"editor": "datepicker",
"title": "Posted on or after",
"type": "string"
},
"searchCountry": {
"default": "any",
"description": "'Any country' uses the fastest free connection. A specific country pins the search to that market's results, which is slower but shows what people there see.",
"editor": "select",
"enum": [
"any",
"US",
"CA",
"MX",
"BR",
"AR",
"CO",
"CL",
"PE",
"GB",
"IE",
"FR",
"DE",
"AT",
"CH",
"NL",
"BE",
"ES",
"PT",
"IT",
"SE",
"NO",
"DK",
"FI",
"PL",
"CZ",
"RO",
"GR",
"TR",
"UA",
"IL",
"SA",
"AE",
"EG",
"MA",
"NG",
"KE",
"ZA",
"PK",
"BD",
"ID",
"MY",
"SG",
"PH",
"TH",
"VN",
"JP",
"KR",
"TW",
"AU",
"NZ"
],
"enumTitles": [
"Any country (fastest)",
"United States",
"Canada",
"Mexico",
"Brazil",
"Argentina",
"Colombia",
"Chile",
"Peru",
"United Kingdom",
"Ireland",
"France",
"Germany",
"Austria",
"Switzerland",
"Netherlands",
"Belgium",
"Spain",
"Portugal",
"Italy",
"Sweden",
"Norway",
"Denmark",
"Finland",
"Poland",
"Czechia",
"Romania",
"Greece",
"Turkey",
"Ukraine",
"Israel",
"Saudi Arabia",
"United Arab Emirates",
"Egypt",
"Morocco",
"Nigeria",
"Kenya",
"South Africa",
"Pakistan",
"Bangladesh",
"Indonesia",
"Malaysia",
"Singapore",
"Philippines",
"Thailand",
"Vietnam",
"Japan",
"South Korea",
"Taiwan",
"Australia",
"New Zealand"
],
"prefill": "any",
"title": "Search from country",
"type": "string"
},
"searchDepth": {
"default": "standard",
"description": "'Just this term' runs one search. 'Standard' adds the #hashtag and @handle for single-word terms. 'Deep' also reads photo/slideshow posts, related searches and case/spacing variants. A direct @handle stays tagged-only in mentionsOnly.",
"editor": "select",
"enum": [
"keywordOnly",
"standard",
"deep"
],
"enumTitles": [
"Just this term (fastest)",
"Standard: keyword + #hashtag + @handle",
"Deep: + photo posts, related searches, variants"
],
"prefill": "keywordOnly",
"title": "How wide to search",
"type": "string"
},
"sortBy": {
"default": "relevance",
"description": "Order of the final rows. 'Newest first' is the monitor-friendly order; TikTok's own newest ordering is used where available, with a client-side fallback.",
"editor": "select",
"enum": [
"relevance",
"newest",
"mostLiked"
],
"enumTitles": [
"Relevance (TikTok order)",
"Newest first",
"Most liked first"
],
"prefill": "relevance",
"title": "Order",
"type": "string"
},
"soundUrls": {
"description": "Optional TikTok sound URLs or IDs to read as explicit sources, e.g. https://www.tiktok.com/music/original-sound-123... . Sound evidence sets soundMatch; a sound-only row uses source=sound and keyword=sound:<musicId>.",
"editor": "stringList",
"example": [
"https://www.tiktok.com/music/original-sound-7627060748200577810"
],
"items": {
"type": "string"
},
"title": "Brand or campaign sounds",
"type": "array"
}
},
"required": [
"keywords"
],
"schemaVersion": 1,
"title": "TikTok Keyword & Brand Mention Monitor Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
data | string | optional | Dataset containing one row per matched TikTok post. Each row includes keyword, source, matchReason, matchedKeywords/sources, caption/hashtag/tag evidence, engagement, author snapshot, music/video metadata, slideshow images, hashtags and mentions. |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "One row per public TikTok post matched to a keyword, hashtag, handle, photo search, related search or explicit sound, with match evidence, provenance and engagement.",
"properties": {
"data": {
"description": "Dataset containing one row per matched TikTok post. Each row includes keyword, source, matchReason, matchedKeywords/sources, caption/hashtag/tag evidence, engagement, author snapshot, music/video metadata, slideshow images, hashtags and mentions.",
"template": "{{links.apiDefaultDatasetUrl}}/items",
"title": "Keyword Mentions",
"type": "string"
}
},
"title": "TikTok Keyword & Brand Mention Monitor Output"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
Download the published dataset schema
View complete JSON
{
"actorSpecification": 1,
"fields": {
"AIGCDescription": {
"description": "Description for AI-generated content label",
"title": "AIGC Description",
"type": "string"
},
"adLabelVersion": {
"description": "Ad label version; non-zero when the post is a sponsored ad",
"title": "Ad Label Version",
"type": "integer"
},
"aiContentLabel": {
"description": "Human-readable AI label when TikTok exposes one",
"title": "AI Content Label",
"type": "string"
},
"author": {
"description": "Author/creator information at time of scraping",
"properties": {
"avatarUrl": {
"title": "Avatar URL",
"type": "string"
},
"bio": {
"title": "Bio",
"type": "string"
},
"displayName": {
"title": "Display Name",
"type": "string"
},
"followerCount": {
"title": "Followers",
"type": "integer"
},
"followingCount": {
"title": "Following",
"type": "integer"
},
"heartCount": {
"title": "Hearts/Likes",
"type": "integer"
},
"id": {
"title": "User ID",
"type": "string"
},
"privateAccount": {
"title": "Private Account",
"type": "boolean"
},
"roomId": {
"title": "Live Room ID (non-empty when live)",
"type": "string"
},
"secUid": {
"title": "Secure UID",
"type": "string"
},
"username": {
"title": "Username",
"type": "string"
},
"verified": {
"title": "Verified",
"type": "boolean"
},
"videoCount": {
"title": "Videos",
"type": "integer"
}
},
"title": "Author",
"type": "object"
},
"caption": {
"description": "Post caption/description text including hashtags and mentions",
"title": "Caption",
"type": "string"
},
"captionMatch": {
"description": "The monitored term appears contiguously in the caption (whole word for single tokens; true phrase for multi-word Latin; substring for non-Latin scripts)",
"title": "Caption Match",
"type": "boolean"
},
"collectCount": {
"description": "Number of times post was bookmarked/collected",
"title": "Collect Count",
"type": "integer"
},
"commentCount": {
"description": "Number of comments",
"title": "Comment Count",
"type": "integer"
},
"commentStatus": {
"description": "Comment permission setting (0=all, 1=friends, 2=none)",
"title": "Comment Status",
"type": "integer"
},
"createTime": {
"description": "Unix timestamp (seconds) when the post was created",
"title": "Create Time (Unix)",
"type": "integer"
},
"createdAt": {
"description": "ISO 8601 UTC datetime when the post was created",
"title": "Created At",
"type": "string"
},
"duetEnabled": {
"description": "Whether the creator allows duets with this video",
"title": "Duet Enabled",
"type": "boolean"
},
"firstSeenAt": {
"description": "ISO 8601 time this monitor first saw the post",
"title": "First Seen At",
"type": "string"
},
"forFriend": {
"description": "Whether the post is restricted to friends only",
"title": "For Friend",
"type": "boolean"
},
"hashtagMatch": {
"description": "A hashtag slug equals the monitored term with whitespace removed (e.g. #nikeshoes for \"nike shoes\"); separate #nike and #shoes do not count",
"title": "Hashtag Match",
"type": "boolean"
},
"hashtags": {
"description": "Structured hashtags from TikTok metadata; a caption-only tag that matched is in matchedHashtags",
"items": {
"properties": {
"id": {
"title": "Hashtag ID",
"type": "string"
},
"isCommerce": {
"title": "Is Commerce",
"type": "boolean"
},
"name": {
"title": "Hashtag Name",
"type": "string"
}
},
"type": "object"
},
"title": "Hashtags",
"type": "array"
},
"images": {
"description": "Images for slideshow posts (postType='slideshow')",
"items": {
"properties": {
"height": {
"title": "Height",
"type": "integer"
},
"ordinal": {
"title": "Index",
"type": "integer"
},
"urlList": {
"title": "Image URLs",
"type": "array"
},
"width": {
"title": "Width",
"type": "integer"
}
},
"type": "object"
},
"title": "Slideshow Images",
"type": "array"
},
"isAd": {
"description": "Whether this post is a paid advertisement",
"title": "Is Ad",
"type": "boolean"
},
"isAigc": {
"description": "True only when TikTok exposes an AI label; omitted when unknown",
"title": "AI Generated Content",
"type": "boolean"
},
"isNew": {
"description": "New to this monitor (not necessarily new on TikTok)",
"title": "Is New",
"type": "boolean"
},
"isPaidPartnership": {
"description": "TikTok's paid-partnership label when exposed",
"title": "Paid Partnership",
"type": "boolean"
},
"keyword": {
"description": "The monitored keyword/brand/hashtag/handle this post primarily matched; a sound-only row uses sound:<musicId>",
"title": "Keyword",
"type": "string"
},
"likeCount": {
"description": "Number of likes/diggs",
"title": "Like Count",
"type": "integer"
},
"matchReason": {
"description": "Primary evidence: caption (contiguous phrase), hashtag (slug match), taggedMention (clickable @tag), sound (explicit sound source) or searchResult (weak search hit, allResults only)",
"title": "Match Reason",
"type": "string"
},
"matchReasons": {
"description": "Every evidence type found across sources",
"items": {
"type": "string"
},
"title": "All Match Reasons",
"type": "array"
},
"matchedHashtags": {
"description": "Hashtag entries that matched the monitored term",
"items": {
"type": "object"
},
"title": "Matched Hashtags",
"type": "array"
},
"matchedKeywords": {
"description": "Every monitored keyword that surfaced this post, plus sound:<musicId> for explicit sound sources",
"items": {
"type": "string"
},
"title": "Matched Keywords",
"type": "array"
},
"matchedMention": {
"description": "The clickable @mention that matched, with real handle and UTF-16 offsets",
"title": "Matched Mention",
"type": "object"
},
"matchedTerm": {
"description": "The exact search term, related suggestion, source variant or sound ID that surfaced this post",
"title": "Matched Term",
"type": "string"
},
"mediaUrlExpiresAt": {
"description": "ISO 8601 UTC datetime when signed media URLs (video/cover) expire",
"title": "Media URL Expires At",
"type": "string"
},
"mentionInCaption": {
"description": "Whether the monitored term appears contiguously in the caption (same as captionMatch; kept for backward compatibility)",
"title": "Mention In Caption",
"type": "boolean"
},
"mentions": {
"description": "@mentions in the post caption with UTF-16 text offsets",
"items": {
"properties": {
"awemeId": {
"title": "Mentioned Post ID (if any)",
"type": "string"
},
"end": {
"title": "End Offset",
"type": "integer"
},
"start": {
"title": "Start Offset",
"type": "integer"
},
"username": {
"title": "Username",
"type": "string"
}
},
"type": "object"
},
"title": "Mentions",
"type": "array"
},
"monitorName": {
"description": "Watchlist state name used for onlyNewMentions",
"title": "Monitor Name",
"type": "string"
},
"music": {
"description": "Sound/music used in the post",
"properties": {
"album": {
"title": "Album",
"type": "string"
},
"authorName": {
"title": "Author",
"type": "string"
},
"coverUrl": {
"title": "Cover URL",
"type": "string"
},
"dspLinks": {
"title": "DSP Links (Spotify, Apple Music, etc.)",
"type": "array"
},
"duration": {
"title": "Duration (s)",
"type": "integer"
},
"id": {
"title": "Music ID",
"type": "string"
},
"isCommerceMusic": {
"title": "Is Commerce Music",
"type": "boolean"
},
"isCopyrighted": {
"title": "Is Copyrighted",
"type": "boolean"
},
"isPrivate": {
"title": "Is Private Sound",
"type": "boolean"
},
"isUnlimitedMusic": {
"title": "Is Unlimited Music",
"type": "boolean"
},
"original": {
"title": "Original Sound",
"type": "boolean"
},
"playUrl": {
"title": "Play URL",
"type": "string"
},
"shootDuration": {
"title": "Shoot Duration (s)",
"type": "number"
},
"title": {
"title": "Title",
"type": "string"
}
},
"title": "Music",
"type": "object"
},
"originalItem": {
"description": "Whether this post is an original (non-duet, non-stitch) creation",
"title": "Original Item",
"type": "boolean"
},
"playCount": {
"description": "Number of video plays",
"title": "Play Count",
"type": "integer"
},
"postId": {
"description": "TikTok's unique post/video ID (string to preserve 64-bit precision)",
"title": "Post ID",
"type": "string"
},
"postType": {
"description": "Either 'video' or 'slideshow'",
"title": "Post Type",
"type": "string"
},
"postUrl": {
"description": "Direct URL to the TikTok post",
"title": "Post URL",
"type": "string"
},
"privateItem": {
"description": "Whether the post is set to private",
"title": "Private Item",
"type": "boolean"
},
"rank": {
"description": "Best (lowest) 0-based position this post had across all merged source queries",
"title": "Rank",
"type": "integer"
},
"repostCount": {
"description": "Number of reposts",
"title": "Repost Count",
"type": "integer"
},
"scrapedAt": {
"description": "ISO 8601 run timestamp",
"title": "Scraped At",
"type": "string"
},
"searchCountry": {
"description": "Market code when the run was country-pinned",
"title": "Search Country",
"type": "string"
},
"searchUrl": {
"description": "The TikTok search or sound page this post came from",
"title": "Search URL",
"type": "string"
},
"shareCount": {
"description": "Number of shares",
"title": "Share Count",
"type": "integer"
},
"shareEnabled": {
"description": "Whether sharing is enabled for this post",
"title": "Share Enabled",
"type": "boolean"
},
"soundMatch": {
"description": "The post came from an explicit sound URL",
"title": "Sound Match",
"type": "boolean"
},
"source": {
"description": "Primary source for this row: keyword, hashtag, handle, photo, relatedSearch or sound; sources[] lists every merged source",
"title": "Source",
"type": "string"
},
"sources": {
"description": "Every source that surfaced this post",
"items": {
"type": "string"
},
"title": "Sources",
"type": "array"
},
"stitchEnabled": {
"description": "Whether the creator allows stitching this video",
"title": "Stitch Enabled",
"type": "boolean"
},
"taggedMatch": {
"description": "TikTok's clickable @mention for the monitored handle is present; for direct @handle inputs this is the only caption-side match kept in mentionsOnly",
"title": "Tagged Mention",
"type": "boolean"
},
"textLanguage": {
"description": "Detected language code of the caption text",
"title": "Text Language",
"type": "string"
},
"video": {
"description": "Video technical metadata and signed play/cover URLs",
"properties": {
"bitrate": {
"title": "Bitrate",
"type": "integer"
},
"bitrateInfo": {
"description": "All available quality/bitrate variants with play URLs",
"title": "Bitrate Info",
"type": "array"
},
"codecType": {
"title": "Codec",
"type": "string"
},
"cover": {
"title": "Cover Image URL",
"type": "string"
},
"definition": {
"title": "Definition",
"type": "string"
},
"downloadAddr": {
"title": "Download URL",
"type": "string"
},
"duration": {
"title": "Duration (s)",
"type": "integer"
},
"dynamicCover": {
"title": "Dynamic Cover URL",
"type": "string"
},
"format": {
"title": "Format",
"type": "string"
},
"height": {
"title": "Height (px)",
"type": "integer"
},
"originCover": {
"title": "Original Cover URL",
"type": "string"
},
"playUrl": {
"title": "Play URL",
"type": "string"
},
"ratio": {
"title": "Aspect Ratio",
"type": "string"
},
"size": {
"title": "File Size (bytes)",
"type": "string"
},
"videoQuality": {
"title": "Video Quality Label",
"type": "string"
},
"volumeInfo": {
"description": "Audio loudness and peak level metadata",
"title": "Volume Info",
"type": "object"
},
"width": {
"title": "Width (px)",
"type": "integer"
}
},
"title": "Video",
"type": "object"
}
},
"title": "TikTok Keyword Mentions",
"views": {
"matching": {
"description": "One row per post with the evidence that caused it to be kept.",
"display": {
"component": "table",
"properties": {
"captionMatch": {
"format": "boolean",
"label": "Caption"
},
"hashtagMatch": {
"format": "boolean",
"label": "Hashtag"
},
"isNew": {
"format": "boolean",
"label": "New"
},
"keyword": {
"format": "text",
"label": "Keyword"
},
"matchReason": {
"format": "text",
"label": "Match Reason"
},
"matchedTerm": {
"format": "text",
"label": "Matched Term"
},
"postId": {
"format": "text",
"label": "Post ID"
},
"soundMatch": {
"format": "boolean",
"label": "Sound"
},
"source": {
"format": "text",
"label": "Source"
},
"taggedMatch": {
"format": "boolean",
"label": "Tagged"
}
}
},
"title": "Matching Evidence",
"transformation": {
"fields": [
"postId",
"keyword",
"source",
"matchReason",
"matchedTerm",
"captionMatch",
"hashtagMatch",
"taggedMatch",
"soundMatch",
"matchedHashtags",
"matchedMention",
"matchedKeywords",
"sources",
"isNew"
]
}
},
"overview": {
"description": "TikTok posts matching a monitored keyword, hashtag or handle, with match evidence and engagement.",
"display": {
"component": "table",
"properties": {
"caption": {
"format": "text",
"label": "Caption"
},
"commentCount": {
"format": "number",
"label": "Comments"
},
"createdAt": {
"format": "text",
"label": "Created At"
},
"isNew": {
"format": "boolean",
"label": "New"
},
"keyword": {
"format": "text",
"label": "Keyword"
},
"likeCount": {
"format": "number",
"label": "Likes"
},
"matchReason": {
"format": "text",
"label": "matchReason"
},
"mentionInCaption": {
"format": "boolean",
"label": "In Caption"
},
"playCount": {
"format": "number",
"label": "Views"
},
"postId": {
"format": "text",
"label": "Post ID"
},
"postUrl": {
"format": "link",
"label": "Post URL"
},
"rank": {
"format": "number",
"label": "Rank"
},
"shareCount": {
"format": "number",
"label": "Shares"
},
"source": {
"format": "text",
"label": "source"
}
}
},
"title": "Mentions Overview",
"transformation": {
"fields": [
"postId",
"keyword",
"rank",
"mentionInCaption",
"caption",
"createdAt",
"author",
"playCount",
"likeCount",
"commentCount",
"shareCount",
"postUrl",
"source",
"matchReason",
"isNew",
"captionMatch",
"hashtagMatch",
"taggedMatch"
]
}
}
}
}
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
TikTok Keyword & Brand Mention Monitor
Find the TikTok posts that actually mention your brand, product, campaign, hashtag or handle — and see why each one matched. This actor reads TikTok's public search from several angles (keyword, hashtag, handle, photo posts, related searches and optional brand sounds), merges duplicates into one row per post, and labels the evidence so a real caption mention is never confused with a weak search hit.
What this actor does
- Six source types: keyword search,
#hashtagsearch,@handlesearch, photo/slideshow search, token-filtered related searches, and explicit brand/campaign sounds. - Evidence, not guesses: every row says whether the term appears in the caption, in a hashtag, as TikTok's clickable
@mention, from a sound page, or only as a search result. - Strict by default:
mentionsOnlykeeps rows with real evidence; switch toallResultsto keep every search hit, labelled. - One row per post: multi-keyword and multi-source duplicates are merged, with
matchedKeywords[],sources[]and the bestrankacross merged sources. - Built for monitoring: server-backed date windows, newest-first ordering, an exact cut-off, and optional only-new delta state.
- Full post detail: caption, engagement, creator snapshot, sound, video or slideshow images, hashtags, mentions and location tags.
- Honest empty fields: values TikTok does not provide are omitted, never filled with placeholders.
Output per post
Every row is one public TikTok post.
postId,postType(videoorslideshow),postUrl— identity and canonical linkcaption,textLanguage,createTime,createdAt— text and publication timekeyword— the monitored term that primarily matched this post; an explicit sound-only row usessound:<musicId>source— the primary source:keyword,hashtag,handle,photo,relatedSearchorsound; merged rows list every source insources[]matchReason—caption,hashtag,taggedMention,soundorsearchResultmatchReasons[]— every reason this post matched across sourcesmatchedTerm,matchedKeywords[],sources[]— full provenance after dedupe; explicit sounds contribute asound:<musicId>labelmentionInCaption,captionMatch,hashtagMatch,taggedMatch,soundMatch— evidence flagsmatchedHashtags[]— the hashtag entries that matched the monitored termmatchedMention—{username, displayText, userId, secUid, start, end}for a clickable tagrank— best (lowest) position this post had across all merged sourcessearchUrl,searchCountry,monitorName,scrapedAt— provenance and run contextisNew,firstSeenAt— monitor state (first run: everything is new)playCount,likeCount,commentCount,shareCount,collectCount,repostCountauthor—{id, secUid, username, displayName, bio, verified, privateAccount, avatarUrl, followerCount, followingCount, heartCount, videoCount}music—{id, title, authorName, original, duration, album, playUrl, coverUrl, ...}when a sound is attachedvideo— dimensions, duration, definition,playUrl,downloadAddr, cover images and quality detailsimages[]— ordered slideshow images ({ordinal, width, height, urlList})hashtags[]— all structured hashtags in the postmentions[]— all clickable@mentions, with real handles and source-text offsetspoi— location tag, when the creator added oneisAd,adLabelVersion,privateItem,forFriend,originalItem,stitchEnabled,shareEnabled,duetEnabled,commentStatusisAigc,aiContentLabel,isPaidPartnership— only when TikTok exposes a verified labelmediaUrlExpiresAt— when TikTok's signed media links expire
Media URLs (video.playUrl, video.downloadAddr, music.playUrl) are reference-only. Most video play/download links return 403 outside TikTok; music and cover links are signed too and can expire or fail. Covers and slideshow images also expire.
How matching works
A search result is not automatically a mention.
caption— the monitored term appears contiguously in the caption. Single words use whole-word matching, sonikedoes not matchnikeshoes. Multi-word terms must appear as a true phrase:#nike ... #shoesis not anike shoescaption match;my nike shoesis. For non-Latin scripts, matching is a contiguous substring, so related forms can count (for exampleالقهوةforقهوة).hashtag— the post carries a hashtag whose slug equals the monitored term with whitespace removed.nike shoesmatches#nikeshoes; separate#nikeand#shoesdo not count as a phrase match.taggedMention— the caption contains TikTok's clickable tag for the monitored@handle. A direct@handlekeyword is strict: inmentionsOnly, only clickable tags (or an explicit sound source) are kept; caption and hashtag hits for the bare name are labelledsearchResultand appear only inallResults.sound— the post came from a sound URL you supplied.searchResult— TikTok returned the post for the search, but none of the above is true. These rows are kept only inallResultsmode.
For non-Latin scripts (for example Arabic, Chinese or Devanagari), TikTok often returns relevant posts whose captions do not contain the literal query as a separate word. Matching is a contiguous substring for those scripts, so mentionsOnly can keep related word forms; use allResults when you want every search hit and label the weak ones yourself.
Input
| Field | Type | Default | Description |
|---|---|---|---|
keywords |
array | – | Required. Keywords, phrases, #hashtags or @handles to monitor. |
matchMode |
string | mentionsOnly |
mentionsOnly (real evidence) or allResults (every search hit, labelled). |
datePosted |
string | anyTime |
anyTime, past24Hours, pastWeek, pastMonth, past3Months or past6Months. |
postedAfter |
string | – | Exact cut-off: 2026-10-01 or a relative age such as 3 days. |
sortBy |
string | relevance |
relevance, newest or mostLiked. |
searchDepth |
string | standard |
keywordOnly; standard adds #hashtag and @handle for single-word terms; deep adds photos, related searches and case/spacing variants. |
maxResultsPerKeyword |
integer | 50 | Rows kept per keyword after merging sources (1-500). |
maxItems |
integer | – | Optional global cap across all keywords. |
minPlayCount |
integer | – | Drop posts with fewer views. |
searchCountry |
string | any |
any or a market code such as US, GB, PK, JP. |
onlyNewMentions |
boolean | false | Emit only posts not seen by this monitor before. |
monitorName |
string | default |
State name for this watchlist. |
soundUrls |
array | – | Optional TikTok sound URLs or IDs to read as explicit sources. |
Example: brand monitor, last week, newest first
{
"keywords": ["nike", "adidas"],
"matchMode": "mentionsOnly",
"searchDepth": "standard",
"datePosted": "pastWeek",
"sortBy": "newest",
"maxResultsPerKeyword": 100
}
Example: campaign hashtag with photo posts
{
"keywords": ["#JustDoIt"],
"matchMode": "allResults",
"searchDepth": "deep",
"maxResultsPerKeyword": 200,
"minPlayCount": 1000
}
Example: unicode discovery
{
"keywords": ["قهوة", "咖啡"],
"matchMode": "allResults",
"searchDepth": "standard",
"maxResultsPerKeyword": 50
}
Example: daily only-new watchlist
{
"keywords": ["khaadi", "@gymshark"],
"matchMode": "mentionsOnly",
"datePosted": "pastWeek",
"sortBy": "newest",
"onlyNewMentions": true,
"monitorName": "brand-watch"
}
Example: brand-owned sound
{
"keywords": ["khaadi"],
"soundUrls": ["https://www.tiktok.com/music/original-sound-7627060748200577810"],
"searchDepth": "standard",
"maxResultsPerKeyword": 100
}
Use cases
- Brand monitoring — catch posts that name your brand, not only posts that tag your account.
- Competitor tracking — compare how often competing brands are mentioned and how those posts perform.
- Campaign measurement — follow a campaign hashtag, slogan or brand-owned sound across creators.
- PR and crisis watch — run a daily only-new monitor for a product or brand term.
- Market research — compare the conversation in different countries and languages.
- Creator and partnership discovery — find the creators and posts driving a brand conversation.
FAQ
Do I need a TikTok account, cookies or a proxy?
No. The actor reads public TikTok search and needs no login, cookies or proxy setup. It chooses a working connection automatically.
What is the difference between caption, hashtag, taggedMention and searchResult?
caption means the term appears in the caption text. hashtag means a matching hashtag is attached. taggedMention means TikTok's clickable tag for the monitored handle is present. searchResult means TikTok returned the post for the search but none of the stronger evidence exists; those rows appear only in allResults mode.
Why do I get fewer rows than maxResultsPerKeyword?
TikTok's public search pool is finite and rotates. Runs returned roughly 130-240 posts per source query in current tests; a repeat of the same query can return a different set. Niche terms, narrow date windows and hashtags can run out earlier. Date windows also merge a bounded unfiltered pass, so recent posts are not lost just because TikTok's date-ranked pool is incomplete. deep mode searches several sources and merges them, which usually finds more.
How does onlyNewMentions work?
The actor saves the posts it has seen for each monitorName in a persistent store. On the next run, only posts not seen before are emitted and marked isNew. The first run backfills everything it finds within your filters and caps; if the state is lost, the next run backfills again. "New" means new to this monitor, not new on TikTok.
Does it monitor hashtags and handles?
Yes. A plain single-word keyword can automatically include its #hashtag and @handle in standard or deep mode. You can also enter a hashtag or handle directly. A direct @handle is strict in mentionsOnly: only TikTok's clickable tag for that handle is kept. A direct #hashtag keeps matching hashtag, caption or tag evidence for the bare term. Use a plain keyword when you want caption mentions of a brand name.
Can I search a specific country?
Yes. Set searchCountry to a market code. Country-specific results are slower because the actor uses a country-pinned connection. Leave it on any for the fastest run.
Why are some fields missing from a row?
Empty values are omitted. TikTok does not expose every field for every post. A per-post country is not available from search; poi appears when the creator added a location tag, and textLanguage gives the caption language.
Can I download the video or sound from the output?
Treat media URLs as reference-only. Most video play/download links return 403 outside TikTok; music and cover links are signed too and can expire or fail. Covers and slideshow images also expire.
Does this actor include comments?
No. It monitors public posts and captions. TikTok's public web search does not offer a comment search, so comment-level mentions are out of scope.
Is this affiliated with TikTok?
No. This is a third-party actor that reads publicly visible TikTok search results and is not affiliated with TikTok or ByteDance.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build Zi3c96rdMUizrbfUg. Documentation is regenerated from the default published build; private drafts are not included.