PostZen

TikTok

Connect TikTok to PostZen and publish or schedule video and photo posts.

PostZen publishes and schedules TikTok video and photo posts through the same /v1/posts endpoint used for other platforms. Each TikTok post needs media, and a single post cannot mix images and video.

Quick reference

RequirementTikTok limit
Video post mediaExactly 1 video
Photo post mediaTikTok allows up to 35 images; a PostZen request accepts up to 10 mediaItems
CaptionUp to 2,200 characters for video posts
Photo post titleUp to 90 characters
Video file sizeUp to 4 GB
PostZen presigned image upload typesjpeg, jpg, png, webp, gif; TikTok rejects GIF photo posts
PostZen presigned video upload typesmp4, mpeg, quicktime, avi, webm, m4v
GIF photo postsNot supported
Mixed image and video postsNot supported
Recommended layout9:16 vertical at 1080×1920
Immediate publishingSupported with publishNow
SchedulingSupported with scheduledFor
Draft uploadSupported with settings.uploadAsDraft

Before you start

You need a PostZen profile and a connected TikTok account. TikTok uses OAuth 2.0, and PostZen manages the required profile, video upload, and video publish scopes internally.

  • Set POSTZEN_API_KEY in your environment before using the examples below.
  • Keep the profile ID for the workspace that will own the connection and the account ID returned for the connected TikTok account.
  • Include media with each TikTok post. Text-only posts fail validation.
  • Use either one video or images. TikTok posts cannot combine the two media types.
  • Set privacyLevel unless you set uploadAsDraft: true. The available privacy levels depend on the creator's TikTok account settings.
  • On the free tier, connecting more than 2 social accounts requires a payment method and otherwise returns a 402.

Cloud-storage share links from Google Drive, Dropbox, OneDrive, and iCloud return HTML pages instead of media files. Use a publicly accessible direct media URL, or upload the file through /v1/media/presign. External URLs can be up to 100 MB; presigned uploads can be up to 5 GB, while TikTok videos remain limited to 4 GB.

Connect your account

Request a TikTok connect URL for your PostZen profile, then send the user to the returned authUrl. The returned OAuth state expires after 10 minutes.

import PostZen from '@postzen/node';

const postzen = new PostZen({
  apiKey: process.env.POSTZEN_API_KEY,
});

const { data } = await postzen.connect.createConnectUrl({
  path: { platform: 'tiktok' },
  query: {
    profileId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
    redirectUrl: 'https://yourapp.com/connected',
  },
});

console.log(data.authUrl); // redirect the user here
console.log(data.state);   // expires after 10 minutes
from postzen import PostZen

client = PostZen()  # reads POSTZEN_API_KEY

response = client.connect.create_connect_url(
    "tiktok",
    profile_id="jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
    redirect_url="https://yourapp.com/connected",
)

print(response.authUrl)  # redirect the user here
print(response.state)    # expires after 10 minutes
curl "https://api.postzen.dev/v1/connect/tiktok?profileId=jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e&redirectUrl=https://yourapp.com/connected" \
  -H "Authorization: Bearer $POSTZEN_API_KEY"

The endpoint returns { authUrl, state }. Complete the OAuth flow at authUrl before the state expires.

Quick start

Publish a video now by passing one public video URL and a privacy level. Replace the account ID with the ID of your connected TikTok account.

const { data } = await postzen.posts.createPost({
  body: {
    content: 'A quick look at the finished project.',
    publishNow: true,
    mediaItems: [
      { url: 'https://cdn.example.com/project-tour.mp4' },
    ],
    platforms: [
      {
        platform: 'tiktok',
        accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
        settings: { privacyLevel: 'publicToEveryone' },
      },
    ],
  },
});

console.log(data.post._id);
response = client.posts.create_post(
    content="A quick look at the finished project.",
    publish_now=True,
    media_items=[
        {"url": "https://cdn.example.com/project-tour.mp4"},
    ],
    platforms=[
        {
            "platform": "tiktok",
            "account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
            "settings": {"privacy_level": "publicToEveryone"},
        },
    ],
)

print(response.post.field_id)
curl -X POST https://api.postzen.dev/v1/posts \
  -H "Authorization: Bearer $POSTZEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "A quick look at the finished project.",
    "publishNow": true,
    "mediaItems": [
      { "url": "https://cdn.example.com/project-tour.mp4" }
    ],
    "platforms": [
      {
        "platform": "tiktok",
        "accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
        "settings": { "privacyLevel": "publicToEveryone" }
      }
    ]
  }'

Use scheduledFor with an ISO-8601 timestamp at least 60 seconds in the future instead of publishNow to schedule the post.

Content types

Video posts

A video post requires exactly one video. Its caption can contain up to 2,200 characters, and the video file can be no larger than 4 GB. The TikTok settings let you choose the privacy level and control comments, duets, and stitches.

const { data } = await postzen.posts.createPost({
  body: {
    content: 'Behind the scenes from today\'s shoot.',
    scheduledFor: '2026-07-16T18:00:00Z',
    mediaItems: [
      { url: 'https://cdn.example.com/behind-the-scenes.mp4' },
    ],
    platforms: [
      {
        platform: 'tiktok',
        accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
        settings: {
          privacyLevel: 'publicToEveryone',
          allowComments: true,
          allowDuet: false,
          allowStitch: false,
        },
      },
    ],
  },
});

console.log(data.post._id);
response = client.posts.create_post(
    content="Behind the scenes from today's shoot.",
    scheduled_for="2026-07-16T18:00:00Z",
    media_items=[
        {"url": "https://cdn.example.com/behind-the-scenes.mp4"},
    ],
    platforms=[
        {
            "platform": "tiktok",
            "account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
            "settings": {
                "privacy_level": "publicToEveryone",
                "allow_comments": True,
                "allow_duet": False,
                "allow_stitch": False,
            },
        },
    ],
)

print(response.post.field_id)
curl -X POST https://api.postzen.dev/v1/posts \
  -H "Authorization: Bearer $POSTZEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Behind the scenes from today'"'"'s shoot.",
    "scheduledFor": "2026-07-16T18:00:00Z",
    "mediaItems": [
      { "url": "https://cdn.example.com/behind-the-scenes.mp4" }
    ],
    "platforms": [
      {
        "platform": "tiktok",
        "accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
        "settings": {
          "privacyLevel": "publicToEveryone",
          "allowComments": true,
          "allowDuet": false,
          "allowStitch": false
        }
      }
    ]
  }'

Photo posts

TikTok allows up to 35 images in a photo post, while a PostZen create-post request accepts up to 10 mediaItems. A photo post can have a title of up to 90 characters. TikTok rejects GIFs, and the post cannot include a video.

const { data } = await postzen.posts.createPost({
  body: {
    title: 'A look inside the studio',
    content: 'A few details from this week\'s work.',
    publishNow: true,
    mediaItems: [
      { url: 'https://cdn.example.com/studio-1.jpg' },
      { url: 'https://cdn.example.com/studio-2.jpg' },
    ],
    platforms: [
      {
        platform: 'tiktok',
        accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
        settings: {
          privacyLevel: 'mutualFollowFriends',
          allowComments: true,
        },
      },
    ],
  },
});

console.log(data.post._id);
response = client.posts.create_post(
    title="A look inside the studio",
    content="A few details from this week's work.",
    publish_now=True,
    media_items=[
        {"url": "https://cdn.example.com/studio-1.jpg"},
        {"url": "https://cdn.example.com/studio-2.jpg"},
    ],
    platforms=[
        {
            "platform": "tiktok",
            "account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
            "settings": {
                "privacy_level": "mutualFollowFriends",
                "allow_comments": True,
            },
        },
    ],
)

print(response.post.field_id)
curl -X POST https://api.postzen.dev/v1/posts \
  -H "Authorization: Bearer $POSTZEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "A look inside the studio",
    "content": "A few details from this week'"'"'s work.",
    "publishNow": true,
    "mediaItems": [
      { "url": "https://cdn.example.com/studio-1.jpg" },
      { "url": "https://cdn.example.com/studio-2.jpg" }
    ],
    "platforms": [
      {
        "platform": "tiktok",
        "accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
        "settings": {
          "privacyLevel": "mutualFollowFriends",
          "allowComments": true
        }
      }
    ]
  }'

Media requirements

Video

RequirementValue
Videos per postExactly 1
Maximum file size4 GB
PostZen presigned upload typesmp4, mpeg, quicktime, avi, webm, m4v
Caption lengthUp to 2,200 characters
Images in the same postNot allowed
Recommended orientation9:16 vertical
Recommended resolution1080×1920

Photos

RequirementValue
Images per postTikTok allows up to 35; a PostZen request accepts up to 10 mediaItems
PostZen presigned upload typesjpeg, jpg, png, webp, gif
GIFsRejected
Title lengthUp to 90 characters
Videos in the same postNot allowed
Recommended orientation9:16 vertical
Recommended resolution1080×1920

PostZen accepts up to 10 mediaItems in a create-post request. It downloads and re-hosts external media URLs up to 100 MB. Presigned uploads can be up to 5 GB, subject to TikTok's 4 GB video limit.

Platform settings

Put TikTok settings in the settings object for the TikTok target inside platforms:

platforms: [
  {
    platform: 'tiktok',
    accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
    settings: {
      privacyLevel: 'publicToEveryone',
      allowComments: true,
      allowDuet: false,
      allowStitch: false,
    },
  },
]
SettingTypeNotes
privacyLevel"publicToEveryone" | "mutualFollowFriends" | "followerOfCreator" | "selfOnly"Required unless uploadAsDraft: true. Available levels depend on the creator's account settings.
allowCommentsbooleanEnables or disables comments.
allowDuetbooleanEnables or disables duets for video posts.
allowStitchbooleanEnables or disables stitches for video posts.
disableCommentbooleanInverse comment toggle.
disableDuetbooleanInverse duet toggle.
disableStitchbooleanInverse stitch toggle.
videoCoverTimestampMsnumberSelects the video frame used as the cover.
uploadAsDraftbooleanSends the post to the creator's TikTok inbox as a draft instead of publishing it.
brandContentTogglebooleanDiscloses a paid partnership.
brandOrganicTogglebooleanDiscloses promotion of the creator's own brand.

Branded content must be public or visible to friends. A request with brandContentToggle: true and privacyLevel: "selfOnly" fails validation.

PostZen analytics is live for TikTok, including video metrics, follower counts, and best-time-to-post suggestions. TikTok reports metrics for public videos only. Accounts connected before analytics launched must be reconnected to grant the new user stats and video list read permissions before metrics appear.

What you can't do

PostZen does not yet support these TikTok capabilities:

  • Publish a text-only post.
  • Add music from TikTok's music library.
  • Create a duet or stitch. The settings above control whether other users may duet or stitch a video.
  • Use TikTok effects or filters.
  • Go live.
  • Edit a published post.
  • Read or manage DMs and inbox messages.
  • Read or manage comments.

Common errors

ErrorMeaningFix
Missing privacyLevelTikTok requires a privacy level unless uploadAsDraft is true.Add an available privacyLevel, or set uploadAsDraft: true.
Privacy level unavailableThe creator's account does not allow the requested level.Choose a privacy level available to that creator.
Branded content set to privateTikTok rejects brandContentToggle: true with privacyLevel: "selfOnly".Use a public or friends-visible privacy level that the creator allows.
GIF attached to a photo postTikTok rejects GIFs in photo posts.Remove the GIF from mediaItems.
Video over 4 GBThe video exceeds TikTok's maximum file size.Reduce the video to 4 GB or less.
Photo title over 90 charactersThe title exceeds TikTok's photo post limit.Shorten the title to 90 characters or fewer.
402 while connectingConnecting more than 2 social accounts on the free tier requires a payment method.Add a payment method, then start the connect flow again.
OAuth state expiredThe connect flow was not completed within 10 minutes.Request a new connect URL and complete OAuth within 10 minutes.

On this page