Datasets API endpoint
Searches TikTok creators stored in a search index (one document per creator), with follower counts, verified status, niche, and engagement. Deleted and private accounts are excluded by default; set `include_inactive=true` to include them for historical lookups. Sort enum: `followers_desc`, `engagement_desc`, `engagement_qualified_desc`, `likes_desc`, `relevance`. Coverage note: `followers_desc`, `likes_desc`, and `relevance` are backed by profile fields present across the full dataset; the post-level engagement metrics (`engagement_rate`, `avg_views`, and the nested `post_stats` object) and the `engagement_desc`/`engagement_qualified_desc` sorts are currently populated for a growing subset of creators, prioritizing the highest-reach accounts. Creators without these metrics are still returned but sort last under `engagement_desc` and omit those fields; `engagement_qualified_desc` excludes them outright (they cannot clear its floors). `engagement_desc` ranks by raw `engagement_rate` with no eligibility floor — it surfaces a real stale-record + ratio-by-design trap: an account whose last real post was years ago can still carry an unrealistic rate computed from a handful of old posts. `engagement_qualified_desc` is the same metric restricted to creators with a recent post (`last_post_at` within 90 days), a minimum reach (`avg_views >= 10000`) and sample size (`post_stats.sampled_posts >= 10`), and a sanity ceiling (`engagement_rate <= 50%`) — use this, not the raw sort, for a "best engagement" leaderboard. Sound fields: `post_stats.top_sounds` holds only a creator's FIVE most-used sounds from the sampled posts, ranked by use count with ties broken by lowest `music_id`, so it is a top-5 view and not the creator's full sound list; `post_stats.distinct_sounds` gives the true number of different sounds the sample used. Use each sound's `original` boolean to tell TikTok-generated original audio from catalogue tracks - do NOT infer it from the title, because TikTok localizes the original-audio label (`sonido original`, `som original`, `оригинальный звук`, and at least fifteen more), so a title match silently reclassifies original audio as named tracks. Billing: this search costs 1 credit per record returned, with a minimum of 2 (an empty result costs 2, a full page of 100 costs 100).
/datasets/creators/searchSearches TikTok creators stored in a search index, one document per creator. Each record carries the creator's handle, nickname, bio, verified status, content niche, region, follower/following/likes/video counts, and (when enriched) engagement rate and contact email. Returns structured creator records — sorted by reach, engagement, or relevance — without triggering live scraping.
Deleted and private accounts are excluded by default so results only cover live, publicly visible creators. Set include_inactive=true to include them for historical lookups.
Dataset id: creators
engagement_desc vs engagement_qualified_desc: engagement_desc ranks by the raw engagement_rate field with no eligibility floor, and it surfaces a real stale-record + ratio-by-design trap — an account whose last real post was years ago can still carry a 60-100%+ "engagement rate" computed from a handful of ancient posts. engagement_qualified_desc is the same underlying metric restricted to creators that clear three floors, applied as query filters (not a post-processing step, so pagination stays correct):
last_post_at within the last 90 days.avg_views >= 10,000.post_stats.sampled_posts >= 10.engagement_rate <= 50% — a rate above that is far more likely a stats artifact than a real, sustainable rate.Use engagement_qualified_desc, not the raw sort, for any "best engagement" leaderboard or featured-creators use case. See docs/tiktok-influencer-ranking-scope.md for the full rationale and the specific stale-record examples that motivated the floors.
Query parameters:
q: optional full-text query over creator handle, nickname and bio, max 256 characters.handle: optional exact handle lookup (case-insensitive), e.g. khaby.lame. Returns the single creator with that exact TikTok @handle — more reliable than q for a known account (which is relevance-ranked and can bury an exact handle under same-name accounts).niche: optional exact content-niche filter, max 128 characters, e.g. skincare.country: optional exact creator country/region filter, max 128 characters, e.g. us.verified: optional verified-badge filter; true keeps only verified creators, false only unverified.min_followers: optional minimum follower count, must be 0 or greater.has_email: optional contact-email presence filter; true keeps only creators with a contact email, false only those without.include_inactive: optional boolean; defaults to false, which excludes deleted and private accounts (only live accounts are returned). Set true to include inactive (deleted/private) accounts for historical lookups.sort: optional sort enum: followers_desc (default), engagement_desc, engagement_qualified_desc, likes_desc, relevance. Defaults to relevance when q is supplied, otherwise followers_desc. engagement_desc ranks by raw post-level engagement rate, currently populated for a subset of creators (highest-reach first); creators without it sort last (see the coverage note above). engagement_qualified_desc is the same metric restricted to creators clearing a recency/reach/sample-size/sanity-ceiling floor — see the section above.page: optional page number, defaults to 1.page_size: optional page size, defaults to 20 and maxes at 100.Response behavior:
10000; page * page_size must not exceed 10000.items list rather than an error.1 credit per record returned, minimum 2. A search that returns 20 records costs 20 credits, a full page of 100 costs 100, and an empty or one-record result costs 2. You are charged for what comes back, not for the page_size you ask for, but your balance must cover the requested page_size for the call to be accepted. The response header x-credit-cost shows what a call was charged.
| Field | Filled in, of 20 checked | Values in the sample |
|---|---|---|
| creator_uid | 20 | |
| platform | 20 | |
| sec_uid | 20 | |
| unique_id | 20 | |
| nickname | 20 | |
| bio | 19 | |
| avatar_url | 20 | p19-common-sign.tiktokcdn-us.com and 5 other hosts |
| niche | 20 | |
| verified | 20 | true in 17 of 20 |
| follower_count | 20 | 12,500,000 to 36,600,000, median 17,400,000 |
| following_count | 20 | 36 to 2,402, median 230 |
| total_likes | 20 | 199,700,000 to 2,700,000,000, median 456,000,000 |
| video_count | 20 | 83 to 2,737, median 1,118 |
| engagement_rate | 20 | 0.04 to 0.17, median 0.1 |
| avg_views | 20 | 155,114 to 34,402,857, median 2,283,380 |
| last_post_at | 20 | 2026-09-03 to 2026-10-11 |
| post_stats | 20 | |
| source | 20 | |
| first_seen | 20 | 2026-08-09 to 2026-08-30 |
| last_crawled | 20 | 2026-10-10 to 2026-10-13 |
| Parameter | Type | Required | Default | Description | Example |
|---|---|---|---|---|---|
| q | string | No | Full-text query over handle, nickname and bio, max 256 characters | ||
| handle | string | No | Exact handle lookup (case-insensitive), e.g. khaby.lame; returns the single creator with that exact @handle | ||
| niche | string | No | Exact content-niche filter, max 128 characters | ||
| country | string | No | Exact creator country/region filter, max 128 characters | ||
| verified | boolean | No | Filter by verified badge; true keeps only verified creators | ||
| min_followers | integer | No | Minimum follower count | ||
| has_email | boolean | No | Filter by contact-email presence; true keeps only creators with an email | ||
| include_email | boolean | No | Return the stored contact email instead of a blanked value. Off by default for everyone, and honoured only for entitled (non-Free) API keys | ||
| include_inactive | boolean | No | false (only live accounts returned | Include deleted/private accounts; defaults to false (only live accounts returned) | |
| sort | string | No | Sort enum: followers_desc, engagement_desc, engagement_qualified_desc, likes_desc, relevance. engagement_desc ranks by raw post-level engagement rate, currently populated for a subset of creators (highest-reach first); creators without it sort last. engagement_qualified_desc is the same metric restricted to creators with a recent post (<=90d), a minimum reach (avg_views>=10000) and sample size (>=10 posts), and a sanity ceiling (<=50%) -- use this, not the raw sort, for a 'best engagement' leaderboard Allowed values: followers_desc, engagement_desc, engagement_qualified_desc, likes_desc, relevance. | ||
| page | integer | No | 1 | Page number, defaults to 1 | |
| page_size | integer | No | 20 and maxes at 100 | Page size, defaults to 20 and maxes at 100; page * page_size must be <= 10000 | |
| x-api-key (header) | string | Yes | API key required |
curl -X GET "https://api.crawlora.net/api/v1/datasets/creators/search?q=coffee&country=us&verified=true&has_email=true&include_email=true&include_inactive=true&sort=followers_desc&page=1" \ -H "x-api-key: $CRAWLORA_API_KEY"
{
"code": 200,
"msg": "OK",
"data": {
"dataset": "creators",
"items": [
{
"creator_uid": "tiktok:@bubble",
"platform": "tiktok",
"sec_uid": "MS4wLjABAAAAxxxxxxxx",
"unique_id": "bubble",
"nickname": "Bubble Skincare",
"bio": "Built with dermatologists since day 1.",
"bio_link": "https://hellobubble.com",
"niche": "skincare",
"country": "us",
"verified": true,
"follower_count": 4100000,
"following_count": 312,
"total_likes": 25246297,
"video_count": 1840,
"first_seen": "2026-06-20T05:00:00Z",
"last_crawled": "2026-06-20T05:00:00Z"
}
],
"page": 1,
"page_size": 20,
"total": 1,
"sort": "followers_desc"
}
}Request schema
No body schema
Response schema
#/definitions/datasets.creatorsSearchResponseDoc
| Field | Type | Required | Enum | Bounds | Example | Description |
|---|---|---|---|---|---|---|
| code | integer | No | 200 | |||
| data | datasets.CreatorsSearchResponse | No | ||||
| data.dataset | string | No | Identifier of the dataset queried (`creators`). | |||
| data.items | array | No | Matching creator records on this page. | |||
| data.items[].avatar_url | string | No | URL of the profile picture. | |||
| data.items[].avg_views | integer | No | top-level mirror of PostStats.AvgViews for sort/filter | |||
| data.items[].bio | string | No | Profile biography text. | |||
| data.items[].bio_link | string | No | Link shown in the profile biography. | |||
| data.items[].brand_affiliations | array | No | Brands linked to the creator. | |||
| data.items[].country | string | No | Creator's country or region code in lowercase, for example `us`. | |||
| data.items[].creator_uid | string | No | Unique creator id, made of the platform and the handle, for example `tiktok:@bubble`. | |||
| data.items[].email | string | No | Business contact email found for the creator. | |||
| data.items[].email_status | string | No | Verification status of the contact email. | |||
| data.items[].engagement_rate | number | No | headline = by-view (TikTok standard); see PostStats for both | |||
| data.items[].first_seen | string | No | FirstSeen is the discovery timestamp: set once when the creator is first indexed, and never changed by a later re-crawl. Values recorded before 2026-08-29 are unreliable and should not be used for cohort or tenure analysis. | |||
| data.items[].follower_count | integer | No | Number of followers. | |||
| data.items[].following_count | integer | No | Number of accounts the creator follows. | |||
| data.items[].language | string | No | Language code set on the creator's profile. | |||
| data.items[].last_crawled | string | No | Time the creator's profile was last fetched, in UTC (RFC 3339). | |||
| data.items[].last_post_at | string | No | top-level mirror of PostStats.LastPostAt for recency filter | |||
| data.items[].niche | string | No | Niche is the DISCOVERY SEED TERM that surfaced this creator (the keyword/hashtag/ explore-category the crawler used), NOT a content classification. It is single-valued and first-write-wins, so a creator found via the "korean skincare" keyword sweep is tagged "skincare" whatever they actually post about. Do not present it as a category; use NicheClassified for that. | |||
| data.items[].niche_classified | string | No | NicheClassified is the content niche DERIVED FROM THE BIO by classifyNiche() in the creatorsdb-enrich worker. Independent of Niche by construction: it is computed for every creator with a bio, including those that already carry a seed term, so the two fields never contaminate each other and "which is this?" is answered by the field name rather than an unbackfillable provenance flag. | |||
| data.items[].nickname | string | No | Display name shown on the creator's profile. | |||
| data.items[].platform | string | No | Social platform the creator is on, for example `tiktok`. | |||
| data.items[].post_stats | object | No | Averages and posting habits worked out from a sample of the creator's recent posts. | |||
| data.items[].post_stats.analyzed_at | string | No | Time these post statistics were calculated, in UTC (RFC 3339). | |||
| data.items[].post_stats.avg_comments | integer | No | Average comment count per sampled post. | |||
| data.items[].post_stats.avg_likes | integer | No | Average like count per sampled post. | |||
| data.items[].post_stats.avg_saves | integer | No | Average save (bookmark) count per sampled post. | |||
| data.items[].post_stats.avg_shares | integer | No | Average share count per sampled post. | |||
| data.items[].post_stats.avg_video_duration_sec | number | No | Average video length of sampled posts, in seconds. | |||
| data.items[].post_stats.avg_views | integer | No | Average view count per sampled post. | |||
| data.items[].post_stats.best_post_id | string | No | TikTok identifier of the most viewed sampled post. | |||
| data.items[].post_stats.best_post_views | integer | No | View count of the most viewed sampled post. | |||
| data.items[].post_stats.distinct_sounds | integer | No | DistinctSounds is how many different sounds the sampled posts used. TopSounds keeps only the five most-used, so without this a consumer cannot tell a creator who reuses one sound across the sample from one who used a different sound every time — both surface five rows. | |||
| data.items[].post_stats.engagement_rate_by_follower | number | No | Average over sampled posts of likes, comments and shares divided by the creator's follower count, as a fraction. | |||
| data.items[].post_stats.engagement_rate_by_view | number | No | Likes, comments, shares and saves divided by views across sampled posts, as a fraction (0.05 means 5%). | |||
| data.items[].post_stats.first_post_at | string | No | Time of the oldest sampled post, in UTC (RFC 3339). | |||
| data.items[].post_stats.last_post_at | string | No | Time of the most recent sampled post, in UTC (RFC 3339). | |||
| data.items[].post_stats.median_likes | integer | No | Median like count across sampled posts. | |||
| data.items[].post_stats.median_views | integer | No | Median view count across sampled posts. | |||
| data.items[].post_stats.original_sound_ratio | number | No | Share of sampled posts that use the creator's own original sound, as a fraction from 0 to 1. | |||
| data.items[].post_stats.posts_per_week | number | No | Average number of posts per week between the oldest and newest sampled post. | |||
| data.items[].post_stats.sampled_posts | integer | No | Number of the creator's recent posts included in these figures. | |||
| data.items[].post_stats.top_hashtags | array | No | Most used hashtags across sampled posts, lowercase and without the leading `#`, most used first. | |||
| data.items[].post_stats.top_sounds | array | No | Most used sounds across sampled posts, most used first. | |||
| data.items[].post_stats.top_sounds[].author | string | No | Name of the account or artist credited for the sound. | |||
| data.items[].post_stats.top_sounds[].music_id | string | No | TikTok identifier of the sound. | |||
| data.items[].post_stats.top_sounds[].original | boolean | No | Original marks TikTok-generated original audio. Titles cannot be used for this: TikTok localizes the label (`sonido original`, `оригинальный звук`, …), so a title match silently reclassifies original audio as a named track. | |||
| data.items[].post_stats.top_sounds[].title | string | No | Title of the sound as TikTok shows it. | |||
| data.items[].post_stats.top_sounds[].uses | integer | No | Number of the creator's sampled recent posts that used this sound. | |||
| data.items[].post_stats.views_to_follower_ratio | number | No | Average views per sampled post divided by the creator's follower count. | |||
| data.items[].sec_uid | string | No | Stable account id the platform assigns to the creator. | |||
| data.items[].source | string | No | Label for where the creator entry came from. | |||
| data.items[].status | string | No | "" active | "deleted" | "private" — refresh marks these to skip future crawls | |||
| data.items[].total_likes | integer | No | Total likes received across the creator's videos. | |||
| data.items[].unique_id | string | No | Creator's handle on the platform, without the `@`. | |||
| data.items[].verified | boolean | No | Whether the profile has the platform's verified badge. | |||
| data.items[].video_count | integer | No | Number of videos posted. | |||
| data.page | integer | No | Page number returned. | |||
| data.page_size | integer | No | Number of items per page. | |||
| data.sort | string | No | Sort order applied. | |||
| data.total | integer | No | Total number of records matching the filters. | |||
| msg | string | No | OK |
| Status | Description | Schema |
|---|---|---|
| 400 | Bad Request | #/definitions/app.Response |
| 429 | Too Many Requests | #/definitions/app.Response |
| 500 | Internal Server Error | #/definitions/app.Response |
Use environment variables for secrets and keep Crawlora API keys server-side.
curl -X GET "https://api.crawlora.net/api/v1/datasets/creators/search?q=coffee&country=us&verified=true&has_email=true&include_email=true&include_inactive=true&sort=followers_desc&page=1" \
-H "x-api-key: $CRAWLORA_API_KEY"