TikTok API
Publish compliant TikTok videos and photo carousels with explicit creator choices, consent, and verified media sources.
Quick reference
| Property | RelayAPI behavior |
|---|---|
| Platform key | tiktok |
| Authentication | OAuth 2.0 with video.publish and video.upload |
| Publishing mode | Direct Post or creator-inbox upload |
| Media | Exactly one video with duration_ms, or 1–35 photos; no text-only or mixed-media posts |
| Video caption | 2,200 UTF-16 code units |
| Photo title | First 90 Unicode code points of content |
| Photo description | 4,000 UTF-16 code units |
| Video sources | Bounded FILE_UPLOAD, or verified PULL_FROM_URL |
| Photo sources | Verified PULL_FROM_URL only |
| Lifecycle | RelayAPI records TikTok's publish_id and polls the status API |
RelayAPI does not choose privacy, interaction, commercial-disclosure, preview, or consent values for a creator. Your application must render TikTok's current choices, show the exact content preview, obtain the creator's choices and consent, and then send every required field explicitly.
Connections authorized before RelayAPI added creator-inbox uploads do not have
the new video.upload grant automatically. Reconnect TikTok before using
publish_mode: 'inbox'; token refresh cannot add a scope the user never
granted.
Required creator flow
Before creating a TikTok post:
- Call
GET /v1/accounts/{id}/tiktok-creator-info. - Show the connected creator and only the returned
privacy_level_options. - Disable comment, duet, or stitch choices that creator info reports as disabled.
- For video, validate the media duration against
max_video_post_duration_sec. - Show the exact content preview and the appropriate TikTok commercial-content and Music Usage Confirmation notices.
- After the creator makes each choice and expressly consents, submit the post with the required booleans.
RelayAPI queries creator info again immediately before provider I/O. A stale privacy choice, an interaction disabled since the UI was rendered, or a video longer than the creator's current limit fails before TikTok publish initialization.
TikTok requires users to select privacy and interaction settings without a
default. The two consent attestations are literal true values, but they are
not automatic defaults: set them only after the creator previews the exact
post and completes the required consent step.
Publish a video
All eight creator-choice fields shown below are required for a video. Set both commercial disclosure fields explicitly even for non-commercial content.
const post = await client.posts.create({
content: 'A quiet morning by the sea #travel',
targets: ['tiktok'],
scheduled_at: 'now',
media: [{
url: 'https://media.example.com/tiktok/morning.mp4',
type: 'video',
mime_type: 'video/mp4',
duration_ms: 18_000,
}],
target_options: {
tiktok: {
privacy_level: 'PUBLIC_TO_EVERYONE',
allow_comment: true,
allow_duet: false,
allow_stitch: false,
brand_content_toggle: false,
brand_organic_toggle: false,
content_preview_confirmed: true,
express_consent_given: true,
source_mode: 'file_upload',
},
},
});The same request with curl:
curl --fail-with-body -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"content": "A quiet morning by the sea #travel",
"targets": ["tiktok"],
"scheduled_at": "now",
"media": [{
"url": "https://media.example.com/tiktok/morning.mp4",
"type": "video",
"mime_type": "video/mp4",
"duration_ms": 18000
}],
"target_options": {
"tiktok": {
"privacy_level": "PUBLIC_TO_EVERYONE",
"allow_comment": true,
"allow_duet": false,
"allow_stitch": false,
"brand_content_toggle": false,
"brand_organic_toggle": false,
"content_preview_confirmed": true,
"express_consent_given": true,
"source_mode": "file_upload"
}
}
}'FILE_UPLOAD does not put binary data in the JSON request. RelayAPI fetches the
media URL through its guarded public-media client, validates the response MIME
type and declared length, initializes TikTok's upload, and streams bounded,
sequential chunks to TikTok. MP4, MOV, and WebM are accepted; the provider limit
is 4 GB. RelayAPI's own media library has a separate 200 MiB direct-to-storage
upload-session limit (and a 50 MiB Worker-proxy limit)
described in Media Uploads.
Publish a photo carousel
TikTok photo publishing always uses PULL_FROM_URL. Every image must use an
HTTPS URL under a TikTok URL prefix that was verified by the RelayAPI operator
and pinned when this account connected.
const post = await client.posts.create({
content: 'Weekend in Cornwall',
targets: ['tiktok'],
scheduled_at: 'now',
media: [
{ url: 'https://media.example.com/tiktok/cornwall-1.jpg', type: 'image' },
{ url: 'https://media.example.com/tiktok/cornwall-2.jpg', type: 'image' },
],
target_options: {
tiktok: {
privacy_level: 'MUTUAL_FOLLOW_FRIENDS',
allow_comment: true,
brand_content_toggle: false,
brand_organic_toggle: false,
content_preview_confirmed: true,
express_consent_given: true,
source_mode: 'pull_from_url',
description: 'The complete weekend recap #cornwall',
photo_cover_index: 0,
auto_add_music: false,
},
},
});Duet and stitch do not apply to photos. source_mode: "file_upload" is not
supported for photo posts. Direct mode sends DIRECT_POST; inbox mode sends
TikTok's official MEDIA_UPLOAD flow.
Upload to the creator inbox
Set publish_mode: 'inbox' to upload media for the creator to finish in TikTok
after receiving the provider notification. This is not a RelayAPI local draft:
the bytes have reached TikTok, while privacy, interaction, disclosure, and final
publishing choices happen in TikTok.
const post = await client.posts.create({
content: '',
targets: ['tiktok'],
scheduled_at: 'now',
media: [{
url: 'https://media.example.com/tiktok/rough-cut.mp4',
type: 'video',
mime_type: 'video/mp4',
duration_ms: 24_000,
}],
target_options: {
tiktok: {
publish_mode: 'inbox',
source_mode: 'file_upload',
},
},
});Inbox mode rejects Direct Post fields including privacy_level, interaction
choices, disclosure/consent attestations, AI/music flags, and
video_cover_timestamp_ms. TikTok's video inbox endpoint accepts only
source_info, so description is also rejected for inbox video; add the
caption when completing the post in TikTok. Photo inbox uploads can retain the
photo title/description and photo_cover_index supported by TikTok's
MEDIA_UPLOAD photo contract.
Media source selection
| Media and request | Result |
|---|---|
Video, source_mode: "file_upload" | RelayAPI performs bounded TikTok FILE_UPLOAD; no verified prefix is needed |
Video, source_mode: "pull_from_url" | Allowed only when the URL matches a prefix pinned to the connected account |
Video, source_mode omitted, URL is pinned | RelayAPI uses PULL_FROM_URL |
Video, source_mode omitted, URL is not pinned | RelayAPI falls back to bounded FILE_UPLOAD |
| Photos | Every image must match a pinned prefix; TikTok requires PULL_FROM_URL |
For pull mode, the media must remain publicly reachable over HTTPS for TikTok's download window and must not redirect. A URL that is merely on the same site is not sufficient: its hostname and path must be beneath a configured, verified prefix.
Verified prefixes are connection authority, not caller-controlled post options. The operator configures them, and RelayAPI snapshots the canonical list into the TikTok account when OAuth completes. Changing the operator list does not silently expand an existing account's media authority; reconnect the account to adopt the new list.
For self-hosted installations, configure the non-secret list in
relayapi.selfhost.json before connecting TikTok:
{
"publishing": {
"tiktokVerifiedUrlPrefixes": [
"https://media.example.com/tiktok/"
]
}
}The deployment CLI validates and emits this as the API Worker's
TIKTOK_VERIFIED_URL_PREFIXES variable. Each prefix must use HTTPS and contain
no credentials, IP host, non-default port, query string, or fragment. Its path
must end in /, which keeps /media/ from authorizing /mediaevil. Verify the
same exact prefix in the TikTok developer application's URL properties first.
Provider media limits
| Media | TikTok limit |
|---|---|
| Video container | MP4, MOV, or WebM |
| Video codec | H.264, H.265, VP8, or VP9 |
| Video frame rate | 23–60 FPS |
| Video dimensions | Each side 360–4,096 pixels |
| Video duration | At most 10 minutes and no longer than fresh creator info allows |
| Video size | At most 4 GB |
| Photo format | JPEG or WebP |
| Photo dimensions | At most 1080p |
| Photo size | At most 20 MB each |
RelayAPI can best-effort normalize ready media when processing is enabled, but TikTok remains authoritative for its provider limits. RelayAPI's media library accepts direct-to-storage upload sessions up to 200 MiB; the Worker-proxy endpoint remains 50 MiB.
target_options.tiktok
| Field | Required | Behavior |
|---|---|---|
publish_mode | No | direct by default; inbox uses TikTok's official creator-inbox upload and rejects Direct Post-only fields |
privacy_level | Yes | Explicit creator selection; RelayAPI checks it against fresh privacy_level_options |
allow_comment | Yes | Explicit creator choice; true is rejected when comments are disabled for the creator |
allow_duet | Yes for video | Explicit video choice; true is rejected when duet is disabled |
allow_stitch | Yes for video | Explicit video choice; true is rejected when stitch is disabled |
brand_content_toggle | Yes | true means paid partnership/third-party promotion |
brand_organic_toggle | Yes | true means the creator's own brand or business |
content_preview_confirmed | Yes, literal true | Attests that the creator previewed this exact content |
express_consent_given | Yes, literal true | Attests that the creator expressly consented after TikTok's required notice |
source_mode | No | file_upload or pull_from_url; photo posts always use pull mode |
description | No | Video caption override up to 2,200; photo description up to 4,000 |
video_made_with_ai | No | Sends TikTok's AI-generated-content disclosure for video |
video_cover_timestamp_ms | No | Video cover frame position in milliseconds |
photo_cover_index | No | Zero-based carousel cover index; defaults to 0 |
auto_add_music | No | Requests TikTok-recommended music for photos |
content / media | No | TikTok-specific override of the shared post fields |
The fields marked required apply to Direct Post. Inbox mode rejects those
Direct Post choices instead. brand_content_toggle: true cannot be combined
with SELF_ONLY. Video media must include duration_ms so RelayAPI can enforce
both TikTok's global limit and the creator-specific limit before publish
initialization. Legacy draft and commercial_content_type options are
rejected before provider I/O. Use publish_mode: 'inbox' for TikTok's provider
inbox; use the two explicit disclosure booleans for Direct Post.
Processing and errors
RelayAPI persists TikTok's publish_id before continuing, polls
/v2/post/publish/status/fetch/, and reconciles retries from that provider
operation rather than initializing a duplicate post. PROCESSING_UPLOAD and
PROCESSING_DOWNLOAD remain nonterminal; PUBLISH_COMPLETE is terminal even
when TikTok does not expose a public post ID. FAILED records TikTok's failure
reason.
Common pre-provider errors include:
| Code | Meaning |
|---|---|
PRIVACY_LEVEL_UNAVAILABLE | The requested privacy level is absent from fresh creator info |
COMMENTS_DISABLED_BY_CREATOR | The request enabled comments although creator settings disable them |
VIDEO_INTERACTIONS_REQUIRED | A video omitted allow_duet or allow_stitch |
COMMERCIAL_DISCLOSURE_REQUIRED | One of the two disclosure booleans was omitted |
TIKTOK_CONSENT_REQUIRED | Preview confirmation or express consent was not explicitly true |
VIDEO_TOO_LONG_FOR_CREATOR | The media duration exceeds fresh creator info |
VIDEO_DURATION_REQUIRED | Video media omitted the required duration_ms |
TIKTOK_VERIFIED_MEDIA_URL_REQUIRED | Pull-mode media is outside the account's immutable verified prefixes |
PHOTO_FILE_UPLOAD_UNSUPPORTED | A photo request selected file_upload |
DIRECT_OPTION_WITH_INBOX_MODE | An inbox upload included a Direct Post-only choice |
VIDEO_INBOX_DESCRIPTION_UNSUPPORTED | An inbox video supplied a description that must instead be added when completing the post in TikTok |
TikTok also applies creator posting caps, app active-user caps, moderation, and client-audit restrictions. An unaudited TikTok client can be limited to private visibility regardless of RelayAPI's request.
Official TikTok references
- Direct Post API
- Upload video to the creator inbox
- Query Creator Info
- Direct Post UX and consent guidelines
- Media Transfer Guide
- Photo Post API
- Publish status
Found something wrong? Help us improve this page.