Quickstart: your first video in 5 minutes
Three calls — and you're holding a finished vertical video. All you need is an API key and five minutes.
Base URL for every call:
https://api.piratepress.fun/public/v1
Interactive reference (OpenAPI, clickable right in the browser): https://api.piratepress.fun/public/v1/docs (Scalar).
Step 1. Get a key
Keys are issued by the bot: @PiratePressBot
→ /apikey. The key looks like pp_… and is shown once — save it
immediately. One active key per user: reissuing revokes the previous one
automatically.
Every request carries the header:
X-API-Key: pp_...
Without a key — or with a revoked one — every call answers 401.
Step 2. Order a video in one line
POST /videos:quick — a single-line master prompt; the LLM maps it onto
order parameters itself (like the /go command in the bot):
- curl
- Python
- JavaScript
curl -X POST https://api.piratepress.fun/public/v1/videos:quick \
-H "X-API-Key: pp_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: my-first-video-0001" \
-d '{"prompt": "a 30-second cartoon about a coworker who ate my lunch, with light music"}'
import requests
resp = requests.post(
"https://api.piratepress.fun/public/v1/videos:quick",
headers={
"X-API-Key": "pp_...",
"Idempotency-Key": "my-first-video-0001",
},
json={
"prompt": "a 30-second cartoon about a coworker who ate my "
"lunch, with light music"
},
)
resp.raise_for_status()
job = resp.json() # {id, status, cost, eta_seconds}
print(job["id"], job["status"], job["cost"], job["eta_seconds"])
const resp = await fetch(
"https://api.piratepress.fun/public/v1/videos:quick",
{
method: "POST",
headers: {
"X-API-Key": "pp_...",
"Content-Type": "application/json",
"Idempotency-Key": "my-first-video-0001",
},
body: JSON.stringify({
prompt:
"a 30-second cartoon about a coworker who ate my lunch, with light music",
}),
},
);
if (!resp.ok) throw new Error(`API ${resp.status}: ${await resp.text()}`);
const job = await resp.json(); // {id, status, cost, eta_seconds}
console.log(job.id, job.status, job.cost, job.eta_seconds);
Response 201:
{
"id": "gen_a1b2c3",
"status": "queued",
"cost": 100,
"eta_seconds": 420
}
Doubloons are charged synchronously at creation (cost is what was
charged). The Idempotency-Key header (kept for 24 hours) protects you
from double charging on retries: a repeat with the same key returns the
original task with 200 instead of 201, and no money moves twice.
Want manual control — placement, CTA, captions, music, voice clone,
script review? Use POST /videos with explicit parameters; the full
field list is in the reference.
Step 3. Grab the result
Poll the status via GET /videos/{id}:
- curl
- Python
- JavaScript
curl https://api.piratepress.fun/public/v1/videos/gen_a1b2c3 \
-H "X-API-Key: pp_..."
import requests
resp = requests.get(
"https://api.piratepress.fun/public/v1/videos/gen_a1b2c3",
headers={"X-API-Key": "pp_..."},
)
resp.raise_for_status()
video = resp.json() # {status, result_url, metadata, ...}
print(video["status"], video.get("result_url"))
const resp = await fetch(
"https://api.piratepress.fun/public/v1/videos/gen_a1b2c3",
{ headers: { "X-API-Key": "pp_..." } },
);
if (!resp.ok) throw new Error(`API ${resp.status}: ${await resp.text()}`);
const video = await resp.json(); // {status, result_url, metadata, ...}
console.log(video.status, video.result_url);
Statuses: queued → running → done (or error; director mode adds
awaiting_review — see the FAQ). Usually 2–15 minutes; use
eta_seconds from the creation response as your guide.
Once you see status: "done", the response gains result_url — a signed
mp4 link (valid for 7 days, no key needed to download) — and metadata
with the posting pack (title, description, hashtags):
curl -L -o out.mp4 "<result_url>"
Rather not poll? Subscribe to events instead: Webhooks.
For AI agents (MCP + skill)
If an AI agent (Claude Code, Kimi Code, Claude Desktop) will be ordering videos, install the MCP server and skill with one line:
curl -fsSL https://piratepress.fun/install.sh | PIRATEPRESS_API_KEY=pp_... bash
The installer puts the MCP server into ~/.piratepress/mcp/, the skill
into ~/.agents/skills/ (and ~/.claude/skills/ when present), and
registers the server with Claude Code. Nothing outside $HOME is touched.
Where next
- Error codes — the
{error: {code, message}}envelope and what to do about each code. - Webhooks —
video.donedelivered to your URL, signed. - FAQ — doubloons, limits, trial, director mode.