TikTok's AI generated label isn't automatic. Set it via API

Your pipeline renders an AI-assisted clip, uploads it through TikTok's Content Posting API, and gets back an HTTP 200. Nothing in that path told TikTok the video is AI-generated. Since September 24, 2026, that omission stops being a per-video problem and starts being an account problem.
Quick answer: The TikTok AI generated label is set programmatically by sending"is_aigc": trueinside thepost_infoobject of the Direct Post init call, which produces the creator-labeled "AI-generated" tag on the published video. Because a full re-encode rebuilds the container and drops the C2PA Content Credentials TikTok also reads, burn a visible disclosure into the pixels as a fallback with FFmpeg's drawtext filter:drawtext=text='AI-generated':x=(w-text_w)/2:y=h-560on a 1080x1920 frame. If you'd rather not host an encoder to stamp one line of text, send the render to FFmpeg Micro as one API call and get the labeled file back.
What actually changed on September 24, 2026
The AI-labeling rules did not change. TikTok's newsroom post on AIGC labels still says what it said before: realistic AI-generated or heavily edited depictions of people, places, and events must be disclosed, and a clear caption or sticker counts. What changed is enforcement. TikTok's rewritten Community Guidelines were published August 25, 2026 and took effect September 24, 2026.
The clause that hits automated channels is in TikTok's For You feed standards: accounts posting a high volume of For-You-ineligible content can be made ineligible for the feed themselves. Reused content without creative edits isn't removed, and no per-video screen tells you a post was excluded. Reach just collapses, which is why pipeline owners find out three weeks late from an analytics chart.
So the penalty moved from the video to the channel. A faceless channel pushing 30 renders a day can now lose feed distribution across every post, including the ones that were fine.
Step 1: Set is_aigc on the Direct Post call
The API field exists and it's a plain boolean. TikTok's Direct Post reference documents is_aigc inside post_info with the description "Set to true if the video is AI generated content." Setting it produces the same creator-labeled AI-generated tag as the in-app toggle, the part creator-facing guides skip because they only cover the publish screen.
POST https://open.tiktokapis.com/v2/post/publish/video/init/
{
"post_info": {
"title": "Episode 412 #ai",
"privacy_level": "PUBLIC_TO_EVERYONE",
"disable_comment": false,
"video_cover_timestamp_ms": 1000,
"is_aigc": true
},
"source_info": {
"source": "PULL_FROM_URL",
"video_url": "https://cdn.example.com/renders/ep-0412-labeled.mp4"
}
}
One trap sits next to this and looks nothing like a labeling bug. Content posted by clients that haven't passed TikTok's audit is restricted to private viewing, so your pipeline gets a clean 200, a real publish_id, and zero public posts. Check audit status before you touch the disclosure logic.
Step 2: Burn the disclosure into the pixels, because a re-encode drops the metadata
TikTok detects AI content three ways, per its November 2025 update: C2PA Content Credentials, creator labeling tools, and invisible watermarking only TikTok can read. It reported over 1.3 billion videos labeled as of November 19, 2025. The C2PA path is the one your pipeline breaks.
In MP4 and MOV files, the C2PA manifest lives in a top-level uuid box, outside the video and audio streams. A full re-encode builds a new container with no uuid box unless something deliberately copies it over, and FFmpeg does not. So the moment you concat clips, add music, or resize a Veo or Sora output, the provenance your generator attached is gone. TikTok's auto-labeling has nothing left to read.
A burned-in line of text survives everything downstream: your re-encode, TikTok's transcode, a Spark Ads crop, and auto-dub. On-screen disclosure that isn't part of the base video file can vanish when TikTok reformats a post, which is what happens to text added in TikTok's own editor. TikTok's ad guidance separately accepts a clear disclaimer, caption, or watermark inside the creative as disclosure.
The raw FFmpeg command
Position is the whole job. On a 1080x1920 frame, TikTok's own interface covers roughly the bottom 270 to 484 pixels depending on caption length, and closer to 370 pixels on ads with a CTA button. TikTok also renders its own AIGC label in the bottom-left. Put your disclosure at y=h-560 and it clears both the chrome and that label.
ffmpeg -i ep-0412.mp4 -vf "drawtext=\
fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:\
text='AI-generated':\
fontcolor=white:fontsize=44:\
box=1:boxcolor=black@0.55:boxborderw=18:\
x=(w-text_w)/2:y=h-560" \
-c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p \
-c:a copy ep-0412-labeled.mp4
Two things break this in containers. drawtext needs a build compiled with --enable-libfreetype, plus fontconfig and an actual font package. The n8n community thread on bundling FFmpeg into the official image flags the same failure: without ttf-dejavu or equivalent, drawtext and subtitle burn-in fail silently on a distroless base. Audio gets -c:a copy since nothing touches it.
The same job as one API call
Running that in production means you own an encoder, a font layer, a queue, and a timeout policy for a job whose entire output is one line of text on a 12-second clip. That's the case for moving the render step off your box.
| Self-hosted FFmpeg | FFmpeg Micro | |
|---|---|---|
| Install | Binary, libfreetype, fontconfig, fonts | Nothing |
| Long renders | Your worker holds the request open | Job ID plus webhook |
| Batch of 30/day | Your queue and your scaling | One call per clip |
| n8n Cloud | Not possible, Execute Command is disabled | HTTP Request node |
FFmpeg Micro runs that overlay as a single POST with no encoder to host, on a free tier that covers a small channel's daily volume:
curl -X POST https://api.ffmpeg-micro.com/v1/jobs \
-H "Authorization: Bearer $FFMPEG_MICRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input_url": "https://cdn.example.com/renders/ep-0412.mp4",
"operations": [{
"type": "text_overlay",
"text": "AI-generated",
"position": "bottom_center",
"offset_y": 560,
"font_size": 44,
"background": "#000000",
"background_opacity": 0.55
}],
"webhook_url": "https://hooks.example.com/webhook/tiktok-publish"
}'
You get a job ID back immediately and the output URL arrives on the webhook. The operation schema is in the docs, and you can dial in font size and offset against a real vertical file in the playground first.
The n8n and Make versions
In n8n, this is an HTTP Request node, not a community node and not Execute Command, which n8n disabled by default. POST to https://api.ffmpeg-micro.com/v1/jobs, use a Header Auth credential carrying Authorization: Bearer <key>, and send the JSON above with input_url bound to your render step's output. Point webhook_url at a Webhook node in the same workflow, then feed that node's output_url into the TikTok PULL_FROM_URL call. The n8n HTTP pattern for FFmpeg work is the same one you'd use for any render step.
Make is the same three fields in a different wrapper: an HTTP module set to "Make a request" with POST, the Authorization header, the JSON with the input URL mapped from the previous module, and a Custom Webhook module as the resume point. The webhook pattern for long-running video jobs applies here too: don't poll on a 40-second timeout when the callback is free.
Step 3: Stop shipping byte-identical renders
Labeling correctly doesn't help if the account is already leaking distribution for another reason. TikTok's duplicate detection layers file hash, perceptual hash, audio fingerprint, and metadata, and that fingerprint database persists for months. A pipeline running one template through 30 captions a day trips the perceptual layer no matter what the file hash says.
The tempting fix is to nudge the hash: re-encode at a random CRF, strip metadata, shift a pixel. Don't bother. The perceptual and audio layers are built to ignore that kind of noise. TikTok's originality rule is about creative edits: variation a human would notice.
- Vary the first 1.5 seconds. Different hook text, different opening frame, different cut point.
- Vary the audio bed, not just the volume. A different track changes the audio fingerprint outright.
- Vary the framing across posts. Alternate crops and pacing rather than one locked template. Veo and Sora only emit two aspect ratios, so you already have a reframe step that can do double duty.
- Add real narration or on-screen commentary. Subtitles alone do not count as a creative edit.
Where this breaks in production
The disclosure sits in a bad spot more often than it's missing. At y=h-560 on 1080x1920 you're clear of the interface, but if you also burn captions with ASS MarginV, the two can stack. Render one file and look at it before you batch 300.
Text that reads fine on your monitor disappears on a bright frame. The box=1:boxcolor=black@0.55 pair keeps the label legible over white product shots and sky, and it's the first thing people drop for aesthetics.
Setting is_aigc and skipping the burn-in leaves you with nothing when TikTok reformats the post. Burning in and skipping is_aigc leaves the platform's own label off. Do both, and remember that the commercial content disclosure toggle for paid promotion is a third obligation.
When a burned-in label is the wrong call
If everything you publish is real footage with light AI assistance, like an AI-written script read by a human on camera, you're outside the rule. Stamping "AI-generated" on a real person's face is worse than not labeling it, because it's inaccurate.
If your output is one video a week that a human posts by hand, the in-app toggle is faster than any of this. Programmatic labeling earns its keep at volume, when the publish step is an HTTP call and nobody sees the video before it goes live.
And if you need a designed lower-third with animation and brand typography rather than a legible compliance bar, a template-editor service fits better than a filter chain. FFmpeg Micro renders media, not layouts.
FAQ
Does setting is_aigc replace the burned-in disclosure?
Setting is_aigc and burning in a visible disclosure solve different failure modes, so a pipeline publishing at volume should do both. The is_aigc flag produces TikTok's own creator-labeled tag on the post. The burned-in text is the only disclosure that survives a re-encode, a Spark Ads crop, or an auto-dub pass.
Does my FFmpeg step destroy C2PA Content Credentials?
A full FFmpeg re-encode destroys C2PA Content Credentials in MP4 and MOV files, because the manifest lives in a top-level uuid box in the container rather than in the video or audio stream, and the re-encode writes a new container. That's why TikTok's auto-labeling can fail on AI footage that was genuinely credentialed at generation time.
My TikTok API posts return 200 but nobody sees them. Is that a labeling problem?
Posts returning HTTP 200 with no public visibility is almost always TikTok's audit restriction, not a labeling failure. Content posted by unaudited API clients is forced to private viewing, so the call succeeds and the video publishes to an audience of one. Check audit status in the TikTok developer portal first.
Does AI narration or AI b-roll need the label?
AI narration and generic AI b-roll need the label when they create a realistic depiction of a person, place, or event a viewer could mistake for real. A cloned voice of a real person needs disclosure. An obviously synthetic animation generally does not.
Is the September 2026 penalty per video or per account?
The September 2026 enforcement change applies at the account level: TikTok's For You feed standards state that accounts posting a high volume of feed-ineligible content can be made ineligible for the feed themselves and harder to find. One unlabeled post is a per-video issue, but a pipeline publishing unlabeled or unvaried content daily risks the whole channel's distribution.
Wiring the disclosure into the render is a 20-minute change if you already have a render step, and a webhook plus one JSON body if you don't. Sign up free and stamp the label on your next batch before the flag ever reaches TikTok.
About Javid Jamae
Founder & CEO at FFmpeg Micro
Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.
You might also like

Extract the Last Frame in FFmpeg to Chain Veo 3 Clips Cleanly
Extract last frame FFmpeg commands return the wrong frame after a fast seek. Use -sseof with -update 1, then join Veo 3 and Sora 2 clips with no seam.

Frame counts lie. FFmpeg progress percentage is in out_time_us
Parse FFmpeg's -progress output for a real ffmpeg progress percentage: out_time_us against an ffprobe duration, speed= for ETA, and where parsing breaks.

scale_npp Removed in FFmpeg 9: Move GPU Scaling to scale_cuda
scale_npp removed in FFmpeg 9.0 along with all libnpp filters. The scale_cuda rewrite for every -vf chain, plus what a rented GPU actually costs you now.
Ready to process videos at scale?
Start using FFmpeg Micro's simple API today. No infrastructure required.
Get Started Free