Skip to main content

Loading…

Clipping API

Upload video or audio, and cut out the parts you want. Try it below.

Drop a video or audio file here

MP4, MOV, AVI, MKV, WEBM, M4V, FLV, MP3, WAV, AAC, FLAC, OGG, M4A, WMA

Browse Files

Drag & drop a video or audio file, or browse

Common video or audio formats — up to 5 GB

Loading video info…

Clip Studio

Loading waveform…

: :
: :

Duration:

Selection:

GIF limited to 30s (current: )

GIF limited to 30s (current: )

Captions require MP4 output. Switch format to MP4 to enable caption rendering.

Loading styles...

Get the full transcript with timestamps.

Cost

A job is still processing. Switch back to check progress.

Process Render Deliver Done

Get $1 free to test when you sign up

Dead simple to integrate

Works with any programming language. Here are a few examples.

# Upload and trim a video
import os, requests, time

headers = {"Authorization": "Bearer sk_live_..."}

# 1. Request an upload slot
upload = requests.post(
    "https://api.fastclip.dev/v1/upload/request",
    headers=headers,
    json={
        "filename": "video.mp4",
        "content_type": "video/mp4",
        "file_size": os.path.getsize("video.mp4")
    }
).json()

# 2. Upload the file
with open("video.mp4", "rb") as f:
    requests.put(upload["upload_url"], data=f, headers={"Content-Type": "video/mp4"})

# 3. Wait for validation
while True:
    s = requests.get(f"https://api.fastclip.dev/v1/upload/{upload['file_id']}", headers=headers).json()
    if s["status"] == "ready": break
    time.sleep(1)

# 4. Trim a segment
clip = requests.post("https://api.fastclip.dev/v1/clip",
    headers=headers,
    json={
        "file_id": upload["file_id"],
        "start": 65.2, "end": 128.7,
        "format": "mp4"
    }
).json()
print(clip["job_id"])
# Audio example: "podcast.m4a", format="m4a" (mp3, wav, flac also supported)
// Upload and trim a video
import { readFile, stat } from "node:fs/promises";

const headers = {
  "Authorization": "Bearer sk_live_...",
  "Content-Type": "application/json"
};
const fileBuffer = await readFile("video.mp4");
const fileStats = await stat("video.mp4");

// 1. Request an upload slot
const upload = await fetch(
  "https://api.fastclip.dev/v1/upload/request", {
    method: "POST", headers,
    body: JSON.stringify({
      filename: "video.mp4",
      content_type: "video/mp4",
      file_size: fileStats.size
    })
  }
).then(r => r.json());

// 2. Upload the file
await fetch(upload.upload_url, {
  method: "PUT",
  headers: { "Content-Type": "video/mp4" },
  body: fileBuffer
});

// 3. Wait for validation
while (true) {
  const s = await fetch(
    `https://api.fastclip.dev/v1/upload/${upload.file_id}`,
    { headers: { "Authorization": "Bearer sk_live_..." } }
  ).then(r => r.json());
  if (s.status === "ready") break;
  await new Promise(r => setTimeout(r, 1000));
}

// 4. Trim a segment
const clip = await fetch(
  "https://api.fastclip.dev/v1/clip", {
    method: "POST", headers,
    body: JSON.stringify({
      file_id: upload.file_id,
      start: 65.2, end: 128.7,
      format: "mp4"
    })
  }
).then(r => r.json());
console.log(clip.job_id);
// Audio example: "podcast.m4a", format: "m4a" (mp3, wav, flac also supported)
# Upload and trim a video

FILE_SIZE=$(wc -c < video.mp4 | tr -d ' ')

# 1. Request an upload slot
UPLOAD=$(curl -s -X POST https://api.fastclip.dev/v1/upload/request \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d "{\"filename\":\"video.mp4\",\"content_type\":\"video/mp4\",\"file_size\":$FILE_SIZE}")
FILE_ID=$(echo $UPLOAD | grep -o '"file_id":"[^"]*"' | cut -d'"' -f4)
UPLOAD_URL=$(echo $UPLOAD | grep -o '"upload_url":"[^"]*"' | cut -d'"' -f4)

# 2. Upload the file
curl -s -X PUT "$UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  --data-binary @video.mp4

# 3. Wait for validation
while true; do
  sleep 1
  STATUS=$(curl -s https://api.fastclip.dev/v1/upload/$FILE_ID \
    -H "Authorization: Bearer sk_live_...")
  echo $STATUS | grep -q '"ready"' && break
done

# 4. Trim a segment
curl -s -X POST https://api.fastclip.dev/v1/clip \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d "{\"file_id\": \"$FILE_ID\", \"start\": 65.2, \"end\": 128.7, \"format\": \"mp4\"}"
# Audio example: "podcast.m4a", format: "m4a" (mp3, wav, flac also supported)
// Upload and trim a video
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
    "time"
)

func main() {
    auth := "Bearer sk_live_..."
    info, _ := os.Stat("video.mp4")

    // 1. Request upload slot
    body, _ := json.Marshal(map[string]any{
        "filename": "video.mp4",
        "content_type": "video/mp4",
        "file_size": info.Size(),
    })
    req, _ := http.NewRequest("POST",
        "https://api.fastclip.dev/v1/upload/request",
        bytes.NewBuffer(body))
    req.Header.Set("Authorization", auth)
    req.Header.Set("Content-Type", "application/json")
    resp, _ := http.DefaultClient.Do(req)
    var upload map[string]any
    json.NewDecoder(resp.Body).Decode(&upload)
    resp.Body.Close()

    // 2. Upload the file
    f, _ := os.Open("video.mp4")
    req, _ = http.NewRequest("PUT", upload["upload_url"].(string), f)
    req.Header.Set("Content-Type", "video/mp4")
    resp, _ = http.DefaultClient.Do(req)
    resp.Body.Close()
    f.Close()

    // 3. Wait for validation
    fileID := upload["file_id"].(string)
    for {
        time.Sleep(time.Second)
        req, _ = http.NewRequest("GET",
            fmt.Sprintf("https://api.fastclip.dev/v1/upload/%s", fileID), nil)
        req.Header.Set("Authorization", auth)
        resp, _ = http.DefaultClient.Do(req)
        var s map[string]any
        json.NewDecoder(resp.Body).Decode(&s)
        resp.Body.Close()
        if s["status"] == "ready" { break }
    }

    // 4. Trim a segment
    body, _ = json.Marshal(map[string]any{
        "file_id": fileID,
        "start": 65.2, "end": 128.7,
        "format": "mp4",
    })
    req, _ = http.NewRequest("POST",
        "https://api.fastclip.dev/v1/clip", bytes.NewBuffer(body))
    req.Header.Set("Authorization", auth)
    req.Header.Set("Content-Type", "application/json")
    resp, _ = http.DefaultClient.Do(req)
    var clip map[string]any
    json.NewDecoder(resp.Body).Decode(&clip)
    fmt.Println(clip["job_id"])
    // Audio example: "podcast.m4a", format: "m4a" (mp3, wav, flac also supported)
}
# Upload and trim a video
require "net/http"
require "json"
require "uri"

http = Net::HTTP.new("api.fastclip.dev", 443)
http.use_ssl = true
auth = "Bearer sk_live_..."

# 1. Request an upload slot
req = Net::HTTP::Post.new("/v1/upload/request")
req["Authorization"] = auth
req["Content-Type"] = "application/json"
req.body = {
  filename: "video.mp4",
  content_type: "video/mp4",
  file_size: File.size("video.mp4")
}.to_json
upload = JSON.parse(http.request(req).body)

# 2. Upload the file
uri = URI(upload["upload_url"])
put = Net::HTTP::Put.new(uri)
put["Content-Type"] = "video/mp4"
put.body = File.binread("video.mp4")
Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(put) }

# 3. Wait for validation
loop do
  sleep 1
  poll = Net::HTTP::Get.new("/v1/upload/#{upload['file_id']}")
  poll["Authorization"] = auth
  s = JSON.parse(http.request(poll).body)
  break if s["status"] == "ready"
end

# 4. Trim a segment
req = Net::HTTP::Post.new("/v1/clip")
req["Authorization"] = auth
req["Content-Type"] = "application/json"
req.body = {
  file_id: upload["file_id"],
  start: 65.2, end: 128.7,
  format: "mp4"
}.to_json
clip = JSON.parse(http.request(req).body)
puts clip["job_id"]
# Audio example: "podcast.m4a", format: "m4a" (mp3, wav, flac also supported)
// Upload and trim a video
$auth = "Authorization: Bearer sk_live_...";
$json = "Content-Type: application/json";

// 1. Request an upload slot
$upload = json_decode(file_get_contents(
    "https://api.fastclip.dev/v1/upload/request",
    false,
    stream_context_create(["http" => [
        "method" => "POST",
        "header" => implode("\r\n", [$auth, $json]),
        "content" => json_encode([
            "filename" => "video.mp4",
            "content_type" => "video/mp4",
            "file_size" => filesize("video.mp4")
        ])
    ]])
), true);

// 2. Upload the file
file_get_contents($upload["upload_url"], false,
    stream_context_create(["http" => [
        "method" => "PUT",
        "header" => "Content-Type: video/mp4",
        "content" => file_get_contents("video.mp4")
    ]]));

// 3. Wait for validation
$ctx = stream_context_create(["http" => ["header" => $auth]]);
do {
    sleep(1);
    $s = json_decode(file_get_contents(
        "https://api.fastclip.dev/v1/upload/" . $upload["file_id"],
        false, $ctx
    ), true);
} while ($s["status"] !== "ready");

// 4. Trim a segment
$clip = json_decode(file_get_contents(
    "https://api.fastclip.dev/v1/clip",
    false,
    stream_context_create(["http" => [
        "method" => "POST",
        "header" => implode("\r\n", [$auth, $json]),
        "content" => json_encode([
            "file_id" => $upload["file_id"],
            "start" => 65.2,
            "end" => 128.7,
            "format" => "mp4"
        ])
    ]])
), true);
echo $clip["job_id"];
// Audio example: "podcast.m4a", format: "m4a" (mp3, wav, flac also supported)

API reload

Top up your balance programmatically with a saved card. Enable in Settings → API Reload. Direct reloads need an idempotency key; email-confirmed reloads return pending until approved.

# Check balance and reload
import requests, uuid

headers = {"Authorization": "Bearer sk_live_..."}

# 1. Check current balance
bal = requests.get("https://api.fastclip.dev/v1/balance", headers=headers).json()
print(f"Balance: ${bal['balance']:.2f}")

# 2. Reload $25 with saved card
reload_headers = {**headers, "Idempotency-Key": str(uuid.uuid4())}
reload = requests.post("https://api.fastclip.dev/v1/reload",
    headers=reload_headers,
    json={"amount": 2500}  # cents
).json()

if reload.get("pending"):
    print(reload["message"])
else:
    print(f"New balance: ${reload['balance']:.2f}")
# → New balance: $29.90, or approval email sent
// Check balance and reload
const headers = {
  "Authorization": "Bearer sk_live_...",
  "Content-Type": "application/json"
};

// 1. Check current balance
const bal = await fetch("https://api.fastclip.dev/v1/balance", { headers })
  .then(r => r.json());
console.log(`Balance: $${bal.balance.toFixed(2)}`);

// 2. Reload $25 with saved card
const reloadHeaders = { ...headers, "Idempotency-Key": crypto.randomUUID() };
const reload = await fetch("https://api.fastclip.dev/v1/reload", {
  method: "POST", headers: reloadHeaders,
  body: JSON.stringify({ amount: 2500 })  // cents
}).then(r => r.json());

console.log(reload.pending ? reload.message : `New balance: $${reload.balance.toFixed(2)}`);
// → New balance: $29.90, or approval email sent
# 1. Check current balance
curl https://api.fastclip.dev/v1/balance \
  -H "Authorization: Bearer sk_live_..."

# 2. Reload $25 with saved card
ATTEMPT_KEY=$(uuidgen)
curl -X POST https://api.fastclip.dev/v1/reload \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: $ATTEMPT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount": 2500}'

# → {"success":true,"balance":29.90,...} or {"pending":true,...}
// Check balance and reload
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "time"
)

func main() {
    auth := "Bearer sk_live_..."

    // 1. Check balance
    req, _ := http.NewRequest("GET", "https://api.fastclip.dev/v1/balance", nil)
    req.Header.Set("Authorization", auth)
    resp, _ := http.DefaultClient.Do(req)
    var bal map[string]any
    json.NewDecoder(resp.Body).Decode(&bal)
    resp.Body.Close()
    fmt.Printf("Balance: $%.2f\n", bal["balance"])

    // 2. Reload $25
    body, _ := json.Marshal(map[string]any{"amount": 2500})
    req, _ := http.NewRequest("POST",
        "https://api.fastclip.dev/v1/reload",
        bytes.NewBuffer(body))
    req.Header.Set("Authorization", auth)
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Idempotency-Key", fmt.Sprintf("reload-%d", time.Now().UnixNano()))

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    out, _ := io.ReadAll(resp.Body)
    fmt.Println(string(out))
    // → {"success":true,"balance":29.90,...} or {"pending":true,...}
}
# Check balance and reload
require "net/http"
require "json"

http = Net::HTTP.new("api.fastclip.dev", 443)
http.use_ssl = true
auth = "Bearer sk_live_..."

# 1. Check balance
req = Net::HTTP::Get.new("/v1/balance")
req["Authorization"] = auth
bal = JSON.parse(http.request(req).body)
puts "Balance: $#{'%.2f' % bal['balance']}"

# 2. Reload $25
req = Net::HTTP::Post.new("/v1/reload")
req["Authorization"] = auth
req["Content-Type"] = "application/json"
req["Idempotency-Key"] = "reload-#{Time.now.to_i}-#{rand(1_000_000)}"
req.body = { amount: 2500 }.to_json
reload = JSON.parse(http.request(req).body)

puts reload["pending"] ? reload["message"] : "New balance: $#{'%.2f' % reload['balance']}"
# → New balance: $29.90, or approval email sent
// Check balance and reload
$auth = "Authorization: Bearer sk_live_...";

// 1. Check balance
$bal = json_decode(file_get_contents(
    "https://api.fastclip.dev/v1/balance", false,
    stream_context_create(["http" => ["header" => $auth]])
), true);
echo "Balance: $" . number_format($bal["balance"], 2) . "\n";

// 2. Reload $25 with saved card
$attemptKey = "reload-" . bin2hex(random_bytes(8));
$r = json_decode(file_get_contents(
    "https://api.fastclip.dev/v1/reload", false,
    stream_context_create(["http" => [
        "method" => "POST",
        "header" => implode("\r\n", [
            $auth,
            "Idempotency-Key: " . $attemptKey,
            "Content-Type: application/json"
        ]),
        "content" => json_encode(["amount" => 2500])
    ]])
), true);

echo !empty($r["pending"]) ? $r["message"] : "New balance: $" . number_format($r["balance"], 2);
// → New balance: $29.90, or approval email sent

Prompt to clip

Let AI do the clipping

Send a plain-language prompt and FastClip returns the matching moments as rendered clips, or use analyze-only mode when you just need timestamps.

Describe the moment you want in natural language.

Transcript-backed matching keeps results explainable.

Render the clips now, or inspect timestamps first.

Auto-clip request

from $0.10
POST /v1/clip/auto { "file_id": "upl_abc123...", "query": "strong product launch soundbites", "format": "mp4", "captions": { "preset_ref": "builtin:short-form-karaoke" } }

Analyze only

from $0.08

Use /v1/clip/analyze to get matching segments with confidence scores before rendering.

{ "segments": [{ "start": 45, "end": 112, "confidence": 0.92 }] }

3-step workflow

How it works

Upload a file, let FastClip handle the processing, and get back a ready-to-use output link for your app or workflow.

Step 1
1

Upload your file

Upload a video or audio file, then POST the timestamps or AI prompt and the output format you want.

You send

file_id + start/end or prompt + format

Step 2
2

We process your upload

FastClip trims the target segment and encodes the final output on our servers.

We handle

Trim, encode, and delivery

Step 3
3

Get the finished asset

You get a job response with a download/share URL for rendered clips, or text and JSON for transcript-only jobs.

You receive

Job status + download URL

Works with most media formats

Video formats

MP4 MOV AVI MKV WebM M4V FLV

Audio formats

MP3 WAV AAC FLAC OGG M4A WMA

Add captions to videos in the same API call.

Pass a captions object when you trim or auto-clip, or call /v1/captions with an existing upload or clip. FastClip renders styled captions into a new MP4 with reusable presets and account fonts.

Custom font control

  • Upload TTF or OTF fonts
  • Use bundled Google-style fonts

Granular styling

  • Color, stroke, shadow, and case
  • Anchor, X/Y position, and sizing

16 pre-made styles

  • Karaoke, highlight box, ticker, glow
  • Duplicate and save custom presets

API-first workflow

  • Add preset_ref to trim or AI Auto
  • New MP4 output, source untouched
Read Caption Docs

We hate subscriptions. Pay per use.

Deposit funds ($5 minimum), process media, and only pay for what you use.

Bitcoin deposits accepted.

Trim

Set start & end, get your clip

$0.02 /segment
  • Video & audio formats
  • Set start & end time
  • MP4 video; MP3/WAV/M4A/FLAC audio
  • Up to 1080p for video
  • GIF free with video trim (site only)
  • Multi-segment batch trim
4K trim: $0.04/segment
Captions

Burn captions into MP4 clips

$0.02 /input min
  • Up to 1080p input video
  • MP4 video only
  • Pre-made + custom presets
  • Uploaded fonts supported
  • Input resolution auto-detected
  • Dedicated caption or trim + caption
4K captions: $0.04/input min
Popular
AI Auto-Clip

Describe it, AI finds and clips

$0.10 – $0.50
  • ≤30 min: $0.10
  • ≤60 min: $0.20
  • ≤120 min: $0.35
  • ≤240 min: $0.50
  • Video & audio formats
  • Multiple clips per job
  • No per-segment surcharge
AI Analyze

Find timestamps, no output clip

$0.08 – $0.40
  • ≤30 min: $0.08
  • ≤60 min: $0.15
  • ≤120 min: $0.25
  • ≤240 min: $0.40
  • Video & audio formats
  • Confidence scores
  • Transcript excerpts
Transcript

Full timestamped transcript

$0.02 – $0.40
  • Video & audio formats
  • Timestamped segments
  • Machine-readable JSON
  • Powered by Whisper
  • ≤10 min: $0.02
  • ≤30 min: $0.05
  • ≤60 min: $0.10; up to ≤240 min: $0.40

Caption input resolution is detected automatically. Use quality only when you want a lower-resolution MP4 back. Caption pricing is additive to trim pricing when you trim and caption in one job, and captioned outputs are new MP4 files.

Deposit bonuses

Top up once, get extra processing balance.

Standard top-up bonuses stack. A first $100 top-up credits $160 total. One-time special deals do not stack with these bonuses.

First Deposit Bonus

+50%

Your first successful paid deposit gets a 50% balance boost.

$50+ Top-Up Bonus

+10%

Every top-up of $50 or more receives an additional 10%.

Why Choose FastClip?

Platform Best For Commitment Public Pricing Manual AI Clips Transcripts Best Reason to Choose
FastClip logo FastClip
Low-cost clipping, captions, transcripts, and AI analysis. $1 signup credit and $5 minimum deposit.
Trim from $0.02/segment
Captions $0.02-$0.04/min
Transcripts $0.02-$0.40 tiered
AI clips from $0.10/file
Yes Yes Yes Lowest commitment for simple media processing.
Shotstack logo Shotstack
Automated video rendering workflows. Pay-as-you-go credits or monthly subscription. Public page lists $0.30/min pay-as-you-go and $0.20/min on subscription. Yes Depends Not core Better for full rendering pipelines.
Cloudinary logo Cloudinary
Media storage, optimization, and delivery. Free plan, then monthly tiers. Free; Plus $99/mo; Advanced $249/mo. Annual discounts shown publicly. Possible Not core Add-on Better for a full media library and CDN stack.
Mux logo Mux
Video hosting, streaming, and playback infrastructure. Free start, then usage-based or prepaid credits. Pricing is split across input, storage, and delivery, so total cost depends on usage. No No Not core Better for scalable playback infrastructure.
OpusClip logo OpusClip
Creator-focused short-form clips. Free plan/trial, then credit plans. Public pricing shows Free, Starter around $15/mo, and Pro around $29/mo. Editor Yes Yes Better for creators repurposing videos for social.
VEED logo VEED API
Broader AI video API suite. Usage-based API pricing with no minimum commitment. Endpoint-based pricing; subtitles are duration-based and Fabric starts at $0.08/sec for 480p. Yes Yes Yes Better for broader AI editing and generation endpoints.
0:03 / 0:20

Get rolled lol

Download
MP4 · 1080p · 9.5 MB · 0:20
Pinned: stored until unpinned or deleted

Every clip gets a share page.

Each clip you create gets its own shareable link with a built-in video player. Send it to anyone, no login required. Embed it on your site, share on social, or keep it for yourself.

View live example
  • Unique link per clip

    Find every clip under Account → History. Each one has a shareable link.

  • Built-in player

    Video, audio, and GIF clips play directly in the browser. No external player needed.

  • Pin to keep

    Clips expire after 24 hours by default. Pin them to keep them until you unpin or delete them.

  • Embed anywhere

    Copy the embed code to drop clips into blogs, docs, or any website.

Webhooks

Get signed push notifications for uploads, clips, balance changes, funding outcomes, clip expiry, and AI segment completions instead of polling every endpoint.

Events

clip.completeupload-based clip output is ready
clip.failedjob failed, balance refunded
upload.readyupload validated and ready
upload.failedupload rejected during validation
balance.reloadeddeposit or reload settled
btc.deposit.failedBTCPay deposit expired or invalid
card.reload.failedAPI reload approval or charge failed
balance.lowbelow your threshold
balance.emptybalance hit zero
clip.expiredunpinned clip output removed from storage
clip.auto.segment_completeAI segment finished
clip.auto.segment_failedAI segment failed

Signature Verification

Every delivery is signed with HMAC-SHA256. Verify before processing:

# Header: X-FastClip-Signature: sha256=abc123... X-FastClip-Event: clip.complete X-FastClip-Delivery-ID: wh_del_abc123

Deduplicate retries using X-FastClip-Delivery-ID. The secret is shown once at webhook creation.

Retry Schedule

Failed deliveries (non-2xx or timeout) retry up to 7 times with exponential backoff:

Attempt 1 → immediate

Attempt 2 → +5 sec

Attempt 3 → +30 sec

Attempt 4 → +5 min

Attempt 5 → +30 min

Attempt 6 → +2 hr

Attempt 7 → +2 hr, then give up

Auto-Disable

After 5 consecutive failures, a webhook is marked as failing and you receive an email alert.

After 30 days with no successful delivery, the webhook is auto-disabled. Re-enable via PATCH /v1/webhooks/{webhook_id}.

Use POST /v1/webhooks/{webhook_id}/test to verify your endpoint before going live.

API Reference

Base URL

https://api.fastclip.dev/v1

Request

{ "file_id": "upl_abc123...", "start": 65.2, "end": 128.7, "format": "mp4", "captions": { "preset_ref": "builtin:short-form-karaoke" } }

Response

{ "job_id": "a1b2c3d4e5f6g7h8", "status": "pending" }

Video uploads export mp4. Audio uploads export mp3, wav, m4a, or flac. quality accepts 720p, 1080p, and 4k for MP4 output. 4k requires a successful paid deposit; free, promo, signup, and test credits can export up to 1080p. Omit quality for audio outputs. GIF output is available in Clip Studio only. Accepts a segments array for batch trimming. Add an optional captions object with preset_ref to trim and caption an MP4 in one job. Trim cost and caption cost are additive.

Request

{ "file_id": "upl_abc123...", "query": "the part about neural networks", "format": "mp4", "captions": { "preset_ref": "builtin:short-form-karaoke" } }

Response

{ "job_id": "x1y2z3a4b5c6d7e8", "status": "pending", "reserved": 0.12 }

Tiered pricing: $0.10 (≤30 min), $0.20 (≤60 min), $0.35 (≤120 min), $0.50 (≤240 min). Video uploads export mp4; audio uploads export mp3, wav, m4a, or flac. Add captions only with MP4 video output. Caption reserve is additive: $0.02/input minute up to 1080p or $0.04/input minute for true 4K, with input resolution detected automatically. Unused reserve is refunded. Audio outputs ignore video quality. There is no extra AI segment surcharge.

Request

{ "file_id": "upl_abc123...", "query": "all mentions of machine learning" }

Response

{ "segments": [{ "start": 45, "end": 112, "excerpt": "...where machine learning really changed the game...", "confidence": 0.92 }], "cost": 0.08, "balance": 4.82 }

Tiered pricing: $0.08 (≤30 min), $0.15 (≤60 min), $0.25 (≤120 min), $0.40 (≤240 min). No output clip is rendered. Works with video and audio uploads. Pair with POST /v1/clip to trim only the segments you want.

Response (complete)

{ "job_id": "a1b2c3d4e5f6g7h8", "status": "complete", "progress": 100, "status_message": "Done!", "download_url": "https://.../abc123.mp4", "file_name": "trim_1-05-2-08.mp4", "format": "mp4", "quality": "1080p", "aspect_ratio": "16:9", "saved_to_library": true, "clip_id": "a1b2c3d4e5f6g7h8", "share_url": "/c/a1b2c3d4e5f6g7h8", "embed_url": "/embed/a1b2c3d4e5f6g7h8", "cost": 0.02 }

Status values include pendinganalyzing (auto only) → processing/processing segment N/Muploadingcomplete. On failure: failed with error field. The quality field always reflects the delivered quality, not just the requested one. Auto-clips return a clips array with per-segment download URLs, delivered quality, and per-segment share_url/embed_url.

Request

{ "file_id": "upl_abc123..." }

Response

{ "text": "[0:00:00] Hello everyone...", "segments": [{ "start": 0.0, "end": 3.5, "text": "Hello everyone" }], "duration": 1200.5, "cost": 0.05, "balance": 4.95 }

Tiered pricing by source duration: $0.02 (≤10 min), $0.05 (≤30 min), $0.10 (≤60 min), $0.20 (≤120 min), $0.40 (≤240 min). Works with video and audio uploads. Includes timestamped segments and formatted text.

Captions

Request

{ "file_id": "upl_abc123...", "preset_ref": "builtin:short-form-karaoke" }

Response

{ "job_id": "job_cap_123", "status": "pending", "creates_new_asset": true, "format": "mp4", "quality": "1080p" }

Provide exactly one eligible MP4 video source: file_id for a validated upload or clip_id for an existing owned MP4 clip. Captioned outputs are new files and never overwrite the source. Pricing is per input video minute: $0.02 up to 1080p or $0.04 for true 4K. Input resolution is detected automatically; omit quality unless you want to cap the returned MP4 lower than the source.

Response (complete)

{ "job_id": "job_cap_123", "status": "complete", "captioned": true, "clip_id": "cap_clip_123", "download_url": "https://.../captioned.mp4", "share_url": "/c/cap_clip_123", "cost": 0.04 }

Status values follow the regular job lifecycle: pending, downloading, processing, uploading, complete, or failed.

Response

[{ "preset_ref": "builtin:short-form-karaoke", "scope": "built_in", "name": "Short-Form Karaoke", "font_size": 120, "highlight_mode": true, "highlight_color": "#fcea2a" }]

Caption preset management uses a signed-in site session in v1. Use scope=all, scope=built_in, or scope=custom. In the API, highlight_color is the caption accent color: active word color for karaoke/highlight mode, box color for highlight-box/news-ticker styles, and accent color for glow-style effects.

Request

POST /v1/caption-fonts?filename=MyFont.ttf Content-Type: multipart/form-data [email protected]

Caption fonts are site-session only in v1. Upload TTF or OTF files up to 10 MB from Caption Studio, then save a custom preset that references the returned font_id.

Uploads

Request

{ "filename": "camera-original.mp4", "content_type": "video/mp4", "file_size": 524288000, "clip": { "start": 30, "end": 90, "source_title": "Camera Original" } }

Response

{ "file_id": "upl_abc123...", "upload_url": "https://...presigned...", "r2_key": "uploads/user_id/upl_abc123/camera-original.mp4", "expires_in": 3600, "clip_requested": true }

Use this single-upload endpoint for small files and simple integrations, generally 96 MB or less. Upload the whole file with one HTTP PUT to upload_url, then poll GET /v1/upload/{file_id}. For large files, use the multipart endpoints below for parallel chunk uploads and safer retries. Accepts video (mp4, mov, avi, mkv, webm, m4v, flv) and audio (mp3, wav, aac, flac, ogg, m4a, wma). Max 5 GB, max duration 3 hours.

Request

{ "filename": "large-source.mp4", "content_type": "video/mp4", "file_size": 2147483648, "part_size": 33554432, "part_count": 64 }

Response

{ "file_id": "upl_abc123...", "upload_id": "multipart_upload_id", "parts": [ { "part_number": 1, "url": "https://...part-1" }, { "part_number": 2, "url": "https://...part-2" } ], "expires_in": 3600, "clip_requested": false }

Use multipart for files over 96 MB, browser-like parallel uploads, or large API uploads. Compute part_count = ceil(file_size / part_size). A good default is 32 MiB parts for desktop/server clients and 16 MiB for mobile. Parts must be at least 5 MiB except the final part. Upload each byte range with HTTP PUT to its matching part URL and capture the returned ETag.

Request

{ "file_id": "upl_abc123...", "upload_id": "multipart_upload_id", "parts": [ { "part_number": 1, "etag": "etag-from-part-1" }, { "part_number": 2, "etag": "etag-from-part-2" } ] }

Response

{ "ok": true, "file_id": "upl_abc123...", "queued_validation": true }

Complete only after every part upload succeeds. Then poll GET /v1/upload/{file_id} until status is ready. If part URLs expire during a long upload, call POST /v1/upload/multipart/refresh. To cancel an unfinished multipart upload, call POST /v1/upload/multipart/abort.

Refresh part URLs

POST /v1/upload/multipart/refresh { "file_id": "upl_abc123...", "upload_id": "multipart_upload_id", "part_numbers": [8, 9, 10] }

Cancel multipart upload

POST /v1/upload/multipart/abort { "file_id": "upl_abc123...", "upload_id": "multipart_upload_id" }

Use refresh when a part URL expires before that part is uploaded. Use abort when the customer cancels or your client will not complete the multipart upload. For a simple single-upload record, cancel with POST /v1/upload/abort and a file_id.

Response

{ "file_id": "upl_abc123...", "status": "ready", "filename": "camera-original.mp4", "file_size": 524288000, "duration": 1800.5, "max_height": 1080, "format_detected": "mov,mp4,m4a,3gp,3g2,mj2", "error": null, "created_at": "2026-04-08T12:00:00Z", "expires_at": "2026-04-09T12:00:00Z", "clip_requested": true, "clip_job_id": "job_upload_123" }

Status: pendingprocessingready (or failed). clip_requested tells you whether a one-shot clip was attached at upload request time, and clip_job_id appears once that trim job is queued. You can still submit file_id manually to POST /v1/clip if you skipped the inline clip request.

Webhooks

Request

{ "url": "https://example.com/hook", "events": ["upload.ready", "upload.failed", "clip.complete", "balance.low", "balance.reloaded", "card.reload.failed", "clip.auto.segment_complete", "clip.auto.segment_failed"], "balance_threshold": 5.0 }

Response

{ "id": "wh_abc123", "url": "https://example.com/hook", "events": ["upload.ready", "upload.failed", "clip.complete", "balance.low", "balance.reloaded", "card.reload.failed", "clip.auto.segment_complete", "clip.auto.segment_failed"], "balance_threshold": 5.0, "status": "active", "consecutive_failures": 0, "created_at": "2026-04-08T12:00:00Z", "updated_at": null, "secret": "whsec_abc123..." }

The secret is shown once on creation. Store it securely. Use it to verify HMAC-SHA256 signatures on incoming deliveries. Max 10 webhooks per account. Supported events: clip.complete, clip.failed, upload.ready, upload.failed, balance.reloaded, btc.deposit.failed, card.reload.failed, balance.low, balance.empty, clip.expired, clip.auto.segment_complete, and clip.auto.segment_failed. balance_threshold only affects balance.low.

Response

[{ "id": "wh_abc123", "url": "https://example.com/hook", "events": ["upload.ready", "balance.low", "card.reload.failed", "clip.auto.segment_complete", "clip.auto.segment_failed"], "balance_threshold": 5.0, "status": "active", "consecutive_failures": 0, "created_at": "2026-04-08T12:00:00Z", "updated_at": null }]

Response

{ "id": "wh_abc123", "url": "https://example.com/hook", "events": ["upload.ready", "balance.low", "card.reload.failed", "clip.auto.segment_complete", "clip.auto.segment_failed"], "balance_threshold": 5.0, "status": "active", "consecutive_failures": 0, "created_at": "2026-04-08T12:00:00Z", "updated_at": null }

Request

{ "events": ["clip.complete", "balance.low", "btc.deposit.failed", "card.reload.failed", "clip.expired"], "balance_threshold": 10.0, "status": "active" }

Response

{ "id": "wh_abc123", "url": "https://example.com/hook", "events": ["clip.complete", "balance.low", "btc.deposit.failed", "card.reload.failed", "clip.expired"], "balance_threshold": 10.0, "status": "active", "consecutive_failures": 0, "created_at": "2026-04-08T12:00:00Z", "updated_at": "2026-04-08T13:00:00Z" }

Response

{ "deleted": true }

Response

{ "delivered": true, "response_status": 200, "response_time_ms": 142, "payload_sent": { "event": "clip.complete", "data": { "job_id": "test_job_000", "clip_id": "test_clip_000", "format": "mp4", "quality": "1080p", "duration": 30.0, "download_url": "https://example.com/test-clip.mp4", "cost": 0.10 } } }

Sends a sample clip.complete payload to the registered endpoint. Delivery metadata is returned in headers, including X-FastClip-Delivery-ID.

Response

[{ "id": "del_row_001", "delivery_id": "wh_del_abc123", "event": "clip.complete", "payload": { "event": "clip.complete" }, "response_status": 200, "response_time_ms": 142, "attempt": 1, "status": "delivered", "next_retry_at": null, "created_at": "2026-04-08T12:00:00Z" }]

Clip Management

Response

{ "clips": [{ "id": "a1b2c3d4e5f6g7h8", "name": "Intro Section", "format": "mp4", "quality": "1080p", "duration": 60, "file_size": 1048576, "pinned": true, "expires_at": null, "hours_left": null, "download_url": "https://.../a1b2c3.mp4", "share_url": "/c/a1b2c3d4e5f6g7h8", "embed_url": "/embed/a1b2c3d4e5f6g7h8", "aspect_ratio": "16:9", "source_title": "My Source Video", "created": "2026-03-30T12:00:00Z" }], "page": 1, "per_page": 20, "total_items": 47, "total_pages": 3 }

Paginated, most recent first. Query params: ?page=1&per_page=20. All trims and AI-generated clips are saved automatically. Each item exposes share_url and embed_url.

Request

{ "name": "Final Intro Cut" }

Response

{ "id": "a1b2c3d4e5f6g7h8", "name": "Final Intro Cut" }

Response

{ "id": "a1b2c3d4e5f6g7h8", "pinned": true }

Pinned clips count against your storage quota. Returns error if quota exceeded.

Response

{ "id": "a1b2c3d4e5f6g7h8", "pinned": false, "expires_at": "2026-03-31T12:00:00+00:00" }

Response

{ "id": "a1b2c3d4e5f6g7h8", "deleted": true, "storage_freed": 40509 // bytes }

This permanently deletes the clip from storage. This action cannot be undone.

Account

Response

{ "balance": 4.90 }

Response

{ "cards": [{ "id": "pm_1abc123", "brand": "visa", "last4": "4242", "exp_month": 12, "exp_year": 2027, "is_default": true }] }

Saved-card management requires a signed-in site session, not an API key. Listing cards does not require API Reload to be enabled. Use the returned pm_... id with DELETE /v1/cards/{payment_method_id} and password confirmation to remove a saved card.

Request

{ "amount": 2500, // cents ($25) "reload_password": "...", // if set "attempt_key": "reload_20260429_001" // for direct reloads }

Response

{ "success": true, "payment_intent_id": "pi_3T...", "amount": 2500, "balance": 29.90, "base_amount": 25.00, "credited_amount": 25.00, "total_bonus": 0.00, "first_topup_bonus": 0.00, "threshold_bonus": 0.00 }

$5–$500 per reload. Rate limit: 10/hour. Requires API Reload enabled in Settings. Direct reloads require an idempotency key via attempt_key, Idempotency-Key, or X-Idempotency-Key. If email confirmation is on, the immediate response is { "pending": true, "message": "...", "amount": 2500 } and an approval email is sent before any charge.

Authentication

Authorization: Bearer sk_live_...

Generate API keys from your account dashboard. Max 10 keys per account. Most API endpoints require auth; public share and reload-approval links are exceptions.

Rate Limiting

API-key requests: 60 req/min per key. Media processing jobs: 20/hour per user. Reloads: 10/hour. Unauthenticated endpoints are IP-limited.

X-RateLimit-Remaining: 8 X-RateLimit-Reset: 1711814400 Retry-After: 42 // on 429 only

Using AI agents?
Give them this URL.

A plain-text reference mirroring the public endpoints and product rules shown on this page. Optimised for LLMs to read and reason about.

https://fastclip.dev/llm.md
View LLM Reference

API Details

Everything else you need to know when integrating.

Error Responses

All errors return JSON with a detail field:

{ "detail": "Insufficient balance" }

400 Bad request

404 Not found

401 Invalid API key

409 Storage quota full

402 Low balance

403 Feature gated

422 Invalid file, unsupported format, or undecodable upload

429 Rate limited (check Retry-After)

Uploads that fail validation return 422 with a descriptive detail message.

Accepted Uploads

Video

MP4 MOV AVI MKV WebM M4V FLV

Audio

MP3 WAV AAC FLAC OGG M4A WMA

Output Formats

mp4 mp3 wav m4a flac

Quality

4k 1080p 720p

Video uploads export MP4. Audio uploads can export mp3, wav, m4a, and flac. MP4 outputs support 1080p and 720p by default. 4k requires a successful paid deposit. Captions require MP4 video output. Audio outputs ignore video quality. If a source tops out lower, FastClip auto-downgrades to the highest available quality at or below the request and returns the delivered quality in API responses. GIF output is available in Clip Studio only.

Limits

Max trim10 min
GIF max30 sec
Source file3 hr / 5 GB
Storage10 GB free
Font upload10 MB
AI query500 chars
Clip name200 chars

Clip Lifecycle

1

New clips expire in 24 hours by default

2

Pin to keep until unpinned or deleted (counts against storage)

3

Unpin starts a new 24hr countdown

Expired clips return 404. Download URLs are presigned & regenerated on each request.

Pricing

Trim $0.02/segment
AI Auto-Clip from $0.10 tiered
AI Analyze from $0.08 tiered
Transcript from $0.02 tiered
Captions $0.02/input min

4K trims cost $0.04/segment. Captions are $0.02 per input minute up to 1080p or $0.04 per input minute for true 4K and are additive to trim or AI Auto-Clip pricing. Caption input resolution is detected automatically. AI Auto-Clip tiers: $0.10 (≤30 min), $0.20 (≤60 min), $0.35 (≤120 min), $0.50 (≤240 min), with no per-segment surcharge. AI Analyze tiers: $0.08, $0.15, $0.25, $0.40. Transcript tiers: $0.02 (≤10 min), $0.05 (≤30 min), $0.10 (≤60 min), $0.20 (≤120 min), $0.40 (≤240 min).

Storage

10 GB free per account. Only pinned clips count.

Check: GET /v1/storage

Buy: POST /v1/storage/purchase { "gb": 5 }

Share Links

Each clip gets a public URL. No auth needed to view.

Page: /c/{clip_id}

API: GET /v1/share/{clip_id}

Returns name, format, duration, stream & download URLs. Expires with clip.

FAQ

What file formats are supported? +

FastClip accepts video uploads (MP4, MOV, AVI, MKV, WebM, M4V, FLV) and audio uploads (MP3, WAV, AAC, FLAC, OGG, M4A, WMA). Max file size is 5 GB, max source duration is 3 hours. Video uploads export MP4. Audio uploads can export MP3, WAV, M4A, and FLAC. GIF is available in Clip Studio only.

How does pay-as-you-go work? +

Deposit funds into your account with a $5 minimum and pay only for each job you run. Your first successful paid top-up gets a 50% bonus, any top-up of $50 or more gets an extra 10%, and standard top-up bonuses stack when both apply. One-time special deals do not stack with those bonuses. Trims cost $0.02/segment, captions cost $0.02 per input video minute up to 1080p or $0.04 per input video minute for true 4K, AI Auto-Clip starts at $0.10, transcripts start at $0.02 and scale by source duration, and there are no subscriptions. 4K trims cost $0.04/segment.

How do captions work in the browser? +

Caption Studio is for creating and previewing reusable presets. To apply captions, upload a video in Clip Studio, choose Trim or AI Auto, set output to MP4, enable Add captions, choose a pre-made or custom preset, and run the job. Captioned results are new MP4 files; your source upload is not modified.

Can I caption an upload or an existing clip through the API? +

Yes. Use POST /v1/captions with either a video file_id or an owned MP4 clip_id. If you want to caption in the same workflow, add captions: { preset_ref } to POST /v1/clip or POST /v1/clip/auto.

How are captions billed? +

Captions cost $0.02 per input video minute up to 1080p, or $0.04 per input video minute for true 4K. The input resolution is detected automatically from the upload or source clip. Caption cost is additive when you trim or AI auto-clip and caption in one job.

Which sources can receive captions? +

Captions require video and MP4 output. Audio exports and GIFs cannot receive captions. Dedicated caption jobs accept validated video uploads and owned MP4 history clips that are still available. Captioned clips cannot be captioned again.

Can I upload custom caption fonts? +

Yes. Caption Studio supports TTF and OTF font uploads up to 10 MB. Uploaded fonts are private to your account and appear in the font dropdown alongside bundled Google fonts when you create custom presets.

How are caption presets reused? +

Every preset has a preset_ref. Browser users pick it from the Clip Studio dropdown. API users pass the same value to /v1/captions or inside a captions object on /v1/clip or /v1/clip/auto. Pre-made presets are available to everyone; custom presets are account-scoped.

How is AI auto-clip cost calculated? +

AI Auto-Clip uses tiered pricing based on source duration: $0.10 (≤30 min), $0.20 (≤60 min), $0.35 (≤120 min), $0.50 (≤240 min). Multiple matching clips can be returned within that tier. Any unused reserved amount is refunded after processing, and failed jobs are refunded automatically.

What happens to my balance if I don't use it? +

Deposits do not expire. Funds stay in your account until used. Deposits are non-refundable, and if you delete your account any remaining balance is forfeited.

Can I get a receipt or invoice for deposits? +

Successful deposits trigger receipt emails, and API Reload charges have receipt emails too. If you need extra billing documentation, contact support from the dashboard.

What payment methods are accepted? +

FastClip accepts credit and debit cards through Stripe, plus Bitcoin deposits through BTCPay. BTCPay checkout may offer on-chain BTC and Lightning invoices depending on checkout availability. Balance is credited only after FastClip verifies settlement server-side.

What's the minimum deposit? +

The minimum deposit is $5.00.

What is AI Auto-Clip? +

AI Auto-Clip lets you describe the moment you want in plain English. FastClip analyzes the uploaded media, finds matching highlights, and can return multiple clips when several moments match the prompt.

What's the maximum clip length? +

Manual trims and AI clips can be up to 10 minutes long. GIF exports are limited to 30 seconds (Clip Studio only).

How long are clips available? +

Clip assets stay available for 24 hours unless the clip is pinned. Download URLs are presigned and regenerated when requested. Pinned clips remain in storage until you unpin or delete them.

What if a clip fails to process? +

FastClip automatically refunds the reserved amount for any failed clip job, so you are never charged for failed processing.

How do I know when my job is done? +

Poll GET /v1/clip/{job_id} for trim jobs, or GET /v1/upload/{file_id} for upload validation. If you do not want to poll, register webhooks for clip.complete, clip.failed, upload.ready, upload.failed, clip.auto.segment_complete, or clip.auto.segment_failed.

Why did my job return a 422 error? +

A 422 usually means the uploaded file could not be decoded, the format is unsupported, the file exceeds the 5 GB or 3-hour limit, or the source exceeds the AI duration limit for auto-clip or analyze.

Can I cancel a job after submitting it? +

Not currently. FastClip does not expose a cancel endpoint after submission, so the usual pattern is to poll the job until it completes or fails, or use webhooks for notifications.

How long does a typical clip take to process? +

There is no single fixed time because it depends on source length, queue load, output format, and requested quality. Short standard trims usually finish in a few seconds. AI jobs and 4K trims take longer. Poll the job status or use webhooks if you need an exact completion signal.

Are there any restrictions on what content I can clip? +

Yes. You must only process content you have the right to use. FastClip also prohibits malicious uploads, malware, exploit attempts, and abusive webhook use.

Is API Reload secure? +

API Reload is off by default. When enabled, you should also set a reload password or email confirmation so an exposed API key cannot charge saved cards without another check.

What are the rate limits? +

API-key requests are limited to 60 requests per minute per key. Media processing jobs are limited to 20 per hour per user, and reload is limited to 10 per hour.

How many API keys can I have? +

You can have up to 10 API keys per account. Each key is shown once on creation and can be revoked any time.

Can I use FastClip without coding? +

Yes. The Clip Studio on fastclip.dev lets you upload a file and create trims, AI auto-clips, transcripts, and more without writing code.

Can I add this to n8n / Make / Zapier? +

Yes. FastClip exposes a REST/JSON API plus webhooks, so you can connect it to n8n, Make, Zapier, or any tool that can send HTTP requests and receive webhook events.

What does the reserved field mean in the auto-clip response? +

reserved is the upfront amount held from your balance when the auto-clip job is accepted. It covers the estimated processing cost before the final result is known. If the final cost is lower, the unused amount is refunded after processing, and failed jobs are refunded automatically.

Do I need a credit card to start? +

No. New accounts receive a $1.00 free balance after email verification, which is enough to try the platform before adding funds.

Can I have multiple accounts? +

FastClip accounts are individual, and the Terms say you must not share your account or API keys with other people. If you need separation for different people or workflows, keep them separate instead of sharing one login.

How do I delete my account? +

You can delete your account from the dashboard. FastClip requires you to type DELETE and re-enter your password before the deletion is processed.

What happens to my clips if I delete my account? +

Account deletion is irreversible. FastClip deletes your user, clip records, and related data, removes stored clip objects, and forfeits any remaining balance.

What is AI Analyze? +

AI Analyze returns timestamps, confidence scores, and transcript excerpts without rendering a clip, so you can preview likely highlights before creating outputs.

Are deposits refundable? +

No. Deposits are non-refundable, but failed jobs are automatically refunded back to your FastClip balance.

What output formats are available? +

Video uploads export MP4. Audio uploads can export MP3, WAV, M4A, and FLAC. MP4 requests support 720p and 1080p by default. 4K output requires a successful paid deposit. Audio outputs ignore video quality. GIF output is available in Clip Studio only, not via the API.

What should I know about 4K exports? +

4K output requires a successful paid deposit. 4K trims cost $0.04/segment (vs. $0.02 for standard). Free, promo, signup, and test credits can export up to 1080p.

What happens if the source doesn't support my requested quality? +

FastClip auto-downgrades to the highest available quality at or below your request. If you request 4K and the source only supports 1080p, FastClip exports 1080p. If you request 720p and the source tops out at 480p, FastClip exports 480p. The delivered quality is returned in the API response and shown in History. Trim pricing uses delivered quality; caption pricing uses detected input resolution.

Can I manage clips through the API? +

Yes. The API supports listing clip history, renaming clips, pinning and unpinning them, deleting them, checking balance, and reloading balance. Saved-card management is available to signed-in site sessions.

What does pinning a clip do? +

Pinning keeps a clip in your storage so it will not expire after 24 hours. Pinned clips count against your storage quota until you unpin or delete them.

How does clip storage work? +

Every account gets 10 GB of free storage for pinned clips. You can purchase one-time storage add-ons if you need more capacity for saved highlights.

What are webhooks? +

Webhooks let FastClip push signed real-time notifications to your server for upload validation, clip completion and failure, funding outcomes, balance alerts, clip expiry, and per-segment AI auto-clip completions. Instead of polling multiple endpoints, you register a URL and FastClip sends a POST the moment the event fires.

How do I verify webhook signatures? +

Each webhook delivery includes a X-FastClip-Signature header containing an HMAC-SHA256 hash of the request body, signed with your webhook secret (whsec_...). Compute the HMAC on your end and compare it before processing the event.

Can I upload my own video files? +

Yes. For small files, use POST /v1/upload/request to get a single upload URL. For large files, use POST /v1/upload/multipart/initiate, upload parts in parallel, then call POST /v1/upload/multipart/complete. Both paths return a file_id. You can optionally include a clip object with start, end, and source_title so FastClip validates the upload and auto-queues a trim after the file lands. Then either poll GET /v1/upload/{file_id} for clip_job_id or wait for upload.ready and the normal clip.complete/clip.failed webhook flow.

What formats can I upload? +

FastClip accepts video containers (mp4, mov, avi, mkv, webm, m4v, flv) and audio files (mp3, wav, aac, flac, ogg, m4a, wma). Max file size is 5 GB and max source duration is 3 hours.

How long does upload validation take? +

There is no fixed SLA. Validation starts after the upload finishes and depends on file size and queue load. Poll GET /v1/upload/{file_id} until the status becomes ready.