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
| Requirement | TikTok limit |
|---|---|
| Video post media | Exactly 1 video |
| Photo post media | TikTok allows up to 35 images; a PostZen request accepts up to 10 mediaItems |
| Caption | Up to 2,200 characters for video posts |
| Photo post title | Up to 90 characters |
| Video file size | Up to 4 GB |
| PostZen presigned image upload types | jpeg, jpg, png, webp, gif; TikTok rejects GIF photo posts |
| PostZen presigned video upload types | mp4, mpeg, quicktime, avi, webm, m4v |
| GIF photo posts | Not supported |
| Mixed image and video posts | Not supported |
| Recommended layout | 9:16 vertical at 1080×1920 |
| Immediate publishing | Supported with publishNow |
| Scheduling | Supported with scheduledFor |
| Draft upload | Supported 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_KEYin 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
privacyLevelunless you setuploadAsDraft: 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 minutesfrom 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 minutescurl "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
| Requirement | Value |
|---|---|
| Videos per post | Exactly 1 |
| Maximum file size | 4 GB |
| PostZen presigned upload types | mp4, mpeg, quicktime, avi, webm, m4v |
| Caption length | Up to 2,200 characters |
| Images in the same post | Not allowed |
| Recommended orientation | 9:16 vertical |
| Recommended resolution | 1080×1920 |
Photos
| Requirement | Value |
|---|---|
| Images per post | TikTok allows up to 35; a PostZen request accepts up to 10 mediaItems |
| PostZen presigned upload types | jpeg, jpg, png, webp, gif |
| GIFs | Rejected |
| Title length | Up to 90 characters |
| Videos in the same post | Not allowed |
| Recommended orientation | 9:16 vertical |
| Recommended resolution | 1080×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,
},
},
]| Setting | Type | Notes |
|---|---|---|
privacyLevel | "publicToEveryone" | "mutualFollowFriends" | "followerOfCreator" | "selfOnly" | Required unless uploadAsDraft: true. Available levels depend on the creator's account settings. |
allowComments | boolean | Enables or disables comments. |
allowDuet | boolean | Enables or disables duets for video posts. |
allowStitch | boolean | Enables or disables stitches for video posts. |
disableComment | boolean | Inverse comment toggle. |
disableDuet | boolean | Inverse duet toggle. |
disableStitch | boolean | Inverse stitch toggle. |
videoCoverTimestampMs | number | Selects the video frame used as the cover. |
uploadAsDraft | boolean | Sends the post to the creator's TikTok inbox as a draft instead of publishing it. |
brandContentToggle | boolean | Discloses a paid partnership. |
brandOrganicToggle | boolean | Discloses 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
| Error | Meaning | Fix |
|---|---|---|
Missing privacyLevel | TikTok requires a privacy level unless uploadAsDraft is true. | Add an available privacyLevel, or set uploadAsDraft: true. |
| Privacy level unavailable | The creator's account does not allow the requested level. | Choose a privacy level available to that creator. |
| Branded content set to private | TikTok rejects brandContentToggle: true with privacyLevel: "selfOnly". | Use a public or friends-visible privacy level that the creator allows. |
| GIF attached to a photo post | TikTok rejects GIFs in photo posts. | Remove the GIF from mediaItems. |
| Video over 4 GB | The video exceeds TikTok's maximum file size. | Reduce the video to 4 GB or less. |
| Photo title over 90 characters | The title exceeds TikTok's photo post limit. | Shorten the title to 90 characters or fewer. |
402 while connecting | Connecting 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 expired | The connect flow was not completed within 10 minutes. | Request a new connect URL and complete OAuth within 10 minutes. |