Many brand pages in the Meta Ad Library read like a copywriter's folder. Temu's reads like a database export. In the 150 active ads I pulled on November 6, 2026, 149 are labelled DPA, Meta's dynamic product ads that fill a template from a product catalog. And one of the ad texts still starts with the catalog variable itself: {{product.custom_label_0}}.
Below: those ads as records, a short script, and where the source stops you.
Sample frame
| Source | Snapshot date | Filters | Ads collected | Texts analysed |
|---|---|---|---|---|
| Meta Ad Library | 2026-11-06 | page facebook.com/temu, activeStatus: active, cap 150, no country or date filter |
150, all active | first 30 bodies in library order, cut at 300 characters; 24 distinct |
| Google Ads Transparency Center | 2026-11-06 | domain temu.com, cap 150, no country or date filter |
150 | headline decoded for 13 ads; copy not analysed |
Both runs hit the cap of 150, so each is likely a slice of a larger set of unknown size. Meta shows catalog shopping ads in several languages; Google shows mostly text ads served for the domain. Almost everything below is about the Meta set.
What a catalog ad looks like as data
Each Meta record carries a title, a body, a ctaText, placements and a start date. On a DPA, the title is the product name from the feed, often a long string of keywords:
"Shading Tarpaulin For Camping, Protective Items, Tent Decoration, Outdoor Canopy Tent Cloth, Rain Shade Cloth, For Wilderness Camping Rain And Wind Protection, Courtyard Garden Backyard,"
The body is the template text around it. In this record it is in Portuguese, under an English product name:
"⚡️Ofertas relâmpago⚡️ Descontos incríveis em tudo! Não perca! Baixe agora o app da Temu! 📱"
So for a developer the useful questions are what the template layer looks like and whether it ever breaks.
The script
I ran this over the collector's JSON. Language is guessed with marker words, checked in a fixed order, so it is crude but auditable.
import collections
import json
import re
with open("promo/data/2026-11-06-temu.json", encoding="utf-8") as f:
d = json.load(f)
fb = d["facebook"]
bodies = fb["bodies"]
# Language by marker words, checked in this order; anything else counts as English.
LANG = [
("pt", re.compile(r"\b(Comece|Compre|Descontos|frete|Baixe|aproveite)\b|relâmpago")),
("es", re.compile(r"¡|Ahorra|\b(Descuentos|Envío|Solo|increíbles|Descarga)\b")),
("tl", re.compile(r"\b(sa|para|Wag|marami)\b")),
]
def lang(text):
for code, rx in LANG:
if rx.search(text):
return code
return "en"
RULES = {
"unfilled {{...}} placeholder": re.compile(r"\{\{.*?\}\}"),
"mentions the app or downloading": re.compile(r"download|descarga|baixe|\bapp\b", re.I),
"'new users' condition": re.compile(r"new (app )?users|nuevos usuarios", re.I),
"hashtag": re.compile(r"#\w"),
}
print(f"Meta ads: {fb['ads']}, active: {fb['active']}")
print("Meta formats:", fb["formats"])
print(f"Bodies kept: {len(bodies)}, distinct: {len(set(bodies))}")
print("Languages:", dict(collections.Counter(lang(b) for b in bodies).most_common()))
for name, rx in RULES.items():
print(f" {name}: {sum(1 for b in bodies if rx.search(b))}")
top = collections.Counter(bodies).most_common(1)[0]
print(f"Most repeated body ({top[1]}x): {top[0][:40]!r}...")
print("\nLongest-running Meta ads (body language, product title):")
for a in fb["longestRunning"]:
print(f" {a['daysActive']:>3}d {a['startDate']} body={lang(a['body'])} {a['title'][:34]}...")
g = d["google"]
print(f"\nGoogle ads: {g['ads']}, decoded headlines: {len(g['headlines'])}")
print("Google advertisers:", g["advertisers"])
Output, pasted as-is:
Meta ads: 150, active: 150
Meta formats: [['DPA', 149], ['IMAGE', 1]]
Bodies kept: 30, distinct: 24
Languages: {'en': 23, 'es': 4, 'tl': 2, 'pt': 1}
unfilled {{...}} placeholder: 1
mentions the app or downloading: 8
'new users' condition: 2
hashtag: 4
Most repeated body (4x): '🛍️ Grab Hot Deals 🎉\n🛒 Shop the Best\n🔥 Ho'...
Longest-running Meta ads (body language, product title):
366d 2025-11-06 body=es Camisetas Sin Deportivas Para Homb...
366d 2025-11-05 body=pt Shading Tarpaulin For Camping, Pro...
366d 2025-11-05 body=en Stylish Letter Print Men's Casual ...
366d 2025-11-05 body=es Máquina de Troquelado y Repujado M...
361d 2025-11-10 body=en Women's Retro Flat Loafers, Comfor...
356d 2025-11-15 body=es Camiseta informal de manga larga c...
326d 2025-12-15 body=en 1 Pair Elegant Floral Print Women'...
314d 2025-12-28 body=en Elegant A-line Maxi Dress With Puf...
Google ads: 150, decoded headlines: 13
Google advertisers: [['ELEMENTARY INNOVATION PTE. LTD.', 137], ['Whaleco Inc.', 12], ['Dagen He', 1]]
The template layer
One Meta page, four languages in 30 texts. The most repeated body appears 4 times and names no product or offer:
"🛍️ Grab Hot Deals 🎉
🛒 Shop the Best
🔥 Hot Deals Inside 🎁"
Two texts carry a condition instead, such as "Only for new app users with qualifying orders". Across all 150 ads, "Shop now" is the button on 138.
The leaked variable
This is the body as the library stores it:
"{{product.custom_label_0}}🎉 Unbelievable na DISCOUNTS sa Fashion, Home, at marami pang iba! Wag palampasin! I-Download na ang Temu! 💕"
custom_label_0 is a catalog field advertisers use for their own tags. The library shows it unresolved; I can't tell whether people saw the braces, nothing, or a filled-in value, since the library may store the template rather than the rendered ad. Still, \{\{.*?\}\} is a cheap check for any feed pipeline.
A note on Google
Searching Google's library for an advertiser called Temu would miss most of it: 137 of the 150 ads for temu.com run under ELEMENTARY INNOVATION PTE. LTD., 12 under Whaleco Inc. Query by domain, then group by advertiser.
What this data can't tell you
- A capped slice. Both sources stopped at 150, and text counts come from 30 Meta bodies cut at 300 characters.
- Labels and dates come from the source. 149 of 150 marked DPA may partly reflect how the library labels shopping ads. Four ads show 366 days and started November 5–6, 2025, exactly a year before the snapshot, so I don't read that order as a ranking.
- No spend, reach or results. Nothing says which template sells or how Temu splits budget. Google left headline and description empty for 137 of 150 ads, so I skip Google copy.
Running it on another store
I built these scrapers. The Facebook Ads Library Scraper returns title, body, CTA, display format, placements and start date per ad, and the Google Ads Transparency Scraper returns format, advertiser and dates for a domain, as JSON or CSV. The Meta input for this post was {"queries": ["https://www.facebook.com/temu"], "maxAdsPerQuery": 150, "activeStatus": "active"}. Both are free to try on the Apify free plan, and a run of a few hundred ads costs well under a dollar.
Not affiliated with Temu; all data is from public ad libraries.
If you were writing a lint step for a catalog ad feed, which check would you ship first: unresolved {{...}} tokens, a body language that doesn't match the title, or an offer line with no product in it?
Top comments (0)