Most "prompt of the day" bots are a random line of text in a Slack channel. Nobody reads them, because a prompt with no output next to it is just a sentence.
I wanted the opposite: one prompt a day, with the clip that prompt actually produced, and no credentials to set up. It turned into a small n8n workflow and a much more interesting question — what does a prompt that actually works look like, statistically?
Six nodes, no API key, no account:
sitemap.xml. day % total. h1, the pre blocks, and the JSON-LD script tags.VideoObject schema, assemble a Slack-flavoured message.
The source is a public library of Seedance 2 video prompts — 150 of them, each with the clip it generated.
Rotate, don't randomize. Math.floor(Date.now() / 86400000) % urls.length gives you one prompt per day that is stable across re-runs. Math.random() gives you duplicates within a week and a different result every time you debug.
const urls = [...xml.matchAll(/<loc>([^<]+)<\/loc>/g)]
.map((m) => m[1].trim())
.filter((u) => u.includes('/prompt/'))
.filter((u) => !u.includes('/prompt/category/'));
const dayIndex = Math.floor(Date.now() / 86400000);
const url = urls[dayIndex % urls.length];
Read the schema, not the layout. Every prompt page ships a VideoObject in JSON-LD with name, description and contentUrl. That is a stable contract; the rendered markup is not. Scraping the schema means a CSS refactor on their side does not silently break my workflow.
for (const raw of item.structuredData || []) {
const parsed = JSON.parse(raw);
const blocks = Array.isArray(parsed) ? parsed : [parsed];
const found = blocks.find((b) => b['@type'] === 'VideoObject');
if (found) { video = found; break; }
}
Count your selectors before you trust index 0. Each page has two <pre> blocks: the Japanese original and the English version. cssSelector: "pre" with returnArray: false silently gives you the first one — so my first run posted Japanese into an English channel and looked completely fine. Set returnArray: true and choose explicitly.
Since I had all 150 in a list, I counted them. Median prompt length is 152 characters. Longest is 215.
That is roughly one sentence each for style, place, subject and motion. Every "advanced prompting guide" I have read implies the opposite — that control comes from length. In this corpus, it doesn't exist. Long prompts tend to contain contradicting instructions, and the model drops one of them, non-deterministically.
Element frequency across the 150:
| Element | Appears in |
|---|---|
| Camera / lens / framing | 69 / 150 (46%) |
| Duration stated in seconds | 63 / 150 (42%) |
| Lighting | 55 / 150 (37%) |
| Cut structure | 22 / 150 (15%) |
| Audio (ambience, SFX, dialogue, ASMR) | 22 / 150 (15%) |
| Resolution (4K / 8K) | 14 / 150 (9%) |
| Slow motion | 4 / 150 (3%) |
Two readings I'd stand behind:
A third of the corpus (34/150) splits the prompt by time range rather than describing one continuous action:
[00:00-00:05] intro, camera enters from behind the subject
[00:05-00:10] turn, light source switches
[00:10-00:15] wide, full scene
That form clusters hard: sci-fi (9), cinematic (7), people (5) — and it is almost absent from scenery (2) and food (0). It shows up exactly where order carries meaning. For a single continuous shot, stating total duration is enough.
And 32 of 150 need a reference image rather than text alone. The tell is simple: the moment a prompt points at a specific person or a specific frame instead of a kind of thing, text can no longer specify it. That is the line between text-to-video and image-to-video.
/slack to the webhook URL — it then accepts the same { "text": ... } payload.
That's the whole thing. Everything it reads is public, so there is no credential step and nothing to rotate.
The prompts themselves are written against Seedance 2.5; if you have not run one before, the walkthrough is shorter than this post.
I wrote the full breakdown of all 150 — element frequency, duration patterns, the reference-image split by category — as a standalone reference (Japanese). There is also a Japanese write-up of the implementation on Qiita if that is your reading language.
If you build something similar against another corpus, the transferable part is not the scraping — it is checking whether the thing you are scraping publishes structured data first. It usually does, and it is usually more stable than the HTML around it.