Posts
Video posts, covers, photo carousels, creator info, commercial content, posting caps and Creator Inbox drafts.
A plain post becomes a video post when the media is one video, and a photo carousel when it is images.
Send tiktokSettings at the top level of the request body, where it applies to every TikTok entry in platforms. TikTok requires it on every post.
Both snake_case and camelCase field names are accepted.
Video post
A single video between 3 seconds and 10 minutes. Vertical 9:16 is the only aspect ratio that works well on TikTok.
import Zernio from '@zernio/node';
const zernio = new Zernio();
const { data: published } = await zernio.posts.createPost({
body: {
content: 'New cooking tutorial #recipe #foodtok',
mediaItems: [
{ type: 'video', url: 'https://cdn.example.com/cooking-tutorial.mp4' }
],
platforms: [
{ platform: 'tiktok', accountId: '66b2e19d8c3f5a7e9d0b1c2d' }
],
tiktokSettings: {
privacy_level: 'PUBLIC_TO_EVERYONE',
allow_comment: true,
allow_duet: true,
allow_stitch: true,
content_preview_confirmed: true,
express_consent_given: true
},
publishNow: true
}
});
console.log(published.post.platforms[0].status);Response (201):
{
"post": {
"_id": "65f1c0a9e2b5af0012ab34cd",
"status": "published",
"platforms": [
{
"platform": "tiktok",
"status": "published",
"platformPostUrl": null
}
]
}
}TikTok exposes the video id some minutes after publishing, so platformPostUrl can be empty at first. The post.tiktok.url_resolved webhook delivers it once it exists.
Every sample below changes only mediaItems or tiktokSettings of this request.
Video cover
video_cover_timestamp_ms picks the frame used as the cover.
video_cover_image_url replaces it with a custom image (JPG, PNG or WebP, at most 20 MB). Any image URL Zernio can download works.
- For most accounts, Zernio downloads the image, rehosts it, stitches it in as a single frame at the start of the video, and TikTok uses that as the cover.
- Accounts connected through the TikTok for Business app skip the stitching: Zernio rehosts the image and hands TikTok that URL as the cover. If
video_cover_image_urlis omitted, those accounts fall back tovideo_cover_timestamp_ms.
When both are set, the image wins:
"tiktokSettings": {
"privacy_level": "PUBLIC_TO_EVERYONE",
"allow_comment": true,
"allow_duet": true,
"allow_stitch": true,
"video_cover_image_url": "https://cdn.example.com/teaser-cover.jpg",
"content_preview_confirmed": true,
"express_consent_given": true
}Photo carousel
Up to 35 images.
content becomes the photo title (90 characters, hashtags and URLs stripped). Put the full caption in description, which takes up to 4,000 characters.
Photos and videos cannot be mixed in one post, and allow_duet and allow_stitch do not apply:
"mediaItems": [
{ "type": "image", "url": "https://cdn.example.com/photo1.jpg" },
{ "type": "image", "url": "https://cdn.example.com/photo2.jpg" },
{ "type": "image", "url": "https://cdn.example.com/photo3.jpg" }
],
"tiktokSettings": {
"privacy_level": "PUBLIC_TO_EVERYONE",
"allow_comment": true,
"media_type": "photo",
"photo_cover_index": 0,
"description": "Full trip recap from our weekend across the coast. #travel #roadtrip #adventure",
"auto_add_music": true,
"content_preview_confirmed": true,
"express_consent_given": true
}Creator info
Call GET /v1/accounts/{accountId}/tiktok/creator-info (Get TikTok creator info) before you build a post. It returns:
- the creator's allowed
privacyLevels postingLimits, with the interaction toggles- the
commercialContentTypesthe account can disclose
A non-TikTok accountId returns 400.
const { data: info } = await zernio.accounts.getTikTokCreatorInfo({
path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' },
query: { mediaType: 'video' }
});
console.log(info.privacyLevels);Response (200):
{
"creator": { "nickname": "myaccount", "isVerified": false, "canPostMore": true },
"privacyLevels": [
{ "value": "PUBLIC_TO_EVERYONE", "label": "Public To Everyone" },
{ "value": "MUTUAL_FOLLOW_FRIENDS", "label": "Mutual Follow Friends" },
{ "value": "SELF_ONLY", "label": "Self Only" }
],
"postingLimits": {
"maxVideoDurationSec": 600,
"interactionSettings": {
"allow_comment": { "enabled": true, "required": true, "default": false, "label": "Allow Comment" },
"allow_duet": { "enabled": true, "required": true, "default": false, "label": "Allow Duet" },
"allow_stitch": { "enabled": false, "required": true, "default": false, "label": "Allow Stitch" }
}
},
"commercialContentTypes": [
{ "value": "none", "label": "No Commercial Content" },
{ "value": "brand_organic", "label": "Your Brand", "requires": ["is_brand_organic_post"] }
]
}On each toggle:
enabled: falsemeans the creator turned that interaction off in the TikTok app.requiredis alwaystrue, because TikTok forbids defaulting these fields.defaultis only a value for your composer to render.
With mediaType=photo, allow_duet and allow_stitch are null.
Commercial content
commercialContentType discloses a commercial post: "brand_organic" (the creator's own brand) or "brand_content" (a paid partnership).
Each value implies its boolean flag. isBrandOrganicPost and brandPartnerPromote are only needed to disclose both at once or to override the implied value.
Branded content cannot be posted with privacy_level: "SELF_ONLY".
Daily posting caps
On an account connected through TikTok's Business app, Zernio allows 15 videos and 15 photo posts per account per rolling 24 hours.
The 2 counts are separate, so 15 videos leave the photo allowance untouched.
A post beyond the cap is not failed. Zernio holds it and publishes it by itself once the rolling window frees a slot.
The window trails the posts that fill it, so there is no clock time at which it resets.
Accounts on TikTok's developer app keep Zernio's generic per-platform daily cap instead (posting velocity limits).
TikTok's own limit. TikTok enforces a limit of its own on how many posts an account creates through the API in 24 hours.
A post TikTok rejects for it fails with "You have created too many posts in the last 24 hours via the API". Wait for TikTok's own window to roll, or post in the TikTok app.
Direct posting at capacity
A TikTok post from an account connected through TikTok's developer app can fail with this message even though nothing is wrong with the account or the media:
TikTok direct posting is at capacity right now. Use tiktokSettings.draft: true to deliver via Creator Inbox, or try again in a few hours as capacity frees up.TikTok caps how many distinct accounts can direct-post through one developer app in a rolling 24-hour window.
TikTok's own error for it is reached_active_user_cap, which Zernio surfaces as Daily active user quota reached.
Every Zernio customer still on that lane publishes through the same TikTok app, so the budget is app-wide. It is not tied to your account, your API key, or how many posts you have made.
An account that has already direct-posted inside the window keeps posting. The cap only blocks accounts that would need a new slot.
Accounts connected through TikTok's Business app do not draw on that budget and never see this error.
Reconnecting an account moves it to the Business app, with the caveat in connection lanes.
The window is rolling, so slots free up continuously as activity from 24 hours ago ages out. There is no midnight reset to wait for.
When the window is close to full, Zernio stops handing new developer app slots to accounts whose owner has never added a payment method.
Teams with a card on file (or a paid plan) are not gated by Zernio at all, only by TikTok's hard cap.
Adding a card moves a team to that group, even if the bill stays $0 inside the free allowance. See Pricing.
The post is marked failed and is not retried automatically. Either:
- Retry it a few hours later, or reschedule it.
- Send it as a draft instead:
tiktokSettings.draft: trueis exempt from the cap (next section).
Check first. To check before you create anything, send the same request with dryRun: true.
It returns 200 with canPublish and one verdict per tiktok entry. It persists no post and claims no slot, so you can repeat it freely.
A Business app account comes back canPublish: true here whatever the developer app budget looks like, because the cap does not apply to it.
Draft delivery
Set tiktokSettings.draft: true and Zernio uploads the media to the creator's TikTok inbox instead of publishing it.
Videos go through TikTok's inbox upload endpoint. Photo posts go through the content endpoint with post_mode: MEDIA_UPLOAD.
Drafts need the video.upload scope (granted in the standard connect flow) and TikTok app version 31.8 or later on the creator's phone.
The creator gets an inbox notification in the TikTok app and finishes the post in TikTok's own editor. Caption, cover, privacy, and the final Post tap all happen there.
Nothing is public until the creator posts it.
Zernio marks the platform entry published as soon as TikTok accepts the upload. That is the moment the draft is handed over, not the moment it goes live.
The entry carries platformSpecificData.isDraft: true and no platformPostUrl. The post.tiktok.url_resolved webhook never fires for drafts, because Zernio never learns whether or when the creator posts it.
Limits that still apply:
- TikTok allows 5 pending drafts per account in any 24-hour period. A 6th fails with
TikTok allows only 5 pending drafts per account in any 24-hour period...and the only fix is to finish or discard drafts in the TikTok app; waiting does not free the slot. - TikTok's own per-account limit on posts created through the API in the last 24 hours (
You have created too many posts in the last 24 hours via the API) and Zernio's own caps (25 posts per hour, plus the daily posting caps) count drafts and direct posts alike. - Drafts are exempt from the direct posting capacity gate.