How to Post to TikTok via API (Content Posting API)
You post to TikTok via API with TikTok's Content Posting API. Your app gets the video.publish scope, queries the creator's info, initializes the post with FILE_UPLOAD or PULL_FROM_URL, sends or hands over the video, and polls the publish status. Until TikTok audits the app, every post is private. Adeli publishes to TikTok through one API, without your own TikTok app or audit.
To post to TikTok via API, you use TikTok's Content Posting API: query the creator's info, initialize the post, upload the video (or let TikTok pull it from your URL), then poll until it publishes. The API calls are the straightforward part. Whether anyone besides the creator sees the post depends on TikTok's privacy rules and its audit.
This guide covers the flow call by call, with the curl requests from TikTok's own docs. If you haven't picked a portal yet, read TikTok API for Business vs TikTok for Developers first.
Can you post to TikTok via API?#
Yes. The Content Posting API on TikTok for Developers posts videos and photo posts to the account of any TikTok user who authorized your app.
Until your app passes TikTok's Content Posting API audit, everything it posts is private. TikTok's get-started guide says content from unaudited clients "will be restricted to private viewing mode." You can build and test the whole flow before the audit, but you can't ship it to real users.
TikTok API for Business has its own publishing endpoint in the Accounts API. That one only publishes public video posts, and it's covered in the portal comparison.
Direct post or upload to inbox: which one do you need?#
Direct post publishes the video to the creator's profile. Upload to inbox sends it to their TikTok drafts, and the creator finishes and posts it in the TikTok app.
| Direct post | Upload to inbox (draft) | |
|---|---|---|
| Scope | video.publish |
video.upload |
| Init endpoint | /v2/post/publish/video/init/ |
/v2/post/publish/inbox/video/init/ |
| Caption, privacy, interaction settings | Set by your app, in post_info |
Set by the creator in TikTok |
| Who presses "post" | Your app | The creator, from an inbox notification |
| Extra limit | About 15 posts per creator per day | At most 5 pending shares in 24 hours |
| Docs | Direct Post reference | Upload reference |
Pick direct post if your product publishes on the user's behalf, like a scheduler or an AI content tool. Pick upload to inbox if the creator should review and edit in TikTok before anything goes live. TikTok's upload reference says you should tell users to tap the inbox notification to finish the post.
The rest of this guide follows direct post. The upload flow uses the same media transfer step and skips post_info.
What do you need before you start?#
TikTok's get-started guide lists these prerequisites:
- A registered app on TikTok for Developers, with the Content Posting API product added.
- Direct Post enabled in the Content Posting API configuration for your app.
- The
video.publishscope, approved for your app and authorized by the TikTok user through Login Kit. - The user's access token and open ID, from that OAuth flow.
- A video file (MP4 with H.264 is the recommended pairing) or a video URL on a domain you've verified.
Getting the app and the scope approved is its own process, covered in how to get TikTok API access. This post assumes you already have a working access token.
Step 1: Query the creator's info#
Before every post, call creator_info/query. It tells you which privacy levels this creator can use, whether they've turned off comments, Duets, or Stitches, and the longest video they're allowed to post.
curl --location --request POST 'https://open.tiktokapis.com/v2/post/publish/creator_info/query/' \
--header 'Authorization: Bearer act.example12345Example12345Example' \
--header 'Content-Type: application/json; charset=UTF-8'The creator info reference returns:
creator_nicknameandcreator_username. Show the nickname in your UI so the user knows which account they're posting to.privacy_level_options. The only privacy values you're allowed to send for this creator. A private account gets a different list from a public one.comment_disabled,duet_disabled,stitch_disabled. If one istrue, grey out that toggle in your UI.max_video_post_duration_sec. Reject videos longer than this before you upload them.
The same call returns spam_risk_too_many_posts when the creator has hit their daily post cap, so you can stop there instead of uploading a video that can't be posted.
Step 2: Initialize the post#
POST /v2/post/publish/video/init/ creates the post and returns a publish_id. You send the caption and settings in post_info, and tell TikTok how the video is coming in source_info.
curl --location 'https://open.tiktokapis.com/v2/post/publish/video/init/' \
--header 'Authorization: Bearer act.example12345Example12345Example' \
--header 'Content-Type: application/json; charset=UTF-8' \
--data-raw '{
"post_info": {
"title": "this will be a funny #cat video on your @tiktok #fyp",
"privacy_level": "MUTUAL_FOLLOW_FRIENDS",
"disable_duet": false,
"disable_comment": true,
"disable_stitch": false,
"video_cover_timestamp_ms": 1000
},
"source_info": {
"source": "FILE_UPLOAD",
"video_size": 50000123,
"chunk_size": 10000000,
"total_chunk_count": 5
}
}'Other fields to get right, from the Direct Post reference:
titleis the caption, hashtags and mentions included, up to 2,200 UTF-16 runes.privacy_levelmust be one of theprivacy_level_optionsfrom step 1, or the request fails withprivacy_level_option_mismatch.brand_content_toggleandbrand_organic_togglemark paid partnerships and self-promotion.is_aigclabels AI-generated content.
For FILE_UPLOAD, the response includes an upload_url that's valid for one hour. For PULL_FROM_URL, there's no upload URL, because TikTok fetches the file.
Step 3: Upload the video to TikTok#
There are two ways to get the video to TikTok: push the bytes yourself with FILE_UPLOAD, or have TikTok download it with PULL_FROM_URL. The media transfer guide sets the rules for both.
FILE_UPLOAD |
PULL_FROM_URL |
|
|---|---|---|
| How it works | You PUT the file to upload_url |
TikTok downloads from your video_url |
| Setup | None | Verify the domain or URL prefix in the developer portal first |
| Size rules | Chunks of 5–64 MB, final chunk up to 128 MB, 1 to 1,000 chunks | The URL must use HTTPS, return no redirects, and stay reachable for up to an hour |
| Best for | Files on your server or in memory | Files already in object storage or on a CDN you control |
For FILE_UPLOAD, send each chunk with a Content-Range header:
curl --location --request PUT 'https://open-upload.tiktokapis.com/upload/?upload_id=67890&upload_token=Xza123' \
--header 'Content-Range: bytes 0-30567099/30567100' \
--header 'Content-Length: 30567100' \
--header 'Content-Type: video/mp4' \
--data '@/path/to/file/example.mp4'total_chunk_count is video_size divided by chunk_size, rounded down. The leftover bytes go in the last chunk, which is why it's allowed to be bigger than the rest.
For PULL_FROM_URL, swap the source_info block in step 2 for:
"source_info": {
"source": "PULL_FROM_URL",
"video_url": "https://example.verified.domain.com/example_video.mp4"
}A URL on a domain you haven't verified returns url_ownership_unverified. Serve the file from a domain or URL prefix you've verified.
Whichever source you use, the video has to meet TikTok's specs: MP4, WebM, or MOV, up to 4 GB, 23 to 60 FPS, 360 to 4,096 pixels on each side, and no longer than the creator's max_video_post_duration_sec.
Step 4: Check the post status#
Poll POST /v2/post/publish/status/fetch/ with the publish_id until the status is PUBLISH_COMPLETE or FAILED. A 200 from the init call only means TikTok accepted the job.
curl --location 'https://open.tiktokapis.com/v2/post/publish/status/fetch/' \
--header 'Authorization: Bearer act.example12345Example12345Example' \
--header 'Content-Type: application/json; charset=UTF-8' \
--data '{
"publish_id": "v_pub_url~v2.123456789"
}'The status reference lists five states: PROCESSING_UPLOAD, PROCESSING_DOWNLOAD, SEND_TO_USER_INBOX, PUBLISH_COMPLETE, and FAILED. A failure comes with a fail_reason such as file_format_check_failed, duration_check_failed, or spam_risk.
PUBLISH_COMPLETE doesn't mean the post is public yet. The publicaly_available_post_id field (TikTok's spelling) only appears after the post is public and has cleared TikTok moderation. If you'd rather not poll, the Content Posting webhooks send post.publish.complete, post.publish.failed, and post.publish.publicly_available events.
What privacy levels can you set, and what does TikTok require?#
privacy_level takes PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, or SELF_ONLY, limited to whatever privacy_level_options returned for that creator. TikTok's Content Sharing Guidelines also set rules for the screen your users post from:
- No default privacy. The user picks a privacy level every time. Your UI can't preselect one.
- Interaction toggles start off. Comment, Duet, and Stitch are unchecked by default, and greyed out when the creator has disabled them.
- Branded content can't be private. If the user marks a post as Branded Content,
SELF_ONLYis off the table. - Music Usage Confirmation. The screen shows "By posting, you agree to TikTok's Music Usage Confirmation," with the Branded Content Policy added for branded posts.
- Preview and a processing notice. Users see the content before posting, and you tell them it can take a few minutes to show up on their profile.
These rules are what the audit checks. When we prepared our own Content Posting API submission, the demo video had to show every one of these controls on the posting screen, starting with nothing selected. A tour of the dashboard doesn't cover it.
Why are my TikTok API posts private?#
Your posts are private because your app hasn't passed the Content Posting API audit. Until it does, the Content Sharing Guidelines apply three limits:
- Posts are
SELF_ONLY. Only the creator can see them. - The posting account must be private. A post to a public account fails with
unaudited_client_can_only_post_to_private_accounts. - Five users per 24 hours. At most 5 users can post through your app in any 24-hour window.
Apply for the audit through TikTok's Content Posting API application. Passing it removes the private-only restriction but not the caps. Audited apps still get a daily quota of active posting users (reached_active_user_cap), and creators still hit a per-account limit that's typically around 15 posts per day.
What rate limits apply when you post to TikTok via API?#
Every Content Posting API limit is counted per user access token, per minute.
| Endpoint | Limit |
|---|---|
creator_info/query |
20 requests per minute |
video/init (direct post) |
6 requests per minute |
inbox/video/init (upload) |
6 requests per minute |
status/fetch |
30 requests per minute |
Sources: the creator info, Direct Post, upload, and status references. Six inits a minute is more than one person posting by hand will use. A scheduler that releases a backlog for one account all at once can hit it, so space those posts out.
Is there a faster way to post to TikTok via API?#
Adeli publishes to TikTok through the same endpoint as Instagram, Facebook, YouTube, and X. Adeli maintains the TikTok developer app, goes through TikTok's review process, and handles token refresh for every connected account, so the audit and the private-only phase above aren't yours to run.
- One request, every network. Add TikTok to the platforms in a publish call. See the posting API.
- Your users' own accounts. Each user connects their own TikTok account, isolated from everyone else's.
- Clear failures. Every publish returns a per-platform status, plus a webhook when anything changes, so a TikTok failure doesn't block your other networks.
- Scheduling built in. Send a scheduled time instead of
publishNowand the post waits in the queue.
Your first 3 connected accounts are free; pricing has the rest of the ladder. The TikTok API page shows what Adeli supports on TikTok.