Slice for developers
The Slice API
Send a long video or audio episode, such as a podcast, an interview or a sermon, and get back captioned vertical clips. Or send a recording and get a transcript. It is the same work as the Slice website and the Slice MCP server, on the same account.
Base URL https://tryslicemedia.com/api/v1. JSON in, JSON out. The machine-readable description is at /openapi.json (OpenAPI 3.1). For Claude, ChatGPT or Cursor, connect the MCP server instead: https://mcp.tryslicemedia.com/mcp.
Quickstart
Make a key in your account settings (a test key works straight away and costs nothing), then:
1. Start an episode from a link to the video or audio file
curl -s -X POST https://tryslicemedia.com/api/v1/episodes \
-H "Authorization: Bearer $SLICE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"source_url": "https://example.com/episode-41.mp4", "name": "Episode 41"}'2. When it is ready (the episode.ready webhook, or poll every 10 seconds or more), read the clips it found
curl -s https://tryslicemedia.com/api/v1/episodes/EPISODE_ID \ -H "Authorization: Bearer $SLICE_API_KEY"
3. Make clips 1 and 3, with big captions near the bottom
curl -s -X POST https://tryslicemedia.com/api/v1/episodes/EPISODE_ID/clips \
-H "Authorization: Bearer $SLICE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"clips": [1, 3], "caption_size": "large", "caption_position": "lower"}'Each clip comes back as a job. When it is done, GET /jobs/JOB_ID (or the clip.ready webhook) gives a download_url:
{
"job_id": "00000000-0000-4000-8000-0000000b0001",
"kind": "cut",
"status": "completed",
"clip_id": "00000000-0000-4000-8000-0000000c0001",
"title": "Why we stopped giving free trials",
"download_url": "https://tryslicemedia.com/shorts/guides/lgc430-audio-cover-example-2026-10.mp4",
"livemode": false
}Uploading the file yourself
# No public link? Ask for an upload address instead:
curl -s -X POST https://tryslicemedia.com/api/v1/episodes \
-H "Authorization: Bearer $SLICE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"file_name": "episode-41.mp4"}'
# -> { "episode_id": "...", "upload": { "method": "PUT", "url": "...", "headers": { ... } } }
curl -X PUT "UPLOAD_URL" -H "content-type: video/mp4" -H "x-upsert: true" --data-binary @episode-41.mp4
curl -s -X POST https://tryslicemedia.com/api/v1/episodes/EPISODE_ID/uploaded -H "Authorization: Bearer $SLICE_API_KEY"The upload address and the upload page both last two hours. Episodes can be up to 3 hours and 4.5GB. Video (MP4, MOV, M4V, WebM, MKV, AVI) or audio (MP3, M4A, AAC, WAV, OGG, Opus, FLAC): an audio episode, such as an RSS enclosure, gets clips drawn with your audio template, the same as on the website.
Authentication
Send your key as a bearer token: Authorization: Bearer slk_live_…. Keys belong to one account and act as that account.
A key is shown once when you make it. Slice keeps only a fingerprint of it (sha256), so a lost key cannot be shown again: revoke it and make another. Revoking works at once. A key can stop working on its own after 30 or 90 days if you choose. Up to 5 keys an account.
Each key has permissions (scopes), chosen when you make it:
clips.read Read clipsEpisodes, found clips, jobs and captions.clips.write Make clipsStart episodes, cut, caption and restyle.transcripts TranscriptsUses your free 3 a month, then Transcribe hours.webhooks WebhooksAdd and remove webhook addresses.The free transcription keys (slk_free_, from /tools/audio-to-text/api) keep working with /api/v1/transcribe exactly as before. They do not work here.
Test mode
A slk_test_ key answers every call with the same sample episode, clips, jobs and transcript, and never uploads, renders or charges anything. Every answer carries "livemode": false. Use it to build an n8n flow, a Zap or a script before spending anything. The sample episode is 00000000-0000-4000-8000-00000000e001:
{
"episode_id": "00000000-0000-4000-8000-00000000e001",
"name": "Sample episode: pricing a podcast business",
"status": "ready",
"duration": "42:10",
"clips": [
{
"number": 1,
"ref": "k3p9",
"title": "Why we stopped giving free trials",
"hook": "Free trials taught people to wait.",
"start": "04:12",
"end": "05:01",
"length": "0:49",
"pieces": 1,
"cut_status": "not_cut",
"export_status": "none"
}
],
"livemode": false
}Endpoints
They match the MCP tools one to one, and run the same code. Paths are relative to https://tryslicemedia.com/api/v1.
Retries and Idempotency-Key
Send an Idempotency-Key header (any unique string, such as a UUID) on every POST and PATCH. A retry with the same key and the same body within 24 hours gets the first answer again, so a timeout can never start two episodes or pay for two renders. The same key with a different body is refused (422).
Polling
There is no long wait on any call. Use webhooks, or poll GET /episodes/{id} and GET /jobs/{id} every 10 seconds or more. A 20 minute episode is usually ready in a few minutes; a render takes a minute or two.
Webhooks
Add an address in your account settings or with POST /webhooks, and Slice sends a POST when something on your account finishes, whether it started through the API, the website or a chat. Up to 5 addresses, https only; private network addresses are refused.
episode.readyThe clip search finished. data is the same as GET /episodes/{id}, clips included. Also sent after each extra search.episode.failedThe episode could not be searched, with the reason.clip.readyA render finished. data is the same as GET /jobs/{id} plus episode_id, with download_url (good for 7 days).clip.failedA render failed.transcript.completedA transcript finished, with links to the results page and the TXT, SRT and VTT files.transcript.failedA transcript failed, with the reason.Signatures
Every delivery is signed with the Standard Webhooks scheme, so any Standard Webhooks library can check it. The headers are webhook-id (the same on every retry, so you can drop repeats), webhook-timestamp and webhook-signature:v1, then base64 of HMAC-SHA256 over id.timestamp.body, keyed with the base64 part of your whsec_ secret. Check the raw body, before parsing it, and refuse anything older than 5 minutes.
POST https://your-app.example.com/hooks/slice
webhook-id: msg_6f1c2a0e9b8d4c3a8e7f1a2b3c4d5e6f
webhook-timestamp: 1792252800
webhook-signature: v1,<base64 HMAC-SHA256, see below>
{
"type": "clip.ready",
"timestamp": "2026-10-17T14:00:00.000Z",
"livemode": true,
"data": {
"episode_id": "...",
"job_id": "...",
"kind": "render",
"status": "completed",
"clip_id": "...",
"title": "Why we stopped giving free trials",
"download_url": "https://tryslicemedia.com/d/...",
"message": "Ready: Why we stopped giving free trials. Download: ..."
}
}Checking a signature in Node
import crypto from 'node:crypto';
// secret is the whsec_... shown when you added the webhook.
export function verifySlice(secret, headers, rawBody) {
const id = headers['webhook-id'];
const ts = Number(headers['webhook-timestamp']);
if (!id || Math.abs(Date.now() / 1000 - ts) > 300) return false; // older than 5 minutes
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const expected = 'v1,' + crypto.createHmac('sha256', key).update(`${id}.${ts}.${rawBody}`).digest('base64');
return String(headers['webhook-signature'] || '').split(' ').some((sig) =>
sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)));
}Checking a signature in Python
import base64, hashlib, hmac, time
def verify_slice(secret: str, headers: dict, raw_body: str) -> bool:
msg_id = headers.get("webhook-id")
ts = headers.get("webhook-timestamp", "0")
if not msg_id or abs(time.time() - int(ts)) > 300: # older than 5 minutes
return False
key = base64.b64decode(secret.removeprefix("whsec_"))
mac = hmac.new(key, f"{msg_id}.{ts}.{raw_body}".encode(), hashlib.sha256).digest()
expected = "v1," + base64.b64encode(mac).decode()
return any(hmac.compare_digest(expected, s) for s in headers.get("webhook-signature", "").split(" "))Delivery and retries
Answer with any 2xx within 10 seconds. Redirects are not followed. A delivery that fails is tried again after 1, 5, 30, 60 minutes and then over about 25 hours in all, then given up. An address that has failed for 3 days in a row is switched off. Your account settings show the last 10 deliveries for each address, and a Send test event button. Deliveries can arrive more than once and out of order: use webhook-id and the ids in data.
Limits
Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. A 429 also says retry_after in seconds.
Errors
Every error has the same shape, and says the limit, what was measured and when it resets wherever that applies:
{
"error": {
"code": "credit_required",
"message": "Exports after the first 5 on an episode cost $0.25 each, and there is not enough credit. Nothing was made or charged.",
"needed_cents": 25,
"credit_cents": 10
}
}Pricing
Pay as you go with credit, no plan needed: buy a pack, from $10, in your account settings. The Slice plan ($14.99 a month) includes 5 API episodes a month, used before any credit. What it costs:
An episode is charged once, when its clip search starts, by the length Slice measured. A call that is refused, or a clip that could not be started, is not charged. Real episodes need credit or the Slice plan; with neither, the API offers test mode and your 3 free transcripts a month. The website's free first video and 3 clips do not apply to the API. A refunded pack takes its credit back; if some of it was already spent the balance goes below zero, and live calls stop until you add credit (reads and test keys still work). Episodes and clips you make on the website or in a chat are not counted here. Every answer that spends credit says how much in charged_cents, and GET /me shows what is left.
Changelog
17 October 2026. Credit works without the plan: pay as you go from $10. New 402 codes plan_or_credit_required and balance_below_zero.
17 October 2026. First version: account keys with test mode, episodes, clips, jobs, captions, styles, transcripts, signed webhooks, Idempotency-Key, rate limits and the OpenAPI file.
Rather click than code?
Shorts by Slice does all of this in the browser: upload an episode and pick the clips.
Try Shorts by Slice