Holiday Promo Watch is a small Python Black Friday tracker for e-commerce teams. It takes a dated snapshot for a few brands each day: promo wording on their homepage and sale pages, their latest X, Instagram and TikTok posts, and a web search for their Black Friday page. Over the season it builds a timeline of when each brand started and on which channel.
The problem
Every year brands start their Black Friday push earlier, and on different channels. Knowing when competitors start, and where, helps you plan your own timing. Checking sites and social accounts by hand every day for weeks is not realistic.
What the app does
- Reads a
brands.jsonwith each brand's site, sale pages and social usernames. - Reads homepage and sale page text, including banners and promo strips.
- Pulls the latest posts on X, Instagram and TikTok and counts promo wording.
- Searches the web for each brand's Black Friday page.
- Appends dated rows to
promo_timeline.csvso daily runs build a timeline.
How it works
- Pages.
GET /web/extractas text and as HTML to read titles, banners and promo strips. - Posts.
GET /twitter/user/posts,GET /desearch/instagram/profile/{username}/postsandGET /desearch/tiktok/profile/{username}/posts. - Search.
GET /webfor "<brand> black friday 2026". - Timeline. Each run appends rows per brand and channel with promo terms found.
The code
def extract(c, channel, brand, url, html_check=True):
"""Extract with js=false, retried once when no text comes back. Both tries are kept."""
tries = []
for attempt in (1, 2):
status, body = c.get(channel, brand, "/web/extract", {"url": url, "format": "text", "js": "false"}, attempt)
text = body if isinstance(body, str) else ""
tries.append({"attempt": attempt, "status": status, "chars": len(text.strip()),
"detail": body.get("detail") if isinstance(body, dict) else None})
if text.strip():
break
else:
text = ""
out = {"url": url, "text": text, "tries": tries}
if html_check:
# format=text keeps the main block; banners, promo strips and the page title live in the html read
status, body = c.get(channel, brand, "/web/extract", {"url": url, "format": "html", "js": "false"}, 3)
html = body if isinstance(body, str) else ""
title = re.search(r"(?is)<title[^>]*>(.*?)</title>", html)
out["html"] = {"status": status, "chars": len(html), "title": re.sub(r"\s+", " ", title.group(1)).strip() if title else "",
"visible": visible_text(html)[:50000]}
if len(text.strip()) < THIN:
out["direct"] = direct_check(url)
return out
The folder has holiday_promo_watch.py, an example brands.json and the sample run in output/.
Endpoints
GET /web/extractGET /webGET /twitter/user/postsGET /desearch/instagram/profile/{username}/postsGET /desearch/tiktok/profile/{username}/posts
Example output
From a full snapshot on 2026-10-11: 35 calls covering the sample brands' pages, social posts and Black Friday searches. Each run adds rows to promo_timeline.csv (date, brand, channel, promo terms found), so a daily run shows the day each brand started using Black Friday wording on each channel.
Run it yourself
The public examples folder linked above includes the script and a README. Install its requirements, set DESEARCH_API_KEY from console.desearch.ai, and use the run command in that README.
Useful flags: --since and --until (season window), --count (posts per Instagram and TikTok profile), --no-x-search, --from-raw FILE (offline, no cost).
What it costs
A full daily snapshot of the sample brands was 35 calls and $0.236 on 2026-10-11, summed from the X-Desearch-Cost-Usd headers. A pages-and-search only run was 32 calls and $0.018. See pricing.
Ideas to extend it
- Schedule it daily from October to the end of November.
- Chart the timeline per brand and channel.
- Add Cyber Monday and other season terms.