Cutting long videos into clips
A long video (stream, podcast, webinar) → ready vertical clips with
captions and a posting pack. The same pipeline as the bot and the web
studio: the price is settled on actual duration, and on failure the
estimate is fully refunded. All calls carry X-API-Key: pp_..., except
file downloads (the signed link itself is the credential).
Base URL: https://api.piratepress.fun/public/v1.
Step 1. Probe a URL — POST /clips/probe
curl -X POST https://api.piratepress.fun/public/v1/clips/probe \
-H "X-API-Key: pp_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://youtube.com/watch?v=..."}'
{ "duration_s": 3720.0, "title": "AI podcast", "estimated_cost": 240 }
This is an estimate: the order charges it up front, and after cutting the
actual cost is settled (the difference is refunded or charged). A broken,
private, or 18+ URL returns 422 invalid_params, not 500.
Step 2. Order the cut — POST /clips
The Idempotency-Key header is mandatory (without it 400, someone
else's key — 409 idempotency_conflict). Repeating with your own key
returns the original order with no second charge (200 instead of 201).
curl -X POST https://api.piratepress.fun/public/v1/clips \
-H "X-API-Key: pp_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: clips-order-0001" \
-d '{
"source_url": "https://youtube.com/watch?v=...",
"estimated_seconds": 3720,
"lang": "en",
"top_n": 5,
"captions": true,
"split_layout": 50,
"split_bottom_preset": "lofi_game",
"banner_asset_id": "ast_...",
"banner_rules": "show twice in a row from the middle, with pause"
}'
Body (PublicClipOrderCreate):
| Field | Type | Default | Meaning |
|---|---|---|---|
source_url | string | — | Video URL (http/https, yt-dlp). Exactly one of source_url/source_asset_id is required |
source_asset_id | string | — | Source from the media library (id from GET /assets) |
estimated_seconds | number | 0 | Duration hint for the price preview (see probe) |
lang | string | "en" | Language of captions and metadata |
top_n | int 1–50 | all | How many top clips to return |
captions | bool | true | Embedded captions |
split_layout | 0/33/50/60 | 0 | Split view: % of the top slot (content + bottom filler) |
split_bottom_preset | string | — | Preset for the split bottom (id from the generator catalog) |
smart_crop | bool | false | Smart 3-slot crop for streams; requires split_layout: 33 |
banner_asset_id | string | — | Ready offer banner (library asset) overlaid on the clips |
banner_rules | string | — | Show conditions as text: timings, loops, schedule |
uniquify | off/soft/strong/custom | "off" | Clip uniquifying (anti-shadowban): invisible frame/audio tweaks against platform dedupe. +30 🪙 for any mode ≠ off |
uniquify_opts | object | — | Required with custom: toggles mirror/speed/pitch/zoom/hue/grain (bool) |
variants | int 1–5 | 1 | Versions of each clip (clip_01.mp4, clip_01_v2.mp4…); +10 🪙 per extra version |
resolution | string | "720x1280" | Clip vertical resolution: 720x1280 or 1080x1920 |
caption_font | string | — | Caption font: pool name (montserrat/inter/bebas/oswald/rubik/manrope/golos) or asset:<id> of an own font (font asset) |
caption_color | string | — | Main caption color, hex #RRGGBB |
caption_hot_color | string | — | Current-word color, hex |
caption_highlight_color | string | — | Karaoke highlight color, hex |
caption_position | string | — | top/center/bottom |
If no caption fields are sent, the account caption style applies (the user's font/palette; new accounts get a random one, so clips from different users don't look stamped by one pipeline).
Response 201:
{ "id": "ord_a1b2c3", "status": "queued", "cost": 240, "eta_seconds": 900 }
Clip orders share the same quota as videos: 10 active tasks per key combined (see Error codes).
Step 3. Pick up the result — GET /clips/{id}
curl https://api.piratepress.fun/public/v1/clips/ord_a1b2c3 \
-H "X-API-Key: pp_..."
While running, the response carries stage/stage_detail ("Cutting
clips", "3/12"). When status: "done":
{
"id": "ord_a1b2c3",
"status": "done",
"title": "AI podcast",
"clips": [
{
"name": "clip_01.mp4",
"title": "Why AI won't replace editors",
"url": "https://api.piratepress.fun/public/v1/clips/files/eyJ...",
"description": "…",
"hashtags": ["ai", "podcast"]
}
],
"zip_url": "https://api.piratepress.fun/public/v1/clips/files/eyJ...",
"estimated_cost": 240,
"actual_cost": 236,
"created_at": "2026-10-19T12:00:00Z",
"finished_at": "2026-10-19T12:20:00Z"
}
Statuses: queued|running|done|error|refunded. On error/refunded
the estimate is back on your balance. The url/zip_url links are
signed (HMAC, 7-day TTL) and need no key to download:
curl -L -o clips.zip "<zip_url>"
Rather not poll? Subscribe to clip.done/clip.error:
Webhooks.
List clip orders — GET /clips
curl "https://api.piratepress.fun/public/v1/clips?limit=20&offset=0" \
-H "X-API-Key: pp_..."
Newest first; each row: id, status, title, source_url, is_file, clips_count, cost (actual after done, estimate before), created_at, error.
AI banner for clips
POST /clips/banner generates a banner from a description (priced like
the AI banner in video generation: trial/adveristor quota/doubloons):
curl -X POST https://api.piratepress.fun/public/v1/clips/banner \
-H "X-API-Key: pp_..." \
-H "Content-Type: application/json" \
-d '{"prompt": "headphone store banner, neon, dark background", "lang": "en"}'
# → { "job_id": "ban_...", "cost": 50 }
GET /clips/banner/{job_id} polls the job: on done the response has an
asset_id (the asset lands in your media library — pass its id as
banner_asset_id of the clip order); on error the doubloons are
refunded automatically. The body may optionally carry source_asset_id
— a source pack from the media library for the generator's VIP mode.
Where next
- Quickstart — the general order/status flow;
- Webhooks —
clip.done/clip.errorinstead of polling; - Error codes — the
{error: {code, message}}envelope.