TikTok Photo Post API: Official Endpoint and Examples
TikTok has an official API for photo posts and carousels. Call POST https://open.tiktokapis.com/v2/post/publish/content/init/, set media_type to PHOTO, and provide one to 35 image URLs. Use DIRECT_POST to publish or MEDIA_UPLOAD to send the content into TikTok’s editing flow.
The endpoint has no scheduling. To schedule photo posts without building a queue or passing TikTok’s audit, use the Postqued API for $9/month: validate with dryRun, publish safely with an Idempotency-Key header, and set dispatchAt for the publish time.
The same endpoint handles a single photo and a carousel: one item in photo_images is a single-photo post; multiple items create a multi-photo post.
What does the TikTok photo API require?
| Requirement | Current documented value |
|---|---|
| Endpoint | /v2/post/publish/content/init/ |
| HTTP method | POST |
| Direct-publish scope | video.publish |
| Draft-upload scope | video.upload |
| Media type | PHOTO |
| Transfer method | PULL_FROM_URL |
| Photos per request | 1 to 35 URLs |
| Rate limit | 6 requests/minute per user access token |
| Title limit | 90 UTF-16 runes |
| Description limit | 4,000 UTF-16 runes |
Before Direct Post, TikTok requires the application to query current creator information and render the supported privacy choices. Do not hard-code PUBLIC_TO_EVERYONE as a default.
How do you send a Direct Post request for a carousel?
In short: Send an authorized access token, choose a privacy value returned by creator info, provide verified HTTPS image URLs, and save the returned publish_id so your system can poll the final status.
curl --fail-with-body \
-X POST https://open.tiktokapis.com/v2/post/publish/content/init/ \
-H "Authorization: Bearer $TIKTOK_ACCESS_TOKEN" \
-H "Content-Type: application/json; charset=UTF-8" \
-d '{
"media_type": "PHOTO",
"post_mode": "DIRECT_POST",
"post_info": {
"title": "Three launch details",
"description": "Swipe through the release notes. #product",
"privacy_level": "PRIVACY_LEVEL_FROM_CREATOR_INFO",
"disable_comment": false,
"auto_add_music": true,
"brand_content_toggle": false,
"brand_organic_toggle": true
},
"source_info": {
"source": "PULL_FROM_URL",
"photo_cover_index": 0,
"photo_images": [
"https://media.example.com/launch/01.webp",
"https://media.example.com/launch/02.webp",
"https://media.example.com/launch/03.webp"
]
}
}'
Replace PRIVACY_LEVEL_FROM_CREATOR_INFO with the value the creator selected from the privacy_level_options returned by creator info. The example marks the content as promoting the creator’s own business; commercial-content fields must reflect the real post.
JavaScript example
async function createTikTokPhotoPost(accessToken, imageUrls, privacyLevel) {
if (imageUrls.length < 1 || imageUrls.length > 35) {
throw new Error("TikTok photo posts require 1 to 35 image URLs")
}
const response = await fetch(
"https://open.tiktokapis.com/v2/post/publish/content/init/",
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json; charset=UTF-8",
},
body: JSON.stringify({
media_type: "PHOTO",
post_mode: "DIRECT_POST",
post_info: {
title: "Product details",
description: "Swipe for the complete walkthrough.",
privacy_level: privacyLevel,
disable_comment: false,
auto_add_music: true,
brand_content_toggle: false,
brand_organic_toggle: false,
},
source_info: {
source: "PULL_FROM_URL",
photo_cover_index: 0,
photo_images: imageUrls,
},
}),
},
)
const body = await response.json()
if (!response.ok || body.error?.code !== "ok") {
throw new Error(body.error?.message || `TikTok returned ${response.status}`)
}
return body.data.publish_id
}
Treat publish_id as job state, not proof that the post is already visible. Poll TikTok’s publish-status endpoint or process TikTok’s status webhooks until the job is terminal.
Should you use Direct Post or Media Upload?
In short: Direct Post publishes from your application after the required user controls and consent. Media Upload sends a draft to TikTok, where the creator opens an inbox notification and completes the editing and publishing flow.
| Mode | Scope | Result | Best for |
|---|---|---|---|
DIRECT_POST |
video.publish |
Publishing starts immediately | Approved scheduling products |
MEDIA_UPLOAD |
video.upload |
Draft continues in TikTok | Workflows that require TikTok editing |
For Media Upload, remove Direct Post-only fields such as privacy and use post_mode: "MEDIA_UPLOAD". TikTok currently requires a sufficiently recent TikTok application version for that photo-upload flow.
Which UX and compliance controls does TikTok require?
Building the HTTP call is only part of a compliant integration. TikTok’s Direct Post guidelines require applications to:
- Fetch current creator info when rendering the publishing screen.
- Display the creator’s nickname and the privacy options TikTok returns.
- Require a manual privacy selection; do not preselect one.
- Let the creator control comments. Duet and Stitch do not apply to photo posts.
- Collect the correct commercial-content disclosure.
- Show a preview and obtain express consent before sending the content.
- Keep captions and hashtags editable.
- Avoid promotional watermarks or logos added by the integration.
- Show publishing status while TikTok processes the post.
Unaudited clients are limited to private viewing. TikTok also documents a five-user daily cap for unaudited clients, plus creator and application posting caps that apply more broadly.
How must you host the images?
In short: TikTok photo posts only support PULL_FROM_URL. Serve every image over HTTPS from a URL owned by your developer application, avoid redirects, and keep the resource accessible while TikTok downloads it.
The most common integration mistake is using a signed URL on an unverified host. A URL can be publicly readable and still fail because TikTok has not verified its domain or prefix for your application.
Before launch:
- verify the base domain or exact URL prefix in TikTok for Developers;
- use stable HTTPS URLs without redirects;
- preserve the image order because it controls carousel order;
- select a zero-based
photo_cover_indexthat exists in the array; - test the real production hostname, not only TikTok’s sample media.
How do you schedule TikTok photo posts with Postqued?
In short: Postqued supplies the scheduling layer that TikTok’s public endpoint does not. Upload one or more image assets, read the connected creator’s constraints, validate the target, and set dispatchAt for the future publish time.
Postqued uses a static API key for its own API, while the connected TikTok account is authorized through the product’s user flow.
1. Upload the images
For each image, call POST /v2/content/upload, send the bytes to the returned presigned URL, then call POST /v2/content/upload/complete. Save every contentId in carousel order.
2. Read creator info
GET /v2/integrations/ACCOUNT_UUID/creator-info?workspaceId=WORKSPACE_UUID
Use the returned privacy choices rather than assuming public publishing is available, and pass the creator’s selection as privacyLevel below.
3. Validate and schedule
{
"workspaceId": "WORKSPACE_UUID",
"contentIds": ["IMAGE_UUID_1", "IMAGE_UUID_2", "IMAGE_UUID_3"],
"targets": [
{
"platform": "tiktok",
"accountId": "ACCOUNT_UUID",
"intent": "publish",
"caption": "Swipe through the launch details.",
"dispatchAt": "2026-08-15T17:00:00Z",
"options": {
"privacyLevel": "PRIVACY_LEVEL_FROM_CREATOR_INFO",
"disableComment": false,
"autoAddMusic": true,
"photoCoverIndex": 0,
"commercialContent": false,
"brandContentToggle": false,
"brandOrganicToggle": false,
"authenticContentConfirmed": true
}
}
],
"dryRun": true
}
Send this body to POST https://api.postqued.com/v2/publish. After validation, repeat with dryRun: false and a fresh UUID in the Idempotency-Key header. Then poll the publish status until the post is live. The live OpenAPI document is the source of truth for the exact schema.
What are the common TikTok photo API errors?
url_ownership_unverified
The image host or prefix is not verified for the TikTok developer application. Verify it or serve the assets from a verified property.
privacy_level_option_mismatch
The privacy value is absent or not one of the choices returned by creator info. Refresh creator info and require a valid user selection.
scope_not_authorized
The access token does not include video.publish or video.upload for the selected flow. Re-authorize with the required approved scope.
spam_risk_too_many_posts
The creator has reached a posting cap. Stop retrying immediately and surface the provider error to the user.
rate_limit_exceeded
The user access token exceeded the documented request rate. Queue requests and back off rather than switching tokens or hiding the error.
Final checklist
- Use the official
/v2/post/publish/content/init/endpoint. - Fetch creator info before every publishing flow.
- Provide one to 35 verified HTTPS image URLs.
- Use
video.publishfor Direct Post orvideo.uploadfor Media Upload. - Keep privacy and commercial disclosures under user control.
- Save
publish_idand monitor final status. - Complete TikTok’s audit before expecting public Direct Post behavior.
Final recommendation
Build on TikTok’s endpoint directly only if TikTok publishing is your core product and you can own the app review, audit, hosting and queue. Otherwise, use the Postqued API: up to 35 images per photo post, dryRun validation, an Idempotency-Key header that prevents duplicate publishes when you retry, dispatchAt scheduling, and the same API for 9 other networks. Solo costs $9/month with unlimited posts and API + MCP access.
See the Postqued TikTok scheduler, or read does TikTok have an official scheduling API?.
Building a carousel pipeline? Solo at $9/month includes the API for five accounts. Start a 7-day free trial.