TikTok publishing is asynchronous. A successful request means TikTok accepted the content, not that it is live. Adeli publishes through TikTok API for Business, which pulls every video and photo from a URL: whatever you send — base64, multipart, or a URL — Adeli stages it on its own verified media host first.
Videos are public or drafts
TikTok for Business has no privacy setting for videos. A DIRECT_POST video
is published to everyone, so its privacy_level must be
PUBLIC_TO_EVERYONE or left out; anything else is
422 privacy_level_unsupported rather than a post that is more public than
you asked for. To keep a video private, send post_mode: "MEDIA_UPLOAD": it
arrives as a draft in the creator's TikTok inbox. Photo posts accept every
privacy_level the creator currently allows.
TikTok allows 15 API posts per account per day.
Before a Direct Post, check creator capabilities first.
Body
platform"tiktok"RequiredaccountIduuidRequired- A connected TikTok account.
profileIduuid- Optional, because accountId names its profile. If you pass both, they must agree.
post_mode"DIRECT_POST" | "MEDIA_UPLOAD"- Defaults to
DIRECT_POST, which publishes.MEDIA_UPLOADsends a draft to the creator's TikTok inbox to finish in the app; a video draft carries the media only, a photo draft its title and description too. tiktok_titlestring- Up to 2,200 characters for a video, and up to 90 for photos.
tiktok_descriptionstring- Up to 4,000 characters. Photo posts only.
privacy_levelstring- Videos:
PUBLIC_TO_EVERYONEor omitted. Photos: one ofPUBLIC_TO_EVERYONE,MUTUAL_FOLLOW_FRIENDS,FOLLOWER_OF_CREATOR,SELF_ONLYthat the creator currently allows; defaults toPUBLIC_TO_EVERYONE. music_usage_confirmedtrueRequired- Must be literally true. You are confirming the creator agreed to TikTok's music usage terms.
disable_commentboolean- Defaults to false.
disable_duetboolean- Video only. Defaults to false.
disable_stitchboolean- Video only. Defaults to false.
cover_timestampinteger- Video only. Milliseconds into the video to use as the cover. Defaults to 1000.
photo_cover_indexinteger- Photos only. Defaults to 0, and must be less than the number of photos.
auto_add_musicboolean- Photos only. Defaults to false.
is_aigcboolean- Marks the content as AI-generated. Defaults to false.
brand_content_toggleboolean- Paid partnership. Requires
privacy_level: \"PUBLIC_TO_EVERYONE\"on a Direct Post. brand_organic_toggleboolean- Promotes the creator's own brand.
videoobject- Either
{ content_type: "video/mp4", base64 }or{ url }pointing at public HTTPS. Mutually exclusive withphotos. photosobject[]- 1 to 35 photos, each
{ content_type, base64 }. Remote photo URLs are not accepted. Mutually exclusive withvideo.
Exactly one of video or photos
Sending both, or neither, is 400 invalid_request.
The request example sends a video from a public URL to the creator's inbox. A video published publicly:
{
"platform": "tiktok",
"accountId": "00000000-0000-0000-0000-000000000000",
"tiktok_title": "Morning chorus #birds",
"privacy_level": "PUBLIC_TO_EVERYONE",
"disable_duet": false,
"disable_stitch": true,
"music_usage_confirmed": true,
"video": { "url": "https://media.example/video.mp4" }
}A photo carousel posted directly, visible only to the creator:
{
"platform": "tiktok",
"accountId": "00000000-0000-0000-0000-000000000000",
"post_mode": "DIRECT_POST",
"tiktok_title": "Backyard birds",
"tiktok_description": "Cardinal, Titmouse, and Robin",
"privacy_level": "SELF_ONLY",
"photo_cover_index": 0,
"auto_add_music": false,
"music_usage_confirmed": true,
"photos": [
{ "content_type": "image/webp", "base64": "..." },
{ "content_type": "image/jpeg", "base64": "..." }
]
}Multipart uploads
For large videos, send multipart/form-data instead. Scalar fields use the same
names, booleans must be the strings "true" or "false", and the media is
either one video file or 1–35 repeated photos[] files. Multipart is TikTok
only.
curl --fail-with-body -X POST "https://app.tryadeli.com/api/v1/posts" \
-H "Authorization: Bearer $ADELI_API_KEY" \
-F 'platform=tiktok' \
-F "accountId=$ACCOUNT_ID" \
-F 'post_mode=DIRECT_POST' \
-F 'privacy_level=PUBLIC_TO_EVERYONE' \
-F 'tiktok_title=Morning chorus' \
-F 'music_usage_confirmed=true' \
-F 'video=@video.mp4;type=video/mp4'curl --fail-with-body -X POST "https://app.tryadeli.com/api/v1/posts" \
-H "Authorization: Bearer $ADELI_API_KEY" \
-F 'platform=tiktok' \
-F "accountId=$ACCOUNT_ID" \
-F 'post_mode=MEDIA_UPLOAD' \
-F 'music_usage_confirmed=true' \
-F 'photos[]=@cardinal.webp;type=image/webp' \
-F 'photos[]=@robin.jpg;type=image/jpeg'The response
Success returns 202 with a publishId. Pass it to
TikTok publish status to learn
whether the post went live.
statusUrl is a relative path
Join it to your base URL before requesting it. warnings is present only when
non-empty — inbox uploads report the settings TikTok ignored, since a video
sent to the inbox carries media and nothing else.