Douyin Xingtu API: Creator Search, Audience Profiles and Performance
Search Douyin Xingtu creators by content keyword, retrieve profiles and audience distributions, and save performance metrics with explicit filters and a Python example.
Find creators around a content topic
For a Douyin campaign, begin with a relevant topic, then examine creator profiles, audiences and performance before deciding whom to contact. Just One API's Xingtu endpoints let you connect these steps and retain the source data for review.
This tutorial searches for “露营” (camping), reads up to two search pages, and retrieves profiles, audience distributions and performance metrics for three creators. It produces a small sample for campaign research.
Register and test in the documentation
Register for Just One API, obtain your API token and test Xingtu Creator Search in the interactive documentation. One token works across platform APIs, with usage-based billing. Check your account for endpoint permissions and available free trials.
The workflow uses four GET endpoints. Check the response body's business code=0. If the nested data contains base_resp, also check its status_code; an outer success response does not necessarily establish success of the embedded platform operation.
Step 1: Search and retain Xingtu creator IDs
Use this search endpoint:
/api/douyin-xingtu/gw/api/gsearch/search_for_author_square/v1The example uses keyword=露营, searchType=CONTENT, sort=FANS and page=1. To find a known creator by name, use searchType=NICKNAME instead.
In the current public example, results are in data.authors. Each row's star_id is used as oAuthorId in subsequent requests. This is a Xingtu identifier; do not substitute an ordinary Douyin UID or secUid. If you already have the Xingtu creator ID, skip search.
Increment page and inspect data.pagination.has_more to continue. The script deduplicates IDs and stops on an empty page, no new IDs or the two-page limit. The returned pagination.total_count describes the reported search results, not every creator on Douyin.
Step 2: Retrieve profiles and audience distributions
Pass the selected ID as oAuthorId to Creator Profile V1. The example specifies platform=SHORT_VIDEO. The current public response includes fields such as id, nick_name, follower and sec_uid.
Next, call Audience Distribution V1 with the same ID and platform, plus linkType=CONNECTED. Other documented values include AWARE, INTERESTED, LIKE and FOLLOW.
These values select different audience scopes. Do not label CONNECTED results simply as follower demographics. The example response places groups in data.distributions, with descriptions and distribution lists, and includes last_update_time. Preserve those descriptions and the update time alongside the values.
Step 3: Keep performance filters consistent
Spread Metrics V1 has its own filters. The example sends the same values for every creator:
platform=SHORT_VIDEOfor short-form video.range=DAY_30for the last 30 days;DAY_90is also documented.type=PERSONAL_VIDEOfor personal videos. The documented enum for Xingtu videos is spelledXINTU_VIDEO.onlyAssign=false, so the request is not restricted to assigned videos only.flowType=EXCLUDE, retaining this filter value in the analysis record.
The DAY_30 parameter applies to this performance request. It does not put the preceding audience response on the same 30-day window; inspect the audience update time separately. Confirm the precise meaning of the flowType filter for your analysis rather than assigning it an unverified business definition.
The public example includes objects such as expect_cpe, expect_cpm, interact_rate, play_over_rate and play_stability. Some values are formatted labels with units, percentages or comparative wording. The script preserves the objects rather than forcing display text into floating-point numbers. These fields alone do not establish a campaign's realized ROI.
Python example: Export creator data and metric filters
Install the dependency and set your token. Replace the placeholder. These commands work in a macOS or Linux terminal.
python3 -m pip install requests
export JUSTONEAPI_TOKEN='your_token'Save the code as xingtu_sample.py and run python3 xingtu_sample.py. It makes at most 11 API requests: two searches, then a profile, audience and performance request for each of three creators. A failure stops execution while retaining previously saved files.
import json
import os
from datetime import datetime, timezone
from pathlib import Path
import requests
BASE_URL = "https://api.justoneapi.com"
TOKEN = os.environ.get("JUSTONEAPI_TOKEN", "")
def call_api(path, **params):
try:
response = requests.get(
BASE_URL + path,
params={"token": TOKEN, **params},
timeout=120, allow_redirects=False,
)
except requests.RequestException:
raise RuntimeError("API network error or timeout") from None
if response.status_code != 200:
raise RuntimeError(f"API HTTP {response.status_code}")
try:
result = response.json()
except ValueError:
raise RuntimeError("API response is not JSON") from None
if not isinstance(result, dict) or result.get("code") != 0:
code = result.get("code") if isinstance(result, dict) else None
raise RuntimeError(f"API failed: code={code}; check the docs")
data = result.get("data")
if not isinstance(data, dict):
raise RuntimeError("Unexpected data shape; inspect the docs")
inner = data.get("base_resp")
if isinstance(inner, dict) and inner.get("status_code") not in (0, "0"):
raise RuntimeError("Platform response was unsuccessful")
return result
def save_json(folder, name, value):
(folder / name).write_text(
json.dumps(value, ensure_ascii=False, indent=2), encoding="utf-8"
)
def require_list(data, key):
value = data.get(key)
if not isinstance(value, list):
raise RuntimeError(f"Expected a list at {key}; inspect saved JSON")
return value
def output_folder(topic):
if not TOKEN or TOKEN == "your_token":
raise RuntimeError("Set JUSTONEAPI_TOKEN before running")
run_id = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S%fZ")
folder = Path(topic + "-output") / run_id
folder.mkdir(parents=True, exist_ok=False)
return folder
MAX_PAGES = 2
MAX_CREATORS = 3
PREFIX = "/api/douyin-xingtu/gw/api"
PLATFORM = "SHORT_VIDEO"
def main():
folder = output_folder("xingtu")
ids, seen = [], set()
for page in range(1, MAX_PAGES + 1):
result = call_api(
PREFIX + "/gsearch/search_for_author_square/v1",
keyword="露营", searchType="CONTENT", page=page, sort="FANS",
)
save_json(folder, f"search-{page}.json", result)
data = result["data"]
rows = require_list(data, "authors")
if not rows:
break
previous_count = len(ids)
for row in rows:
uid = row.get("star_id") if isinstance(row, dict) else None
if uid and str(uid) not in seen:
seen.add(str(uid))
ids.append(str(uid))
if len(ids) == previous_count:
break
pagination = data.get("pagination")
if not isinstance(pagination, dict):
raise RuntimeError("Missing pagination; inspect saved JSON")
if pagination.get("has_more") not in (True, 1, "1"):
break
selected = ids[:MAX_CREATORS]
save_json(folder, "selected-creators.json", selected)
scope = {
"platform": PLATFORM, "range": "DAY_30",
"type": "PERSONAL_VIDEO", "onlyAssign": "false",
"flowType": "EXCLUDE",
}
save_json(folder, "analysis-scope.json", {**scope, "linkType": "CONNECTED"})
for index, uid in enumerate(selected, start=1):
profile = call_api(
PREFIX + "/author/get_author_base_info/v1",
oAuthorId=uid, platform=PLATFORM,
)
save_json(folder, f"creator-{index}-profile.json", profile)
audience = call_api(
PREFIX + "/data_sp/author_audience_distribution/v1",
oAuthorId=uid, platform=PLATFORM, linkType="CONNECTED",
)
save_json(folder, f"creator-{index}-audience.json", audience)
spread = call_api(
PREFIX + "/data_sp/get_author_spread_info/v1",
oAuthorId=uid, **scope,
)
save_json(folder, f"creator-{index}-spread.json", spread)
print(f"Saved {len(selected)} creator samples to {folder}")
if __name__ == "__main__":
try:
main()
except RuntimeError as error:
raise SystemExit(str(error)) from NoneCompare the saved results
Each run creates a dated folder under xingtu-output. selected-creators.json stores the IDs and analysis-scope.json records the selected filters. Profile, audience and performance responses are saved separately for each creator so they can be inspected and refreshed independently.
Before comparing metrics, align platform, video type, performance period and the other filter values. Then assess content relevance and audience fit. When deriving numeric columns, identify units and missing-value markers first and preserve the original fields. A creator with unavailable metrics should be marked as having insufficient data, not zero performance.
Common questions
Can ordinary Douyin video APIs replace Xingtu APIs?
Ordinary Douyin endpoints cover video content, details and comments. Xingtu provides creator marketing data. See the Douyin video tutorial for content collection. The datasets can complement one another once you establish identifier mappings and metric definitions.
Does this collect everything in one request?
No. It is a sequence of independent requests. Select candidates first, then enrich only those needing audience or performance analysis. Retain each response's update information because the datasets may refresh at different times.
How should failures and unavailable metrics be handled?
Record request failures separately from missing fields in successful responses. The sample does not retry automatically. For a larger workflow, use bounded retries for transient failures as described in the retry and concurrency guide, and resolve parameter or permission errors before retrying.
Test a topic relevant to your business in Xingtu Creator Search, then enrich the candidates you want to investigate.
Continue with Just One API
Log in to the Dashboard for your token, read the full API docs, or open the MCP GitHub project for the latest configuration.