Post videos and photo carousels to TikTok via our API.
All API requests require authentication using your API Key:
// Add this header to all API requests
X-API-Key: sk_your_api_key_hereSecurity: Never share your API key or commit it to public repositories. Regenerate if compromised.
Post to TikTok in seconds:
// POST Video
fetch('https://api-tiktok.wahdx.co/api/content/post', {
method: 'POST',
headers: {
'X-API-Key': 'sk_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
accountId: 'YOUR_ACCOUNT_ID',
content: 'Check out this video! #viral #fyp',
mediaItems: [
{ url: 'https://your-domain.com/video.mp4' }
],
tiktokSettings: {
privacy_level: 'PUBLIC_TO_EVERYONE',
allow_comment: true,
allow_duet: true,
allow_stitch: true,
video_cover_timestamp_ms: 1000
}
})
})/api/content/postCreate post/api/content/status/:accountId/:publishIdCheck status/api/content/creator-info/:accountIdGet permissions/api/content/accountsList linked accountsTikTok supports two content types:
Single video post (3s - 10min)
2-35 photos in slideshow
Important: You cannot mix photos and videos in the same post.
| Specification | Requirement |
|---|---|
| Format | MP4, MOV, WebM |
| Codec | H.264 (recommended) |
| Aspect Ratio | 9:16 (vertical) |
| Resolution | 1080x1920 (Full HD) |
| Duration | 3 seconds - 10 minutes |
| File Size | Up to 500MB |
| Frame Rate | 24-60 FPS |
Customize which frame appears as the thumbnail:
{
"tiktokSettings": {
"video_cover_timestamp_ms": 3000
}
}Create photo slideshows with up to 35 images.
| Specification | Requirement |
|---|---|
| Format | JPEG, PNG, WebP |
| Count | 2-35 photos |
| Aspect Ratio | 9:16 recommended |
| Resolution | 1080x1920 recommended |
| File Size | Up to 20MB per photo |
| Source | Must be from verified domain |
Set which photo appears as the cover:
{
"tiktokSettings": {
"photo_cover_index": 0 // 0 = first photo
}
}| Content Type | Title Field | Description Field |
|---|---|---|
| Video | 2,200 characters (Caption) | - |
| Photo Carousel | 90 characters | 4,000 characters |
Tip: Use the photoTitle field for the main headline and content field for the longer description when posting photo carousels.
Let TikTok automatically add recommended music to photo carousels:
{
"tiktokSettings": {
"auto_add_music": true // Default: true for photos
}
}Note: This feature is only available for photo carousels.
Disclose AI-generated content to comply with TikTok policies:
{
"tiktokSettings": {
"video_made_with_ai": true
}
}When to use: Set to true if your content was created or significantly modified using AI tools.
Send content to Creator's TikTok Inbox as a draft instead of publishing immediately. Works for both video and photo posts.
Add draft: true to your tiktokSettings:
// Video Draft
{
"accountId": "YOUR_ACCOUNT_ID",
"content": "My video caption",
"mediaItems": [
{ "url": "https://your-domain.com/video.mp4" }
],
"tiktokSettings": {
"draft": true
}
}
// Photo Draft
{
"accountId": "YOUR_ACCOUNT_ID",
"content": "Photo description",
"mediaItems": [
{ "url": "https://your-domain.com/photo1.jpg" },
{ "url": "https://your-domain.com/photo2.jpg" }
],
"tiktokSettings": {
"media_type": "photo",
"draft": true,
"privacy_level": "PUBLIC_TO_EVERYONE"
}
}| Type | Behavior | Settings |
|---|---|---|
| Video Draft | Sent to TikTok inbox | Title, privacy, etc. configured in TikTok app |
| Photo Draft | Sent to TikTok inbox | All settings sent via API |
Video Draft Note: When sending a video to inbox, only the video file is uploaded. Title, privacy level, and other settings must be configured directly in the TikTok app before publishing.
Use case: Ideal for workflows requiring manual review before publishing, or for unaudited apps that cannot use Direct Post.
PUBLIC_TO_EVERYONEVisible to everyone
MUTUAL_FOLLOW_FRIENDSVisible to mutual followers
FOLLOWER_OF_CREATORVisible to followers only
SELF_ONLYPrivate (only you)
These fields are required for all posts:
privacy_level- Functionality visibilityallow_comment- Enable/disable commentsallow_duet- Enable/disable duets (video only)allow_stitch- Enable/disable stitch (video only)The privacy level must maintain or restrict the user's account settings. Check creator-info endpoint for allowed values.
A post must be either single video OR photo carousel. Mixing is not supported.
Photo carousel titles are limited to 90 characters. Descriptions can be up to 4,000 characters.
⚡ Maximum Subscription Only
This feature is exclusively available for users with Maximum subscription plan. Other plans will receive a 403 error.
Connect TikTok accounts via QR code scan — no browser redirect needed.
The user scans the QR code with their TikTok app and confirms the authorization. This method is ideal if you want to integrate our platform into your own custom application, allowing your users to link their TikTok accounts seamlessly from within your app.
Request QR
POST /qr-login/request
Display QR
Show base64 image to user
Poll Status
POST /qr-login/check every 10s
Account Linked
Status: confirmed
POST /api/content/qr-login/request{
"unique_id": "my_session_12345" // Optional, min 8 chars. Auto-generated if not provided.
}// Step 1: Request QR Code
fetch('https://api-tiktok.wahdx.co/api/content/qr-login/request', {
method: 'POST',
headers: {
'X-API-Key': 'sk_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
unique_id: 'my_session_12345' // Optional, min 8 chars
})
}){
"success": true,
"data": {
"qrCodeImage": "data:image/png;base64,iVBORw0KGgo...",
"token": "VJ5JCKGJGRSWNMFWHQH4W5NKY943Q97D",
"unique_id": "my_session_12345",
"expiresIn": 300
}
}{
"error": "QR Code login is only available for Maximum subscription",
"subscription": "free"
}POST /api/content/qr-login/check{
"token": "VJ5JCKGJGRSWNMFWHQH4W5NKY943Q97D" // Required: token from Step 1
}// Step 2: Poll QR Status (every 3 seconds)
fetch('https://api-tiktok.wahdx.co/api/content/qr-login/check', {
method: 'POST',
headers: {
'X-API-Key': 'sk_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
token: 'TOKEN_FROM_STEP_1'
})
})new — QR code not yet scanned
{ "success": true, "data": { "status": "new", "unique_id": "my_session_12345" } }scanned — User has scanned, awaiting confirmation
{ "success": true, "data": { "status": "scanned", "unique_id": "my_session_12345" } }confirmed — Account successfully linked
{
"success": true,
"data": {
"status": "confirmed",
"unique_id": "my_session_12345",
"account": {
"accountId": "8abd72f0-4ede-441c-9c17-XXXXXXXXX",
"display_name": "Creator Name",
"username": "creator_username"
}
}
}expired — QR code has expired, request a new one
{ "success": true, "data": { "status": "expired", "unique_id": "my_session_12345" } }utilised — Auth code has already been used
{ "success": true, "data": { "status": "utilised", "unique_id": "...", "message": "QR code has already been used." } }GET /api/content/accountsResponse List Account
{
"success": true,
"subscription": "maximum",
"data": [
{
"accountId": "aada5586-xxxx-xxxx-xxxx-XXXXXXXXXXX",
"display_name": "Account 1",
"username": "account_1",
"avatar_url": "https://p16-sign-va.tiktokcdn.com/tos-...",
"bio_description": "My awesome bio",
"followers": 1500,
"following": 300,
"likes": 12500,
"videos": 42,
"is_verified": false
},
{
"accountId": "8abd72f0-xxxx-xxxx-xxxx-XXXXXXXXXXX",
"display_name": "Account 2",
"username": "account_2"
},
{
"accountId": "23369e89-xxxx-xxxx-xxxx-XXXXXXXXXXX",
"display_name": "Account 3",
"username": "account_3"
}
]
}// Complete QR Login Flow
async function loginWithQR(apiKey) {
// Step 1: Request QR Code
const qr = await fetch('https://api-tiktok.wahdx.co/api/content/qr-login/request', {
method: 'POST',
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify({ unique_id: 'session_' + Date.now() })
}).then(r => r.json());
// Display QR: <img src={qr.data.qrCodeImage} />
console.log('Scan this QR code with TikTok app');
// Step 2: Poll status every 10 seconds
const poll = setInterval(async () => {
const check = await fetch('https://api-tiktok.wahdx.co/api/content/qr-login/check', {
method: 'POST',
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify({ token: qr.data.token })
}).then(r => r.json());
if (check.data.status === 'confirmed') {
clearInterval(poll);
console.log('Account linked!', check.data.account);
} else if (['expired', 'utilised'].includes(check.data.status)) {
clearInterval(poll);
console.log('QR expired, request a new one');
}
}, 10000);
}{
"success": true,
"data": {
"publish_id": "v_pub_url~v2.123456789",
"public_post_url": "https://www.tiktok.com/@user/video/1234567890" // Only available after processing
},
"mediaType": "video"
}