The TikTok API is several APIs: which one do you need?
“TikTok API” covers a handful of separate products with separate sign-ups, and picking the wrong one wastes weeks of review. TikTok for Developers hosts the ones for apps and creators; ads and commerce live elsewhere.
| API | What it does | Use it if you want to |
|---|---|---|
| Content Posting API | Publishes videos and photo posts to a creator’s profile (Direct Post) or sends them to their inbox as drafts (Upload) | Post or schedule TikTok content from your app |
| Login Kit | OAuth sign-in with TikTok; returns the access token and open_id every other user API needs | Connect a user’s TikTok account (required for posting) |
| Display API | /v2/user/info/, /v2/video/list/, /v2/video/query/ with user.info.basic and video.list | Show a connected user’s profile and their public videos |
| Research API | Public videos, comments and accounts for approved researchers | Do non-commercial academic or non-profit research |
| TikTok API for Business (Marketing API) | Campaigns, ads, audiences and reporting, on business-api.tiktok.com | Manage TikTok ads programmatically |
| TikTok Shop Partner API | Products, orders and fulfilment for TikTok Shop sellers, on partner.tiktokshop.com | Build commerce or logistics integrations |
Sources: Content Posting API, Display API, Research API. Read 29 September 2026.
The Research API is the one people most often apply for by mistake. TikTok limits it to researchers at academic institutions or non-profit organisations in eligible regions (the US, EEA, UK, Canada, Switzerland, and Brazil for youth-safety research) who work on a non-commercial basis. A marketing tool or a trend dashboard will not qualify. The rest of this guide is about the Content Posting API.
How to get a TikTok API key
TikTok does not issue a single “API key”. You get a client key and client secret per app, then a user access token per creator. From the app registration guide:
- Create a developer account at developers.tiktok.com, and ideally an organization to own the app.
- Connect an app: profile icon → Manage apps → Connect an app. The Credentials section shows the client key and client secret. Keep the secret on your server; TikTok’s guidelines forbid embedding it in open source code.
- Fill in app details: icon (1024×1024), name, category, description, platforms (Web needs your website URL), terms of service and privacy policy URLs.
- Add products: Login Kit (with your redirect URIs: https, static, no query string or fragment, up to 10) and the Content Posting API. For publishing, turn on the Direct Post configuration.
- Add scopes:
user.info.basiccomes with Login Kit. Addvideo.publishfor Direct Post and/orvideo.uploadfor inbox drafts. - Verify URL properties: apps created after 9 September 2024 must verify their terms, privacy and website URLs, and any domain you will use with
PULL_FROM_URLmust be verified too, by DNS record or signature file. - Submit for review with an explanation of how each product and scope is used and at least one demo video of the full flow (up to 5 videos, 50 MB each).
Sandbox mode lets you test integrations before the app is approved. App review and the Direct Post audit are two different gates: review makes your app live, the audit decides whether its posts can be public. More on that below.
OAuth: getting a user access token
https://www.tiktok.com/v2/auth/authorize/
?client_key=YOUR_CLIENT_KEY
&response_type=code
&scope=user.info.basic,video.publish
&redirect_uri=https://yourapp.com/callback/
&state=RANDOM_STRINGCheck that state matches on the callback, then exchange the code:
# Code -> access token (24 h) + refresh token (365 days)
curl -X POST https://open.tiktokapis.com/v2/oauth/token/ \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "client_key=YOUR_CLIENT_KEY" \
--data-urlencode "client_secret=YOUR_CLIENT_SECRET" \
--data-urlencode "code=AUTH_CODE" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "redirect_uri=https://yourapp.com/callback/"
# Refresh before the 24 h are up (store the refresh_token you get back)
curl -X POST https://open.tiktokapis.com/v2/oauth/token/ \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "client_key=YOUR_CLIENT_KEY" \
--data-urlencode "client_secret=YOUR_CLIENT_SECRET" \
--data-urlencode "grant_type=refresh_token" \
--data-urlencode "refresh_token=REFRESH_TOKEN"
Access tokens last 24 hours, refresh tokens 365 days (token docs). Refresh in a background job, and save the refresh_token from every refresh response: TikTok may rotate it, and code that keeps using the original is the usual reason TikTok connections die after a few days.
Direct Post vs Upload (inbox drafts)
| Direct Post | Upload | |
|---|---|---|
| Scope | video.publish | video.upload |
| Video endpoint | /v2/post/publish/video/init/ | /v2/post/publish/inbox/video/init/ |
| Result | Published to the profile with your caption and settings | Draft in the creator’s inbox; they finish and post it in the app |
| Caption and privacy | Sent in post_info | Set by the creator in TikTok (video drafts take only the file) |
| Before the audit | Private (SELF_ONLY) only, private accounts only | Not subject to the Direct Post audit |
| Daily cap | ~15 posts per creator (shared across apps) | 5 pending drafts per creator per 24 hours |
Sources: Direct Post, Upload, content sharing guidelines. Read 29 September 2026.
Upload is useful when a person reviews every post anyway, or while you wait for the audit. It is not automation: nothing goes live until the creator taps the notification.
How to post a video with the TikTok Content Posting API
Step 1: query creator info (mandatory)
curl -X POST https://open.tiktokapis.com/v2/post/publish/creator_info/query/ \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json; charset=UTF-8"
# -> "data": {
# "creator_nickname": "Acme Studio",
# "privacy_level_options": ["PUBLIC_TO_EVERYONE", "MUTUAL_FOLLOW_FRIENDS", "SELF_ONLY"],
# "comment_disabled": false, "duet_disabled": false, "stitch_disabled": true,
# "max_video_post_duration_sec": 300
# }
This call is not optional. The privacy_level you send must be one of the returned privacy_level_options, or the post fails with privacy_level_option_mismatch. A public account gets PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS and SELF_ONLY; a private account gets FOLLOWER_OF_CREATOR, MUTUAL_FOLLOW_FRIENDS and SELF_ONLY. Check the video length against max_video_post_duration_sec, since different creators have different limits. If this call returns spam_risk_too_many_posts (with HTTP 200), stop and tell the user to try later.
Step 2: initialize, upload, check status
# 1. Initialize the post (FILE_UPLOAD: you send the bytes)
curl -X POST https://open.tiktokapis.com/v2/post/publish/video/init/ \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json; charset=UTF-8" \
-d '{
"post_info": {
"title": "Day 12 of building in public #buildinpublic",
"privacy_level": "PUBLIC_TO_EVERYONE",
"disable_duet": false,
"disable_comment": false,
"disable_stitch": false,
"video_cover_timestamp_ms": 1000
},
"source_info": {
"source": "FILE_UPLOAD",
"video_size": 30567100,
"chunk_size": 30567100,
"total_chunk_count": 1
}
}'
# -> {"data": {"publish_id": "v_pub_file~v2-1.123", "upload_url": "https://open-upload.tiktokapis.com/video/?upload_id=...&upload_token=..."}}
# 2. PUT the file to upload_url (valid for 1 hour)
curl -X PUT "UPLOAD_URL" \
-H "Content-Type: video/mp4" \
-H "Content-Range: bytes 0-30567099/30567100" \
--data-binary @clip.mp4
# 3. Check the status
curl -X POST https://open.tiktokapis.com/v2/post/publish/status/fetch/ \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json; charset=UTF-8" \
-d '{"publish_id": "v_pub_file~v2-1.123"}'
The caption goes in title, up to 2,200 UTF-16 runes, with hashtags and mentions parsed from the text. Other post_info fields: disable_duet, disable_stitch, disable_comment, video_cover_timestamp_ms, brand_content_toggle (paid partnership), brand_organic_toggle (promoting your own business) and is_aigc (AI-generated label).
The status endpoint returns PROCESSING_UPLOAD, PROCESSING_DOWNLOAD, SEND_TO_USER_INBOX, PUBLISH_COMPLETE or FAILED with a fail_reason. Public posts go through moderation first, which usually takes under a minute but can take hours, and the post ID only appears once it passes (status docs). Webhooks (post.publish.complete, post.publish.failed, post.publish.publicly_available) save you the polling.
FILE_UPLOAD or PULL_FROM_URL?
With PULL_FROM_URL, TikTok downloads the file from your server:
"source_info": {
"source": "PULL_FROM_URL",
"video_url": "https://media.yourdomain.com/tiktok/clip.mp4"
}
The URL must sit under a domain or URL prefix verified in your app, use https and not redirect (a 3xx counts as invalid), or you get url_ownership_unverified. The download times out after an hour. TikTok’s guidelines actually tell you to use PULL_FROM_URL when the file is already on your servers and FILE_UPLOAD when it is on the user’s device.
With FILE_UPLOAD, chunk rules are strict (media transfer guide): chunks of 5 to 64 MB, sent in order; files under 5 MB go in one piece; files over 64 MB must be chunked; 1 to 1,000 chunks; the last chunk takes the leftover bytes and can be up to 128 MB. total_chunk_count is video_size / chunk_size rounded down. Each chunk returns 206, the last one 201.
Photo posts and carousels
curl -X POST https://open.tiktokapis.com/v2/post/publish/content/init/ \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"media_type": "PHOTO",
"post_mode": "DIRECT_POST",
"post_info": {
"title": "5 desk setups",
"description": "Which one would you pick? #desksetup",
"privacy_level": "PUBLIC_TO_EVERYONE",
"auto_add_music": true
},
"source_info": {
"source": "PULL_FROM_URL",
"photo_cover_index": 0,
"photo_images": [
"https://media.yourdomain.com/desks/1.jpg",
"https://media.yourdomain.com/desks/2.jpg"
]
}
}'
Photos use a different endpoint, /v2/post/publish/content/init/, with media_type=PHOTO and post_mode set to DIRECT_POST or MEDIA_UPLOAD. Up to 35 images, WebP or JPEG, up to 1080p and 20 MB each, and only by PULL_FROM_URL, so a verified domain is required. The title is limited to 90 characters and the description to 4,000. Photo drafts need TikTok app version 31.8 or later on the user’s phone, otherwise you get app_version_check_failed.
The audit: what unaudited apps can do
This is the part that stops most projects. From TikTok’s content sharing guidelines, an unaudited API client:
- can let up to 5 users post in a 24-hour window;
- can only post to accounts that are set to private at the time of posting;
- can only post with
SELF_ONLYvisibility. To make a post public later, the owner has to switch the account to public and change each post to “Everyone” by hand.
Posting to a public account from an unaudited client fails at init with unaudited_client_can_only_post_to_private_accounts. To lift the restriction you apply for the audit, which checks your integration against TikTok’s terms and its required UX. That UX is specific: show the creator’s nickname; a privacy dropdown with no default value; comment, duet and stitch toggles that are off by default and greyed out when the creator has disabled them; a commercial content disclosure with “Your brand” and “Branded content”; the “By posting, you agree to TikTok’s Music Usage Confirmation” line; a preview of the post; and editable captions. No watermarks or promotional branding on the content.
The guidelines also say who will not pass. “An app that copies arbitrary contents from other platforms to TikTok” and “a utility tool to help upload contents to the account(s) you or your team manages” are both listed as not acceptable. API clients must be intended for a wide audience. If you are an agency or a brand automating your own accounts, the audit is not a route open to you.
TikTok API limits
| Limit | Value |
|---|---|
| Posts per creator | Typically around 15 per 24 hours via Direct Post, shared across all apps |
| Pending inbox drafts | 5 per creator per 24 hours |
| Active posting users per app | Daily cap set from the usage estimate in your audit application |
| Unaudited app | 5 users per 24 hours, private accounts, SELF_ONLY |
| Rate limits (per user token) | Video/photo init 6 per minute, creator info 20 per minute, status 30 per minute |
| Video | MP4 (recommended), WebM, MOV; H.264, H.265, VP8, VP9; 23–60 fps; 360–4096 px per side; up to 4 GB |
| Video length | Up to 10 minutes via the API; each creator’s own limit is in max_video_post_duration_sec |
| Photos | Up to 35 per post; WebP or JPEG; max 1080p and 20 MB each |
| Caption | 2,200 UTF-16 runes (video); photo title 90, description 4,000 |
| Upload URL | Valid for 1 hour |
| Tokens | Access 24 hours, refresh 365 days |
Sources: content sharing guidelines, Direct Post, creator info, post status, media transfer, photo post, tokens. Read 29 September 2026.
Common TikTok API errors and how to fix them
| error.code | What TikTok says | Fix |
|---|---|---|
| unaudited_client_can_only_post_to_private_accounts | Unaudited clients can only post to a private account. | Test on a private account with SELF_ONLY, or pass the audit. |
| spam_risk_too_many_posts | The daily post cap from the API is reached for the current user. | Stop and retry the next day. Retrying now will not help. |
| spam_risk_too_many_pending_share | The daily upload cap from the API is reached (max 5 pending shares in 24 hours). | Have the creator post or clear the drafts in their inbox. |
| reached_active_user_cap | The daily quota for active publishing users from your client is reached. | An app-wide cap. Queue until the next day or apply for a higher estimate. |
| spam_risk_user_banned_from_posting | The user is banned from making new posts. | Only the account owner can resolve it with TikTok. |
| privacy_level_option_mismatch | privacy_level is not specified or not among the creator’s options. | Use a value from creator_info/query. |
| url_ownership_unverified | The domain or URL prefix of the media URL is not verified. | Verify it under URL properties, or switch to FILE_UPLOAD. |
| scope_not_authorized | The access_token does not bear the user’s grant on video.publish. | Re-authorize with the scope; check it is approved on the app. |
| access_token_invalid | The access_token is invalid or has expired. | Refresh it; if the refresh token is gone, re-authorize. |
| rate_limit_exceeded | Blocked for exceeding the API rate limit. | Back off; init allows 6 calls per minute per token. |
Sources: Direct Post errors, Upload errors. Read 29 September 2026.
TikTok API with Python
TikTok has no official Python SDK for the Content Posting API, and the flow is plain HTTPS. This script checks creator info, uploads a video in chunks that follow TikTok’s rules, and waits for the final status. It needs only requests.
import os
import time
import requests
API = "https://open.tiktokapis.com/v2"
TOKEN = "ACCESS_TOKEN"
HEADERS = {"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json; charset=UTF-8"}
MB = 1024 * 1024
def creator_info():
r = requests.post(f"{API}/post/publish/creator_info/query/", headers=HEADERS)
body = r.json()
if body["error"]["code"] != "ok":
# e.g. spam_risk_too_many_posts: stop and ask the user to try later
raise RuntimeError(body["error"])
return body["data"]
def direct_post(path, caption, privacy):
size = os.path.getsize(path)
# Chunks of 5-64 MB; files under 5 MB go whole; the last chunk
# absorbs the remainder (up to 128 MB)
chunk = min(10 * MB, size)
count = max(1, size // chunk)
r = requests.post(f"{API}/post/publish/video/init/", headers=HEADERS, json={
"post_info": {"title": caption, "privacy_level": privacy},
"source_info": {"source": "FILE_UPLOAD", "video_size": size,
"chunk_size": chunk, "total_chunk_count": count},
})
body = r.json()
if body["error"]["code"] != "ok":
raise RuntimeError(body["error"])
publish_id = body["data"]["publish_id"]
upload_url = body["data"]["upload_url"]
with open(path, "rb") as f:
for i in range(count):
start = i * chunk
end = size - 1 if i == count - 1 else start + chunk - 1
f.seek(start)
data = f.read(end - start + 1)
requests.put(upload_url, data=data, headers={
"Content-Type": "video/mp4",
"Content-Range": f"bytes {start}-{end}/{size}",
}).raise_for_status() # 206 per chunk, 201 at the end
return publish_id
def wait(publish_id):
while True: # status endpoint: 30 requests per minute per token
r = requests.post(f"{API}/post/publish/status/fetch/", headers=HEADERS,
json={"publish_id": publish_id})
data = r.json()["data"]
if data["status"] in ("PUBLISH_COMPLETE", "FAILED"):
return data
time.sleep(5)
info = creator_info()
privacy = "PUBLIC_TO_EVERYONE"
if privacy not in info["privacy_level_options"]:
raise SystemExit(f"Pick one of {info['privacy_level_options']}")
print(wait(direct_post("clip.mp4", "Day 12 #buildinpublic", privacy)))In a real app, the privacy value comes from a dropdown the user picks from (TikTok requires no default), and the token refresh runs in its own job.
The shortcut: post to TikTok with one Upload-Post call
Building on the Content Posting API means an app review, an audit you may not qualify for, verified domains, chunked uploads, rotating refresh tokens and status polling. Upload-Post’s TikTok API is an audited TikTok integration: you connect a TikTok account once in the dashboard, and your posts go out publicly through the official Content Posting API. You send the file itself, so there is no domain to verify:
curl -X POST https://api.upload-post.com/api/upload \
-H "Authorization: Apikey YOUR_KEY" \
-F "[email protected]" \
-F "user=my-brand" \
-F "platform[]=tiktok" \
-F "tiktok_title=Day 12 of building in public #buildinpublic" \
-F "privacy_level=PUBLIC_TO_EVERYONE" \
-F "disable_duet=false" \
-F "disable_stitch=false" \
-F "cover_timestamp=2500" \
-F "brand_organic_toggle=true" \
-F "scheduled_date=2026-10-01T18:00:00" \
-F "timezone=America/Los_Angeles"
The fields map to TikTok’s: privacy_level, disable_duet, disable_comment, disable_stitch, cover_timestamp, brand_content_toggle, brand_organic_toggle and is_aigc. post_mode=MEDIA_UPLOAD sends the video to the creator’s drafts instead of publishing. Connected accounts can also take a custom cover image, a track from TikTok’s Commercial Music Library (tiktok_music_id) and a location tag. The Upload Video docs list every field.
Photo carousels, to TikTok and Instagram in the same request, with files instead of URLs:
curl -X POST https://api.upload-post.com/api/upload_photos \
-H "Authorization: Apikey YOUR_KEY" \
-F "user=my-brand" \
-F "platform[]=tiktok" \
-F "platform[]=instagram" \
-F "photos[][email protected]" \
-F "photos[][email protected]" \
-F "photos[][email protected]" \
-F "title=5 desk setups" \
-F "description=Which one would you pick? #desksetup" \
-F "auto_add_music=true"The creator-info step becomes one GET, so your UI can offer only the privacy levels the account allows:
curl -G https://api.upload-post.com/api/uploadposts/tiktok/settings \
-H "Authorization: Apikey YOUR_KEY" \
-d profile=my-brand
# -> {"privacy_level_options": [...], "max_video_post_duration_sec": 600, ...}- Scheduling:
scheduled_datewith atimezone, up to 365 days ahead, oradd_to_queue=truefor the next free slot. The Content Posting API has no scheduling of its own. - Status: TikTok processes posts asynchronously; poll Upload Status or use webhooks to get the post URL.
- Cross-posting: add
platform[]=youtubeandplatform[]=instagramto send the same vertical video to Shorts and Reels.
You can create an account and API key for free, with no card. TikTok publishing is included in the paid plans, which start at $24 a month ($16 billed annually) with unlimited uploads. For the no-code route, see how to schedule TikTok posts; to post from an AI assistant, use the TikTok MCP server. Sending the same clips to YouTube? Read the YouTube API guide.
Frequently asked questions
Is there an official TikTok API?
Yes, several. TikTok for Developers (developers.tiktok.com) publishes Login Kit, the Content Posting API, the Display API, the Research API and others. Ads are a separate product, the TikTok API for Business, and TikTok Shop has its own Partner API. For publishing videos and photos to a creator’s account, the one you want is the Content Posting API.
How do I get a TikTok API key?
Sign up at developers.tiktok.com, open Manage apps and click Connect an app. The app page shows a Client key and Client secret under Credentials; together they are what people call the TikTok API key. Before real users can use the app you add products and scopes, verify your URLs and submit the app for review with a demo video.
Is the TikTok API free?
TikTok lists no fee or paid tier for Login Kit, the Content Posting API or the Display API. What it costs is approval time: an app review before release, and a separate audit before your posts can be public. The Research API is free but only for approved non-profit and academic researchers.
Why are my TikTok API posts private?
Your API client has not passed TikTok’s audit. Unaudited clients can only post with SELF_ONLY visibility, only to accounts that are set to private at the time of posting, and only for up to 5 users in 24 hours. Posting to a public account fails with unaudited_client_can_only_post_to_private_accounts. You lift this by applying for the Direct Post audit.
How many videos can I post to TikTok per day through the API?
TikTok caps posts per creator account through Direct Post at typically around 15 a day, and that cap is shared across every app the creator uses. Hitting it returns spam_risk_too_many_posts. The init endpoint also allows only 6 requests per minute per user token, and each app has a daily cap on active posting users.
What is the difference between Direct Post and Upload?
Direct Post (video.publish scope) publishes the video to the profile with the caption and settings you send. Upload (video.upload scope) sends the video to the creator’s TikTok inbox as a draft; they get a notification and finish the post in the TikTok app. Upload does not need the Direct Post audit, but at most 5 drafts can be pending in 24 hours.
Can I build an internal tool to post to my own TikTok accounts?
Not through the audit. TikTok’s content sharing guidelines list “a utility tool to help upload contents to the account(s) you or your team manages” as not acceptable, and require API clients to be intended for a wide audience. Unaudited, such a tool can only post privately. Teams in this position usually go through an already audited service.
Can I post TikTok photo carousels through the API?
Yes. POST /v2/post/publish/content/init/ with media_type PHOTO takes up to 35 images per post, as either a direct post or an inbox draft. Photos can only be sent with PULL_FROM_URL, so the images must sit on a domain or URL prefix you have verified in your TikTok app. Titles are limited to 90 characters and descriptions to 4,000.
Do I need a verified domain to use the TikTok API?
Only for PULL_FROM_URL, where TikTok downloads the file from your server: the URL must be under a domain or URL prefix verified in your app, use https and not redirect. FILE_UPLOAD, where you PUT the bytes to TikTok, needs no domain. Separately, apps created after 9 September 2024 must verify their terms of service, privacy policy and website URLs before review.
How long do TikTok access tokens last?
An access token is valid for 24 hours and a refresh token for 365 days. Refresh from a background job with grant_type=refresh_token, and always store the refresh token that comes back: TikTok may return a different one than you sent, and the old one stops being the right one to use.