API GuidesAugust 2, 202611 min readingMarcel Czuryszkiewicz

TikTok API Approval

An unaudited TikTok app is not a broken app. It publishes normally and every post lands private, with no parameter that changes it. Covers the two stages of approval, the scopes each unlocks, domain verification for URL uploads, the creator_info call before every publish, and what gets rejected.

TikTok API approval has two stages, and almost everyone discovers the second one the hard way. Getting the Content Posting API added to your app takes a form. Getting your posts to be visible to anyone other than the creator takes an audit. In between there is a state where the integration works perfectly, returns success on every call, and publishes every post as private. This covers both stages, the scopes each unlocks, and the two requirements that fail audits most reliably.

Overhead airport signage pointing to two separate ranges of departure gates, one arrow left and one arrow down
First gate, then the second one.

Two stages, not one

StageWhat it isWhat it unlocksTypical wait
Product accessAdd the Content Posting API to your app in the developer portal. A form: what the app does, who uses it, links to your site and policiesThe endpoints start responding. You can authenticate, query creator info, and publishDays
Content auditA review of your app's posting behaviour, submitted separately once the integration worksPUBLIC_TO_EVERYONE becomes a legal privacy level. Posts become visibleWeeks, and variable

The critical thing about this split: stage one gives you a working integration and stage two gives you a useful one. Between them, everything succeeds and nothing is visible.

There is also a distinction between the Direct Post capability and Upload (sometimes called draft or inbox). Direct Post publishes straight to the creator's profile. Upload drops the video into the creator's TikTok inbox for them to finish and post themselves. Upload has a lower bar. If your product can tolerate the creator completing the post in the app, that path avoids a large part of this process, and it is worth deciding deliberately rather than by default.

Life without the audit

This is the section that exists because nothing else on the internet says it plainly. We went through this stage ourselves and watched the posts land private, so what follows is observed rather than inferred.

The words AS FAR AS THE EYE CAN SEE mounted in large letters along the roofline of a dark building against a blank white sky
The post is there. Nobody can see it.

An unaudited app can publish. Every post lands as private, and nothing in the API tells you that is going to happen.

You call the init endpoint, upload the video, receive a publish id, poll the status, and get a completed publish. TikTok returns success at every step. The video appears on the creator's account, visible only to them.

The part that makes this genuinely hard to catch: creator_info still lists PUBLIC_TO_EVERYONE in privacy_level_options. The response looks exactly like an audited app's. You build your composer from a list of privacy levels that includes public, the creator picks public, you send public, the call succeeds, and the post is private anyway. There is no field, no warning and no error that reveals the state you are in.

The gate is on access to the audited surface, not on the option list. That distinction is why so many teams conclude their code is wrong.

Three consequences worth planning around.

Your integration tests pass and your product is broken. Every assertion about the API succeeds, including any assertion about the privacy level you requested. The only way to catch this is to open the account in the app, logged in as somebody who is not the creator, and look.

Customers will report it as your bug. From their side, they scheduled a post, your dashboard says published, and their audience saw nothing. There is no error to show them. Put the app's audit status in your own UI and say so explicitly, before they find out.

You cannot back-fill. Posts published while unaudited stay private. Passing the audit does not retroactively publish them. If you onboarded customers during that window, those posts need to be re-created.

Plan the sequence so that the audit lands before customer onboarding, not after. It is the single most expensive ordering mistake in this process.

Scopes and what each one buys

ScopeWhat it allowsNeeded for posting?
user.info.basicOpen id, display name, avatarYes, effectively mandatory
user.info.profileProfile fields including bio and verificationRecommended
user.info.statsFollower and like countsNo
video.publishDirect Post: publish straight to the profileYes, for direct posting
video.uploadUpload to the creator's inbox as an unfinished draftYes, for the draft flow
video.listRead the creator's published videosNo, but needed for history and verification
video.insightsPer-video analyticsNo
comment.list, comment.list.manageRead and manage commentsNo

Two notes that matter.

video.publish and video.upload are different products, not two names for the same thing. Requesting one does not grant the other, and a product that offers both flows needs both.

Requesting scopes you do not demonstrably use is a rejection reason. Ask for what your submitted demo actually shows. Adding comment.list because you might want it later reads as overreach, and it can sink the parts of the submission you needed.

Domain verification for URL uploads

There are two ways to get a video to TikTok: send the bytes yourself (FILE_UPLOAD), or give TikTok a URL to fetch (PULL_FROM_URL).

PULL_FROM_URL is the better option at any scale, and it is the one we run. You do not proxy the bytes, TikTok pulls directly from your storage, and the upload path gets much simpler. The cost is that you have to do the verification below, once, properly.

It also requires that TikTok knows the domain is yours. Verify it in the developer portal, by serving a signature file at a known path or by adding a DNS TXT record. Until you do, any pull request is refused with a message about URL ownership verification rules, which reads as though your URL is malformed rather than your domain being unverified.

Three practical details.

Verify every domain you will actually serve from. A CDN subdomain is a different domain. Signed URLs on a bucket domain need that bucket domain verified, not your marketing site.

The URL has to be publicly fetchable at the moment TikTok pulls. Short-lived signed URLs expire. Give them enough lifetime to survive queueing and transcoding, not just the moment you generated them.

Verification is per app, not per account. Do it once, at setup, and treat it as infrastructure rather than something per customer.

creator_info is not optional

Before publishing, you have to ask TikTok what this specific creator is allowed to do:

curl -X POST "https://open.tiktokapis.com/v2/post/publish/creator_info/query/" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json; charset=UTF-8"
{
  "data": {
    "creator_username": "someone",
    "creator_nickname": "Someone",
    "privacy_level_options": ["PUBLIC_TO_EVERYONE", "MUTUAL_FOLLOW_FRIENDS", "SELF_ONLY"],
    "comment_disabled": false,
    "duet_disabled": false,
    "stitch_disabled": true,
    "max_video_post_duration_sec": 600
  },
  "error": { "code": "ok" }
}

Four things in that response change what your publish request may contain.

privacy_level_options is per creator, not global. A private account will not offer PUBLIC_TO_EVERYONE. Sending a value that is not in this list is rejected. Note the asymmetry with the previous section: this list reflects the creator's settings, not your app's audit status, so its presence is not evidence that a public post will actually be public.

comment_disabled, duet_disabled and stitch_disabled are the creator's settings. If stitch_disabled is true, you cannot enable stitching for that post. Your UI should reflect the creator's actual options rather than offering everything and failing.

max_video_post_duration_sec varies by creator. Not everyone gets 10 minutes.

Skipping this call is an audit failure. TikTok expects the publishing UI to be built from this response, and reviewers check that the creator's settings are respected. It is also a correctness requirement: without it you are guessing at values the platform will reject.

Call it when the user opens the composer, not once at connection time. These settings change.

bundle.social

The audit, the domain verification and the creator_info contract are already ours.

Audited and domain-verified on our side, so your posts are public from day one.

What gets rejected

TikTok reviews a recording of your product, the same as Meta. The list below is what the review criteria ask for, read against what the API actually requires. We do not publish the details of our own submission, so treat this as a checklist rather than a war story.

The demo does not show the full flow. Reviewers need to see the login, the consent screen, the composer built from creator_info, and the post appearing. A product tour without the authorisation step fails.

The creator's settings are not respected. If your composer offers "allow duet" for a creator whose duet_disabled is true, that is a visible contradiction of what the API told you.

No visible confirmation of what will be posted. TikTok expects the creator to see and approve the caption, the privacy level and the interaction settings before publishing. A one-click publish with no review screen does not pass.

Missing or unreachable policy pages. Privacy policy and terms have to load, and they have to describe what you do with the content.

Scope overreach. Requesting more than the demo shows.

The theme is the same as every other platform review: the reviewer has limited time and no context. Narrate the recording, caption what is happening on screen, and point your written justification at timestamps. Our own experience with Meta's App Review was that the most common rejection reason was not a technical gap at all, it was the reviewer not understanding what they were looking at. That lesson transfers directly, and it costs more here, because TikTok's turnaround is measured in weeks rather than days.

Spam risk, and rejections that are not your fault

Once you are live, a category of failures shows up that no amount of correct code prevents. TikTok's anti-spam system rejects publishes based on the creator's behaviour, not yours.

ReasonMeaningRetryable?
spam_risk_too_many_postsThe creator hit a daily posting limitNo
spam_risk_too_many_pending_shareToo many uploads queued and unpublished for this creatorNo
spam_risk_user_banned_from_postingThe creator is banned from postingNo
spam_risk_textThe caption tripped content filtersNo
spam_riskGeneric risk flagNo
auth_removedThe creator revoked the appNo, needs re-authorisation
video_pull_failed, photo_pull_failedTikTok could not fetch your URLYes
internalTikTok-side failureYes

The whole spam_risk family is terminal. Retrying makes it worse, because more pending shares is itself one of the triggers. Surface these to the customer with the actual reason, because "publishing failed" for spam_risk_user_banned_from_posting sends them to your support queue for a problem only TikTok can resolve.

The full classification logic across platforms, including which TikTok error codes we treat as transient, is in social media API error handling.

The shorter path

Two approval stages with different criteria, a middle state where everything works and nothing is visible, domain verification per app, a mandatory pre-publish call whose response shapes your UI, and a review that judges a video recording. Weeks of calendar time before your first customer publishes anything, and none of it is engineering.

bundle.social's TikTok API is already audited and verified. Your users connect their account, you send a video and a caption, and the post is public because the audit is ours. No developer portal, no domain verification, no recording.

If you want the endpoint-level detail on publishing rather than the access process, see our TikTok content posting API page.

Frequently asked questions

How long does TikTok API approval take?

Product access is typically days. The content audit that makes posts public takes considerably longer and is variable. Plan the audit before customer onboarding, because posts published while unaudited stay private permanently.

Why are my TikTok API posts private?

Because the app has not passed the content audit. An unaudited app publishes successfully and every post lands as self-only, regardless of the privacy level you send. What makes it hard to diagnose is that creator_info still lists PUBLIC_TO_EVERYONE as an available option, so nothing in the API indicates the state you are in.

What is the difference between video.publish and video.upload?

video.publish posts directly to the creator's profile. video.upload places the video in the creator's inbox for them to finish and post in the app. They are separate products with separate approval requirements.

Do I need to verify my domain for the TikTok API?

Only if you use PULL_FROM_URL, where TikTok fetches the video from your storage. Verify every domain you serve from, including CDN subdomains. FILE_UPLOAD avoids the requirement but means proxying the bytes yourself.

Can I skip the creator_info call?

No. Privacy options, comment, duet and stitch settings and maximum duration are per creator, and sending values the creator does not allow is rejected. Skipping it is also a reliable audit failure, because reviewers check that the composer reflects the creator's actual settings.

Marcel Czuryszkiewicz
Written by

Marcel Czuryszkiewicz

Co-Founder

Marcel is one of the builders behind bundle.social, creating a social media API with a strong focus on developer experience, reliability, and real support. Before bundle.social, he worked at Samsung R&D, Reply AI, and Docplanner, building internal tools that helped companies grow. He brought that same practical, product-focused mindset to bundle.social, so you can trust that your project is in good hands.