TikTok ad price must match the landing page: burn one price with Sume
TikTok's example: an ad saying 'up to 50% off' while the site says 30% is not allowed. Drive the video and the page from one price variable. Code included.

Make the number in the video and the number on the page come from one value. TikTok's Misleading and false content policy says ad content inconsistent with the product's website is not allowed, and its own example is an ad that states 'up to 50% off' while the website says 'up to 30% off'. It also lists an ad that introduces product A while the website shows product B, and a promotion such as 'Prizes to be won!' with no relevant information on the landing page.
The policy page also says ad content and landing pages with mismatched or inconsistent information on promotion, price or discounts, or missing disclaimers, are not allowed. A burned-in price that went stale after a sale ended is the most common way to fail this.
Mismatches TikTok names
Read your clip against the page, not against your memory of the campaign.
| Mismatch | Policy example |
|---|---|
| Discount differs | Ad: up to 50% off; website: up to 30% off |
| Product differs | Ad introduces product A; site shows product B |
| Promotion unsupported | Ad says 'Prizes to be won!'; no information on the page |
| Page too vague | Ad features a clothing brand; site shows generic city images |
Burn the price from one variable
With Sume video captions you can burn authored text onto an existing clip using cues, each with text, start and end in seconds. No speech-to-text runs on this path, so the text is exactly what you send. A standalone caption job is $0.20 for a clip up to 60 seconds, and video_url must be a public HTTPS video URL Sume can fetch. Set the same PRICE string on your landing page template and in this call:
import asyncio, json, os, urllib.request
PRICE = "Up to 30% off"
def post(body):
req = urllib.request.Request(
"https://api.sume.com/v1/video-captions",
data=json.dumps(body).encode(),
headers={
"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": "price-cue-001",
},
method="POST",
)
with urllib.request.urlopen(req) as r:
return json.load(r)
async def main():
body = {
"video_url": "https://media.sume.com/artifacts/example/clean.mp4",
"cues": [{"text": PRICE, "start": 1.0, "end": 4.0}],
}
print(await asyncio.to_thread(post, body))
asyncio.run(main())Steps
- Keep one source of truth for the offer (price, percent, end date).
- Render the landing page and the cue text from it.
- When the offer changes, re-burn a fresh caption job on the clean master, not on an already captioned file.
- Check the ad review preview and the live landing page side by side.
Where the number tends to drift
Three places hold a price: the video, the ad text, and the landing page. The ad text and the page are usually driven from a feed; the video is a file, and files do not update themselves. That is why the video is the one that goes stale.
A simple rule is to treat the burned-in text as a build artifact. When the offer string changes, a script re-runs the caption job against the same clean master and swaps the creative. The call above is idempotent per Idempotency-Key, so give each offer version its own key, such as one that includes the date, and a retry after a network failure will not create a second job.
Caveat
Burn onto a clean master each time. TikTok's rule is about consistency at review and afterwards; an expired offer in an already-live video is still inconsistent with the page. Replace the creative when the offer ends.
Sources
Related posts
More in Developers
- TikTok Shop trend research via Sume trending search, $0.10 a call
Before a holiday product video, see what is trending for your product on TikTok. Sume's trending-videos search returns public video metadata at $0.10 a call.
- TikTok total_chunk_count rounds down: the final chunk can reach 128 MB
TikTok's Content Posting API sets total_chunk_count to video_size divided by chunk_size, rounded down, so the last chunk is bigger. Worked sizes and a checker.
- Track AI video spend per job: Synthesia Billing API vs Sume job cost
Synthesia added a Billing API and auto top-up. On Sume, every finished job reports usage.cost, so you can keep a per-job ledger without a billing endpoint.
- Transcribe a 30-minute file: OpenAI 25 MB cap vs Sume 10-minute jobs
A 30-minute file hits OpenAI's 25 MB upload limit and Sume's 10-minute duration hint. How to split once, transcribe in pieces, and re-base the word timings.
Written by Sume