Platforms
TikTok
Connect a TikTok account, publish videos and photo posts with privacy and disclosure settings, and read post metrics and account stats.
Publish videos and photo posts, and read post metrics and account stats.
Quick Reference
| Property | Value |
|---|---|
| Account types | TikTok accounts |
| Connection method | OAuth |
| Post types | video, image, multi_image |
| Max media per post | 35 |
| Text limit | 2,200 characters, title 90 (photo posts), description 4,000 (photo posts) |
| Threads | No |
| Quotes | No |
| Remote delete | No |
| Comments | No |
| Likes | No |
| Reposts | No |
| Bookmarks | No |
| Direct messages | No |
| Post analytics | |
| Account analytics | Stats |
A post needs uploaded media, a privacyLevel and each interaction choice.
TikTok takes only files uploaded with Create Media. Any other media URL fails with INVALID_MEDIA, so upload the file first.
Connect
Connect in Dashboard → Connections, or with a connect session.
Permissions
| Scope | Used for |
|---|---|
user.info.basic | User id, name and avatar |
user.info.profile | Username, for post links |
user.info.stats | Account stats |
video.list | Analytics, video list and sync |
video.publish | Publishing |
A missing scope returns PERMISSION_DENIED or ANALYTICS_PERMISSION. Reconnect the account and approve every permission.
Staying Connected
Access renews itself, so canAutoRenew is true. It ends if the creator removes the app in TikTok, or the account is deleted or banned. The account then turns revoked and account.revoked fires with authorization_removed. Requests for it return TOKEN_INVALID.
Reconnect with the same TikTok account. The id stays the same.
Publish
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Behind the scenes of launch day #startup",
"mediaKind": "video",
"media": [
{
"id": "med_7f3a9c2e1b4d4e8f9a6b5c4d3e2f1a0b"
}
],
"accounts": [
{
"accountId": "acc_YOUR_TIKTOK_ACCOUNT_ID",
"tiktok": {
"privacyLevel": "public_to_everyone",
"allowComment": true,
"allowDuet": true,
"allowStitch": true
}
}
]
},
});
console.log(response);{
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"data": [
{
"id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"accountId": "acc_YOUR_TIKTOK_ACCOUNT_ID",
"platform": "tiktok",
"source": "onepostly",
"status": "publishing",
"text": "Behind the scenes of launch day #startup",
"mediaKind": "video",
"media": [
{
"url": "https://cdn.onepostly.com/media/5b0e7c1a-2f4d-4c8e-9a3b-6d1f0e2c4a7b/7f3a9c2e-1b4d-4e8f-9a6b-5c4d3e2f1a0b.mp4"
}
],
"thread": [],
"scheduledFor": null,
"timezone": null,
"options": {
"tiktok": {
"privacyLevel": "public_to_everyone",
"allowComment": true,
"allowDuet": true,
"allowStitch": true
}
},
"platformPostId": null,
"url": null,
"error": null,
"publishedAt": null,
"metrics": null,
"createdAt": "2027-03-15T09:00:00.000Z",
"updatedAt": "2027-03-15T09:00:00.000Z"
}
]
}The post stays publishing while TikTok processes it.
Post Types
mediaKind | Publishes | Media |
|---|---|---|
video | Video post | 1 video |
image | Photo post | 1 photo |
multi_image | Photo carousel | 2 to 35 photos |
A post has one video or photos, never both. text and stories return 400.
Text and Captions
| Post | Caption | Title |
|---|---|---|
| Video | text, up to 2,200 | None |
| Photo | tiktok.description, up to 4,000, else text | First 90 of text |
The title is cut at 90, so long text is not refused.
Photo Posts
photoCoverIndex picks the cover photo, counting from 0. autoAddMusic adds TikTok's suggested music to the post.
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Weekend on the coast",
"mediaKind": "multi_image",
"media": [
{
"id": "med_7f3a9c2e1b4d4e8f9a6b5c4d3e2f1a03"
},
{
"id": "med_7f3a9c2e1b4d4e8f9a6b5c4d3e2f1a04"
},
{
"id": "med_7f3a9c2e1b4d4e8f9a6b5c4d3e2f1a05"
}
],
"accounts": [
{
"accountId": "acc_YOUR_TIKTOK_ACCOUNT_ID",
"tiktok": {
"description": "Two days, three beaches, one very windy picnic. #travel #roadtrip",
"privacyLevel": "public_to_everyone",
"allowComment": false,
"photoCoverIndex": 1,
"autoAddMusic": true
}
}
]
},
});
console.log(response);{
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"data": [
{
"id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"accountId": "acc_YOUR_TIKTOK_ACCOUNT_ID",
"platform": "tiktok",
"source": "onepostly",
"status": "publishing",
"text": "Weekend on the coast",
"mediaKind": "multi_image",
"media": [
{
"url": "https://cdn.onepostly.com/media/5b0e7c1a-2f4d-4c8e-9a3b-6d1f0e2c4a7b/7f3a9c2e-1b4d-4e8f-9a6b-5c4d3e2f1a03.jpg"
},
{
"url": "https://cdn.onepostly.com/media/5b0e7c1a-2f4d-4c8e-9a3b-6d1f0e2c4a7b/7f3a9c2e-1b4d-4e8f-9a6b-5c4d3e2f1a04.jpg"
},
{
"url": "https://cdn.onepostly.com/media/5b0e7c1a-2f4d-4c8e-9a3b-6d1f0e2c4a7b/7f3a9c2e-1b4d-4e8f-9a6b-5c4d3e2f1a05.jpg"
}
],
"thread": [],
"scheduledFor": null,
"timezone": null,
"options": {
"tiktok": {
"description": "Two days, three beaches, one very windy picnic. #travel #roadtrip",
"privacyLevel": "public_to_everyone",
"allowComment": false,
"photoCoverIndex": 1,
"autoAddMusic": true
}
},
"platformPostId": null,
"url": null,
"error": null,
"publishedAt": null,
"metrics": null,
"createdAt": "2027-03-15T09:00:00.000Z",
"updatedAt": "2027-03-15T09:00:00.000Z"
}
]
}Privacy Levels
Every TikTok account needs tiktok.privacyLevel.
| Value | Who can see it |
|---|---|
public_to_everyone | Everyone |
mutual_follow_friends | Followers the creator follows back |
follower_of_creator | Followers |
self_only | Only the creator |
The creator's settings decide which values work. Others return 400 INVALID_PRIVACY_LEVEL. The level is checked again at publish, so a privacy change in TikTok can fail a scheduled post that passed on create.
Only public_to_everyone posts get a post id, a link and metrics.
Creator Info
TikTok asks you to read and show the creator's settings before each post.
import { Configuration, AccountsApi } from "@onepostly/sdk";
const accountsApi = new AccountsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await accountsApi.getCreatorInfo({
id: "acc_YOUR_TIKTOK_ACCOUNT_ID",
});
console.log(response);{
"accountId": "acc_YOUR_TIKTOK_ACCOUNT_ID",
"username": "yourbrand",
"nickname": "Your Brand",
"avatarUrl": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/7310942817465028614~c5_168x168.jpeg",
"privacyLevelOptions": [
"public_to_everyone",
"mutual_follow_friends",
"follower_of_creator",
"self_only"
],
"commentDisabled": false,
"duetDisabled": false,
"stitchDisabled": false,
"maxVideoPostDurationSec": 600,
"fetchedAt": "2027-03-15T08:59:30.000Z"
}| Field | Meaning |
|---|---|
privacyLevelOptions | Allowed privacyLevel values |
commentDisabled | Comments are off in TikTok |
duetDisabled | Duets are off, such as on a private account |
stitchDisabled | Stitches are off, such as on a private account |
maxVideoPostDurationSec | The longest video allowed, or null |
These settings are read live. Creating or editing a post reads them too, and the request fails if they cannot be loaded.
Interactions
TikTok wants the creator to pick every interaction, so each post sets them.
| Option | Applies to | Required |
|---|---|---|
tiktok.allowComment | Videos and photos | Yes |
tiktok.allowDuet | Videos only | On videos |
tiktok.allowStitch | Videos only | On videos |
- A missing one returns
400 VALIDATION_ERROR. On photos, duet or stitch returns400. truefor an interaction the creator turned off returns400 INTERACTION_DISABLED.- If the creator turns one off after scheduling, the post publishes with it off.
Post ID and Link
| Field | Once public | Before that, or not public |
|---|---|---|
platformPostId | The TikTok post id | A publish id, v_pub_… or p_pub_… |
url | The post link | The profile for videos, null for photos |
A public post can be published before TikTok's review ends. Once public, both fields update and post.updated fires, so store the new values.
Options
Options go under tiktok. Another platform's options there return 400.
Prop
Type
Disclosure Labels
You can set both brand labels. A paid partnership cannot be self_only. That combination passes on create but fails at publish.
Media Requirements
These are TikTok's limits, on top of the Media URL Requirements.
| Media | Formats | Max size |
|---|---|---|
| Images | JPEG and WebP | 20 MB |
| Videos | MP4, MOV and WebM | 1 GB |
A file outside these formats and sizes is refused. See Platform Limits.
Images
| Property | Requirement |
|---|---|
| Max resolution | 1080p |
Videos
| Property | Requirement |
|---|---|
| Duration | Up to maxVideoPostDurationSec |
| Frame rate | 23 to 60 fps |
| Dimensions | 360 to 4096 each side |
Scheduling, Editing and Deleting
Scheduling and editing work as usual. Both check the creator's settings. A post that is publishing or published returns NOT_EDITABLE.
You can cancel a post before publish. During processing, a delete returns POST_PUBLISHING. Published posts can only be deleted in the TikTok app.
Engagement
TikTok has no comments, likes, reposts or bookmarks here. Those calls return UNSUPPORTED_PLATFORM. Counts are in analytics.
Inbox
TikTok has no inbox through Onepostly.
Analytics
Metrics
| Field | TikTok metric |
|---|---|
impressions | view_count |
reach | Always null |
views | view_count |
likes | like_count |
comments | comment_count |
shares | share_count |
saves | Always null |
engagement | likes + comments + shares |
Only public_to_everyone posts have metrics. Freshness is the standard.
| Status | Code | Meaning |
|---|---|---|
pending | VIDEO_ID_PENDING | Not public yet, read again later |
pending | VIDEO_NOT_FOUND | Public, but no metrics yet |
unavailable | PRIVACY_RESTRICTED | Not public, for good |
On YouTube, VIDEO_NOT_FOUND means gone for good. Read it with status.
A post that stops being public keeps its last metrics. They update again once it is public again.
Account Stats
Get Account Stats reads followers, following, likes and videos live. likesCount counts likes across all of the creator's videos.
Recent Posts
List Account Media lists public videos with metrics, 20 per page. The first page adds your non-public posts, with a publish id and null metrics.
Limitations
Not available for TikTok through Onepostly.
- Text posts and stories
- Mixing photos and a video
- Editing or deleting published posts
- Comments, likes, reposts and bookmarks
- Quotes and reply chains
- Direct messages
- Sending to drafts
- Metrics for non-public posts
Errors
Breaking a rule on this page returns 400 VALIDATION_ERROR and creates nothing. TikTok can also refuse a setting at publish and fail the post.
Every code is in Errors.
Publish Errors
| Code | Cause | Fix |
|---|---|---|
INVALID_PRIVACY_LEVEL | Not in privacyLevelOptions | Use an allowed value |
INTERACTION_DISABLED | true for an interaction the creator turned off | Send false |
INVALID_MEDIA | A file not uploaded with Create Media | Upload it first |
INVALID_MEDIA | TikTok refused the format, length, frame rate or photo size | Meet the Media Requirements |
MEDIA_UNFETCHABLE | TikTok could not download the file | Upload it again |
URL_OWNERSHIP_UNVERIFIED | TikTok refused the file | Upload it again |
TOKEN_INVALID | The creator removed access while the post was processing | Reconnect the account |
CREATOR_INFO_FAILED | 502. Creator info did not load | Try again, then reconnect |
MEDIA_UPLOAD_FAILED | TikTok refused to start the post | Read error.message |
MEDIA_PROCESSING_TIMEOUT | Processing took too long | Check the profile first |
SPAM_RISK | TikTok flagged the post | Change the caption and media |
POSTING_LIMIT_REACHED | Too many recent posts | Wait before posting |
ACCOUNT_RESTRICTED | TikTok restricted posting | Fix it in the app |
CAPACITY_LIMITED | TikTok is not taking direct posts | Try again in a few hours |
UNSUPPORTED_PLATFORM | A delete of a published post | Delete it in the app |
Analytics Errors
| Code | Cause | Fix |
|---|---|---|
VIDEO_ID_PENDING | Not public yet | Read again later |
VIDEO_NOT_FOUND | No metrics yet | Read again later |
PRIVACY_RESTRICTED | Not public | Publish to everyone |
ANALYTICS_PERMISSION | No video.list | Reconnect with every permission |
STATS_FAILED | Stats did not load | Try again later |