TikTok & YouTube Shorts Script Generator avatar

TikTok & YouTube Shorts Script Generator

Pricing

from $15.00 / 1,000 scripts

Go to Apify Store
TikTok & YouTube Shorts Script Generator

TikTok & YouTube Shorts Script Generator

Transform RSS feeds and raw notes into publish-ready TikTok and Shorts scripts. Includes optional ElevenLabs voiceovers and a built-in AI worker so you can start generating immediately or connect your own model.

Pricing

from $15.00 / 1,000 scripts

Rating

0.0

(0)

Developer

OptiRefine

OptiRefine

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Share

TikTok & YouTube Shorts Script Generator transforms trending RSS articles, raw statistics, or your own custom notes into structured, high-retention short-form video scripts. It automatically generates a captivating hook, engaging body beats, and a strong call-to-action (CTA), each paired with visual cues and exact timestamps.

No API key, no account, no setup. Script generation is built in and runs on an embedded Llama 3.1 8B model. Pick a category, press Start, and get scripts ready for production.

✨ Key Features & Benefits

  • Ready-to-Record: Outputs segmented scripts optimized for the fast pacing of TikTok, YouTube Shorts, and Instagram Reels.
  • 🎙️ Optional AI Audio: Paste your ElevenLabs API key to automatically generate perfectly timed .mp3 voiceovers.
  • 🧠 Free AI Inference: Runs entirely on Llama 3.1 8B. You do not pay any extra API costs for AI generation.
  • 🗂️ Multi-Niche Personas: Tailored prompt structures for Tech, Gaming, News, Beauty, Sports, and more.
  • 🛡️ Risk-Free Runs: Failed generations (unparseable AI text) are not billed. You only pay for successful, usable data.

🎯 Use Cases

  • Faceless Channels: Connect an RSS feed and let the Actor write your daily video content automatically.
  • Sports & Analytics Creators: Instantly convert raw match data into punchy player breakdowns and tactical analysis scripts.
  • Marketing Agencies: Generate bulk scripts across different client niches in a single automated run.

🚀 How to Run (Input)

Pick a mode. That is the whole configuration.

Curated RSS — choose categories and the Actor pulls recent articles:

{
"mode": "rss",
"categories": ["tech"],
"maxItems": 10
}

Your own input — supply the material directly:

{
"mode": "direct",
"items": [
{
"category": "tech",
"topic": "A hidden phone setting",
"data": "Most phones ship with a battery saver that is off by default."
}
]
}

Optional: add voiceover

Set elevenLabsApiKey and every segment is also rendered to audio, with its true measured duration. Leave it empty and you get scripts only — same Actor, same run, no extra step.

📦 Output Structure

Every item pushed to the default dataset looks like this (RSS source, voiceover enabled — the richest shape):

{
"category": "tech",
"topic": "A hidden phone setting",
"sourceType": "rss",
"sourceUrl": "[https://example.com/article](https://example.com/article)",
"duration": "40 seconds",
"platform": "TikTok/YouTube Shorts",
"segments": [
{
"label": "HOOK",
"start": "0:00",
"end": "0:05",
"speech": "This setting is off by default on every phone.",
"visual": "Close-up of settings screen",
"audioUrl": "[https://api.apify.com/v2/key-value-stores/](https://api.apify.com/v2/key-value-stores/)<store-id>/records/0000-00-hook.mp3",
"actualStart": 0.0,
"actualEnd": 4.2,
"durationSec": 4.2
}
],
"hook": "This setting is off by default on every phone.",
"wordCount": 96,
"rawScript": " HOOK: This setting is off by default on every phone. | VISUAL: Close-up of settings screen",
"status": "ok",
"generatedAt": "2026-08-15T12:00:00Z",
"audioStatus": "ok",
"totalDurationSec": 38.7
}

A few fields are conditional, not always present:

  • audioStatus and totalDurationSec appear only when elevenLabsApiKey was set for the run. Scripts-only runs omit both.
  • audioUrl, actualStart, actualEnd, and durationSec on each segment appear only when that segment's audio was rendered — the audioUrl value is whatever public URL the Apify key-value store returns for that key, shown above as an example, not a fixed format.
  • sourceUrl is null in direct mode; sourceType is "direct" or "rss" accordingly.
  • visual is always present but can be an empty string "" if the model's line had no | VISUAL: ... cue.

start/end are the model's plan. actualStart/actualEnd (when present) are measured from the rendered audio — cut your visuals to those. The two will differ from your requested duration, and that is expected.

Items the model produced that could not be parsed go to a separate failed-generations dataset instead, shaped as:

{
"category": "tech",
"topic": "...",
"sourceType": "rss",
"sourceUrl": "https://...",
"status": "unparseable",
"reason": "Model output could not be parsed after 2 attempts.",
"rawScript": "...",
"generatedAt": "2026-08-15T12:00:00Z"
}

🗂️ Categories

tech · trends · beauty · fashion · sports · gaming · news · learning · kid friendly · music · general

Each carries its own persona, hook strategy, and structure. Override the built-in feeds per category with feedOverrides.

maxItems and multiple categories. In rss mode, maxItems is the budget for the whole run, not per category: it is divided evenly across the selected categories while collecting, and the collected items are then interleaved category by category (tech, gaming, tech, gaming, ...) before any truncation to maxItems. Interleaving means an earlier category cannot fill the whole budget and starve a later one — each gets a share, and the results alternate between them.

One limit worth knowing: if maxItems is lower than the number of categories you select, each share floors at one item and only the first maxItems categories in your list are represented. At the default maxItems: 10, selecting all 11 categories leaves the last one out. Set maxItems to at least the number of categories you select if you want every one to appear.

A run can also return fewer items than maxItems for an unrelated reason: an RSS entry only gets read as an article if its feed summary is at least 500 characters, or a fetched page extracts to at least 200 characters of usable body text — thin or paywalled pages are skipped rather than sent to the model with nothing to say.

📝 Notes

  • Failed generations are not billed. If the model returns something unparseable after retries, that item goes to the failed-generations dataset with its raw output — inspectable, not charged.
  • maxItems is capped at 50 per run. Generation draws on a shared daily allowance, and the cap stops one run from consuming it. Start another run if you need more.
  • ElevenLabs characters bill to your account, not through Apify. The run log reports an estimate up front and the total at the end.
  • ttsConcurrency defaults to 2 because ElevenLabs' free tier allows as few as two simultaneous requests. Raise it only if your plan allows.

🛠️ Advanced: Running against your own generator & Development

Generation is built in, so this is not part of normal setup and the Actor's form has no fields for it. If you want generation on hardware you control, or want to change the prompts, run your own copy: deploy the worker in worker/ to your Cloudflare account and set WORKER_URL and WORKER_SECRET on your own build, as under "Operator setup" below.

The Actor still honours workerUrl and workerSecret passed through the API, but only as a pair. A URL with a blank secret is rejected rather than quietly falling back: the built-in secret is never sent to a worker that was named in the input, only to the one the Actor ships with.

Development Checks

pip install -r requirements-dev.txt
python -m pytest
cd worker && npm install && npm test

149 Python tests, 39 worker tests, no network calls in either suite. Before releasing, run the live smoke test — the only check that talks to a real worker and real Workers AI.

Operator Setup

The built-in generator is configured through two Actor environment variables, WORKER_URL and WORKER_SECRET, declared in .actor/actor.json. Both are stored by reference, so neither the worker's address nor its secret appears in any committed file or in the source uploaded to the platform:

apify secrets add sfsgWorkerUrl <the worker's URL>
apify secrets add sfsgWorkerSecret <the ACTOR_SECRET set on the worker>
apify push

The values live in ~/.apify/secrets.json on the operator's machine and are encrypted on upload. actor.json carries only @sfsgWorkerUrl and @sfsgWorkerSecret.

WORKER_SECRET must equal the ACTOR_SECRET set on the Cloudflare Worker. A mismatch is not a startup error — every item fails with a 401 instead. The secret therefore lives in two places that have to stay in step: Cloudflare (wrangler secret put ACTOR_SECRET) and the local Apify secret store (apify secrets add). Rotating one without the other gives you a green build and a run where every single item fails.

Note that apify push, apify call, and apify builds ls all hang after finishing their work on apify-cli 1.7.1 — the operation succeeds server-side and the command never exits. --json helps call reliably and the others only sometimes (push --json and push -w 300 both still hung; builds ls --json worked once and returned nothing the next time). Do not read a timeout as a failure, and verify functionally rather than by waiting: call the Actor and check the result, which is what actually proves which build is live.