Skip to main content

How do I post to TikTok with an API?

Send a multipart/form-data request to Upload-Post's POST https://api.upload-post.com/api/upload endpoint with your video file (or a public video URL), a user profile and platform[]=tiktok. Upload-Post runs its own approved TikTok app, so there's no developer application to register, no TikTok audit to pass, and no OAuth tokens to manage. You connect the TikTok account once in the dashboard and that's it. One caveat: TikTok posting needs a paid plan, it's not available on Free.

Steps

  1. Create an account at upload-post.com and generate an API key in the dashboard under API Keys (see Authentication).
  2. Connect a TikTok account to a profile at Manage Users. It's the normal TikTok login, no developer setup. For posting on behalf of your users, use white-label JWT connect links.
  3. Call the upload endpoint:
curl \
-H 'Authorization: Apikey your-api-key-here' \
-F 'video=@/path/to/your/video.mp4' \
-F 'title="Your Video Title"' \
-F 'user="test"' \
-F 'platform[]=tiktok' \
-X POST https://api.upload-post.com/api/upload

video also accepts a public URL instead of a file: -F 'video="https://example.com/videos/myvideo.mp4"'.

Useful TikTok parameters

All parameters are documented in the Upload Video reference. The most used ones:

ParameterWhat it doesDefault
tiktok_titleTikTok-specific caption (falls back to title). Max 2,200 chars for video.title
post_modeDIRECT_POST publishes immediately; MEDIA_UPLOAD sends the video to the user's TikTok inbox/drafts.DIRECT_POST
privacy_levelPUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLYPUBLIC_TO_EVERYONE
disable_comment / disable_duet / disable_stitchTurn off comments, duets or stitchesfalse
cover_timestampVideo frame (ms) to use as cover1000
is_aigcDeclare AI-generated contentfalse
scheduled_dateISO-8601 date to schedule the postnone
first_comment / tiktok_first_commentAuto-post a first comment under the published post (video and photos). Needs a reconnected account — see After publishingnone
async_uploadReturn immediately with a request_id and process in background (recommended)false
Draft mode gets better organic reach

We recommend post_mode=MEDIA_UPLOAD (Draft): the video lands in the TikTok inbox and the user publishes it from the app, which typically performs better in TikTok's distribution. In Draft mode TikTok ignores title/privacy metadata sent via API.

TikTok photo slideshows

Post images (with optional automatic music) through the Upload Photos endpoint:

curl -X POST https://api.upload-post.com/api/upload_photos \
-H 'Authorization: Apikey your-api-key-here' \
-F 'photos[][email protected]' \
-F 'photos[][email protected]' \
-F 'title="Photo slideshow with music"' \
-F 'user="test"' \
-F 'platform[]=tiktok' \
-F 'auto_add_music=true'

What your account can do: capabilities

Not every TikTok connection accepts the same optional fields. GET /api/uploadposts/users returns a capabilities array on the TikTok account of each profile — music, location, cover_image, draft, profile_analytics, comments, trend_search and friends. Read it and offer only what the connection actually supports, instead of sending a field and hoping.

Two of them, comments and trend_search, only appear once the account has been reconnected: TikTok grants those permissions at the moment of connecting, so an account linked a while ago never received them. That account keeps publishing exactly as before — nothing about its uploads changes — but the comment and keyword-search endpoints answer 400 with error_code: "tiktok_reconnect_required" until its owner reconnects it from Manage Users (or through a white-label connect link, if your users never see our dashboard).

Sending an unsupported field is never fatal: the post is published and the response carries a warnings array naming what was ignored.

After publishing: comments

A TikTok post is not the end of the job — the comments under it are where the reach is decided.

A first comment with the post. Send first_comment (or tiktok_first_comment to override it just for TikTok) on the upload and it is posted under the video or photo carousel as soon as it goes live. It never fails the publish: by the time it is attempted the post is already up, so anything that goes wrong is a warnings entry with success: true. It is skipped, with a warning, for a post sent to drafts — there is nothing published to comment on yet.

Reading and answering them. The Comments API takes platform=tiktok on all three operations, with post_id = the video id the upload returned:

# List the comments on a video
curl 'https://api.upload-post.com/api/uploadposts/comments?platform=tiktok&user=test&post_id=7401234567890123456' \
-H 'Authorization: Apikey your-api-key-here'

# Reply to one of them (TikTok needs post_id even when replying)
curl -X POST https://api.upload-post.com/api/uploadposts/comments/create \
-H 'Authorization: Apikey your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{"platform":"tiktok","user":"test","post_id":"7401234567890123456","comment_id":"7401234567890999888","message":"Thanks! Guide in the bio."}'

Replies and moderation. Both stay inside the same Comments API. The replies under a comment are the listing narrowed to a parent — add comment_id to GET /comments — and the moderation verbs are one endpoint, POST /comments/action, with action set to hide, unhide, like, unlike, pin or unpin. Each verb carries its own inverse, so replaying a request can never flip a comment back.

# The replies under one comment
curl 'https://api.upload-post.com/api/uploadposts/comments?platform=tiktok&user=test&post_id=7401234567890123456&comment_id=7401234567890999888' \
-H 'Authorization: Apikey your-api-key-here'

# Pin it to the top of the video
curl -X POST https://api.upload-post.com/api/uploadposts/comments/action \
-H 'Authorization: Apikey your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{"platform":"tiktok","user":"test","comment_id":"7401234567890999888","action":"pin","post_id":"7401234567890123456"}'

A comment you just wrote takes about 10 seconds to show up in a listing — TikTok indexes it asynchronously, so an empty list right after creating one is not a failure.

All of this needs the comments capability, i.e. a reconnected account.

Before publishing: what to post about

One endpoint, GET /suggestions, turns "write something about instant cameras" into a caption aimed at demand that exists:

  • type=keywords — the terms people actually type on TikTok around your seed word, with their search volume. Needs trend_search, i.e. a reconnected account.
  • type=hashtags — the tags to pair with that keyword and how many views each one carries, optionally ranked per country (country_code) and language. Needs profile_analytics, which any recent connection has.

After publishing: how it did

Get Analytics gives you the numbers, and on TikTok it gives you a lot more than four counters. Everything below needs profile_analytics, which any recent connection has:

  • GET /post-analytics — per post, on top of views, likes, comments and shares: reach, favorites, new_followers, profile_views, full_video_watched_rate, average_time_watched, total_time_watched, the retention curve second by second, impression_sources (For You, Search, Follow, Personal Profile, Sound, Direct Message, Others) and audience_types. What TikTok did not report is omitted, not zeroed.
  • GET /audience — who follows the account (countries, cities, ages, genders), daily followers gained and lost, profile actions, and activity_by_hour: how many of your followers are online in each hour of the day. That last one is the honest answer to "when should I publish", and it comes from your own audience rather than a generic best-time table. Window: up to 60 days, ending yesterday at the latest, trimmed automatically — read the range that comes back.
  • The same call with benchmark_category adds the averages of your content category, so a 6% engagement rate stops being a number and becomes "above average for what I publish".

Limits and gotchas

  • Daily cap: 15 TikTok posts per connected account per rolling 24 h (upload limits).
  • Free plan: TikTok uploads return 403; you need a paid plan (pricing & limits).
  • Rate limits: TikTok allows 6 posts per minute and 15 posts per day per connected TikTok account.
  • reached_active_user_cap error: a temporary TikTok platform limit. Reconnect the TikTok account to move it to our current publishing route, which is not affected, or apply the inbox workaround.
  • Video privacy: privacy_level works on video and photo posts alike, but TikTok decides per account which levels are available — a private account has no PUBLIC_TO_EVERYONE. Asking for one the account does not have fails with tiktok_privacy_unavailable and an error listing the ones it does have; omit the field on video and TikTok keeps the account's own default.
  • Failure codes to branch on: TikTok accepts the job and reports the outcome afterwards, so a failure arrives on Upload Status, not on the upload call. tiktok_media_rejected means TikTok refused the file itself (format, duration, frame rate, resolution or size) — fix the media, a retry of the same file will fail again. tiktok_publish_failed is everything else, including TikTok-side problems, and is worth retrying. Both refund the 24 h allowance, because nothing was published.
  • Extra TikTok options: attach a Commercial Music Library track (tiktok_music_id, see Get TikTok Trending Music), tag a place (tiktok_location_id + tiktok_location_name, see Get TikTok Locations) or set a custom cover (tiktok_cover_image_url). Each of them needs its own capability — check capabilities first.
  • tiktok_reconnect_required: a 400 on the comment, search or insights endpoints. The connection publishes fine but was never granted that permission; the account owner reconnects and it appears. See Common Errors.
  • Formats: video MP4/MOV/WebM, up to 4 GB, 3–600 s, at least 360 px on the shortest side, 23–60 fps. Photos JPG/JPEG/WebP, up to 35 images of 20 MB each. See Video Requirements and Photo Requirements.