The Video Clipping API

Cut clips from any video URL. Manually or with one API call.

Loading video info…

Clip Studio

: :
: :

Duration:

GIF limited to 30s (current: )

Estimated Cost
Download Process Upload Done

Get $5 free to test when you sign up

Dead simple to integrate

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

# Clip a video with one API call
import requests, time

headers = {"Authorization": "Bearer sk_live_..."}
clip = requests.post(
    "https://api.fastclip.dev/v1/clip",
    headers=headers,
    json={
        "url": "https://youtube.com/watch?v=dQw4w9WgXcQ",
        "start": 30,
        "end": 90,
        "format": "mp4",
        "quality": "1080p"
    }
).json()

# Poll until ready
while True:
    r = requests.get(f"https://api.fastclip.dev/v1/clip/{clip['job_id']}", headers=headers).json()
    if r["status"] == "complete":
        print(r["download_url"])
        break
    time.sleep(2)
// Clip a video with one API call
const response = await fetch(
  "https://api.fastclip.dev/v1/clip", {
    method: "POST",
    headers: {
      "Authorization": "Bearer sk_live_...",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      url: "https://youtube.com/watch?v=dQw4w9WgXcQ",
      start: 30, end: 90,
      format: "mp4", quality: "1080p"
    })
  }
);
const clip = await response.json();

// Poll until ready
while (true) {
  const r = await fetch(
    `https://api.fastclip.dev/v1/clip/${clip.job_id}`,
    { headers: { "Authorization": "Bearer sk_live_..." } }
  ).then(r => r.json());
  if (r.status === "complete") { console.log(r.download_url); break; }
  await new Promise(r => setTimeout(r, 2000));
}
# Clip a video with one API call
curl -X POST https://api.fastclip.dev/v1/clip \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://youtube.com/watch?v=dQw4w9WgXcQ",
    "start": 30, "end": 90,
    "format": "mp4", "quality": "1080p"
  }'
// Clip a video with one API call
package main

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

func main() {
    body, _ := json.Marshal(map[string]any{
        "url":     "https://youtube.com/watch?v=dQw4w9WgXcQ",
        "start":   30,
        "end":     90,
        "format":  "mp4",
        "quality": "1080p",
    })

    req, _ := http.NewRequest("POST",
        "https://api.fastclip.dev/v1/clip",
        bytes.NewBuffer(body))
    req.Header.Set("Authorization", "Bearer sk_live_...")
    req.Header.Set("Content-Type", "application/json")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    fmt.Println(resp.Status)
}
# Clip a video with one API call
require "net/http"
require "json"
require "uri"

uri = URI("https://api.fastclip.dev/v1/clip")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer sk_live_..."
request["Content-Type"] = "application/json"
request.body = {
  url: "https://youtube.com/watch?v=dQw4w9WgXcQ",
  start: 30, end: 90,
  format: "mp4", quality: "1080p"
}.to_json

response = http.request(request)
clip = JSON.parse(response.body)
job_id = clip["job_id"]

# Poll until ready
loop do
  sleep 2
  poll = Net::HTTP::Get.new(URI("https://api.fastclip.dev/v1/clip/#{job_id}"))
  poll["Authorization"] = "Bearer sk_live_..."
  r = JSON.parse(http.request(poll).body)
  if r["status"] == "complete"
    puts r["download_url"]
    break
  end
end
// Clip a video with one API call
$response = file_get_contents(
    "https://api.fastclip.dev/v1/clip",
    false,
    stream_context_create([
        "http" => [
            "method" => "POST",
            "header" => implode("\r\n", [
                "Authorization: Bearer sk_live_...",
                "Content-Type: application/json"
            ]),
            "content" => json_encode([
                "url"     => "https://youtube.com/watch?v=dQw4w9WgXcQ",
                "start"   => 30,
                "end"     => 90,
                "format"  => "mp4",
                "quality" => "1080p"
            ])
        ]
    ])
);

$clip = json_decode($response, true);

// Poll until ready
$ctx = stream_context_create(["http" => [
    "header" => "Authorization: Bearer sk_live_..."
]]);
do {
    sleep(2);
    $r = json_decode(file_get_contents(
        "https://api.fastclip.dev/v1/clip/" . $clip["job_id"],
        false, $ctx
    ), true);
} while ($r["status"] !== "complete");
echo $r["download_url"];

Auto-reload via API

Top up your balance programmatically with a saved card. Enable in Settings → API Reload.

# Check balance, list cards, then reload
import requests

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. List saved cards
cards = requests.get("https://api.fastclip.dev/v1/cards", headers=headers).json()
for c in cards["cards"]:
    print(f"  {c['brand']} ****{c['last4']}")

# 3. Reload $25 with saved card
reload = requests.post("https://api.fastclip.dev/v1/reload",
    headers=headers,
    json={"amount": 2500}  # cents
).json()

print(f"New balance: ${reload['balance']:.2f}")
# → New balance: $29.90
// Check balance, list cards, then 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. List saved cards
const cards = await fetch("https://api.fastclip.dev/v1/cards", { headers })
  .then(r => r.json());
cards.cards.forEach(c => console.log(`  ${c.brand} ****${c.last4}`));

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

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

# 2. List saved cards
curl https://api.fastclip.dev/v1/cards \
  -H "Authorization: Bearer sk_live_..."

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

# → {"success":true,"balance":29.90,...}
// Check balance, list cards, then reload
package main

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

func apiGet(url, auth string) map[string]any {
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("Authorization", auth)
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    var out map[string]any
    json.NewDecoder(resp.Body).Decode(&out)
    return out
}

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

    // 1. Check balance
    bal := apiGet("https://api.fastclip.dev/v1/balance", auth)
    fmt.Printf("Balance: $%.2f\n", bal["balance"])

    // 2. List saved cards
    cards := apiGet("https://api.fastclip.dev/v1/cards", auth)
    for _, c := range cards["cards"].([]any) {
        card := c.(map[string]any)
        fmt.Printf("  %s ****%s\n", card["brand"], card["last4"])
    }

    // 3. 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")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    out, _ := io.ReadAll(resp.Body)
    fmt.Println(string(out))
    // → {"success":true,"balance":29.90,...}
}
# Check balance, list cards, then 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. List saved cards
req = Net::HTTP::Get.new("/v1/cards")
req["Authorization"] = auth
cards = JSON.parse(http.request(req).body)
cards["cards"].each { |c| puts "  #{c['brand']} ****#{c['last4']}" }

# 3. Reload $25
req = Net::HTTP::Post.new("/v1/reload")
req["Authorization"] = auth
req["Content-Type"] = "application/json"
req.body = { amount: 2500 }.to_json
reload = JSON.parse(http.request(req).body)

puts "New balance: $#{'%.2f' % reload['balance']}"
# → New balance: $29.90
// Check balance, list cards, then 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. List saved cards
$cards = json_decode(file_get_contents(
    "https://api.fastclip.dev/v1/cards", false,
    stream_context_create(["http" => ["header" => $auth]])
), true);
foreach ($cards["cards"] as $c) {
    echo "  {$c['brand']} ****{$c['last4']}\n";
}

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

echo "New balance: $" . number_format($r["balance"], 2);
// → New balance: $29.90

Let AI do the clipping

Don't know the timestamps? Just describe what you want and our AI finds and clips the right moments.

1. Describe

Tell us what you're looking for in plain English. "The part about machine learning" or "when the host laughs."

2. AI Analyzes

Our AI processes the video's content, using transcripts for YouTube or audio analysis for other sites, to find matching moments.

3. Get Clips

Multiple matching segments? You get multiple clips. Each additional segment is just $0.10 extra. Download them all instantly.

Example Request

POST /v1/clip/auto { "url": "https://youtube.com/watch?v=...", "query": "the part about neural networks", "format": "mp4", "quality": "1080p" }

Analyze Only: from $0.10

Just want the timestamps? The /v1/clip/analyze endpoint returns matching segments with confidence scores and transcript excerpts when available. No output clip is rendered, and no format or quality is needed.

POST /v1/clip/analyze { "url": "https://youtube.com/watch?v=...", "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.10 }

Pair with POST /v1/clip to clip only the segments you want.

Works with YouTube

Coming soon

TikTok X Vimeo Instagram Twitch Kick

Pay per clip. No subscriptions.

Deposit funds ($5 minimum), clip videos.

Manual
$0.10
per clip
  • YouTube support
  • Set start & end time
  • MP4, MP3, or GIF
  • Up to 1080p
  • Max 10 min clip
AI Auto
$0.20
per hour of source video
  • Describe what to clip
  • AI finds the moments
  • Multiple clips possible
  • +$0.10 / extra segment
  • $0.10/hr analyze-only
  • All formats & qualities
  • Up to 3 hr source video

API Reference

Base URL

https://api.fastclip.dev/v1

Request

{ "url": "https://youtube.com/watch?v=..." }

Response

{ "title": "Video Title", "duration": 3600.5, "thumbnail": "https://...", "preview_url": null, "preview_embed_url": "https://...", "max_height": 1080 }

No auth required. Use to validate URLs, check duration (for cost estimates), and confirm platform support before clipping.

Request

{ "url": "https://youtube.com/watch?v=...", "start": 120, "end": 180, "format": "mp4", "quality": "1080p" }

Response

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

Request

{ "url": "https://youtube.com/watch?v=...", "query": "the part about neural networks", "format": "mp4", "quality": "720p" }

Response

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

Request

{ "url": "https://youtube.com/watch?v=...", "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.10 }

From $0.10 (scales with video length). No output clip is rendered. Excerpts contain transcript text when available (and may be empty on some sources). Pair with POST /v1/clip to clip only the segments you want.

Response (complete)

{ "job_id": "a1b2c3d4e5f6g7h8", "status": "complete", "progress": 100, "status_message": "Done!", "download_url": "https://r2.../abc123.mp4", "file_name": "clip_2-00-3-00.mp4", "cost": 0.10 }

Status values include pendinganalyzing (auto only) → downloadingprocessing/processing segment N/Muploadingcomplete. On failure: failed with error field. Auto-clips return a clips array with per-segment download URLs.

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://r2.../a1b2c3.mp4", "share_url": "/c/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

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": "card_1a2b3c", "brand": "visa", "last4": "4242", "exp_month": 12, "exp_year": 2027 }] }

Listing cards does not require API Reload to be enabled. Cards can only be added/removed via the dashboard UI.

Request

{ "amount": 2500, // cents ($25) "reload_password": "..." // if set }

Response

{ "success": true, "payment_intent_id": "pi_3T...", "amount": 2500, "balance": 29.90 }

$5–$500 per reload. Rate limit: 10/hour. Requires API Reload enabled in Settings. If email confirmation is on, returns pending status and sends an approval email.

Authentication

Authorization: Bearer sk_live_...

Generate API keys from your account dashboard. Max 10 keys per account. Exception: /v1/clip/info requires no auth.

Rate Limiting

API-key requests: 60 req/min per key. Clip 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 covering every endpoint, pricing rule, format, limit, and policy. Optimised for LLMs to read and reason about.

https://fastclip.dev/llm
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

422 Unsupported URL

429 Rate limited (check Retry-After)

Platforms

youtube.com

More coming soon

Others return 422

Formats

mp4 mp3 gif

Quality

1080p 720p

Auto-downgrades if source is lower. high=1080p, medium=720p

Limits

Max clip10 min
GIF max30 sec
Source video3 hr
YouTube / other
AI query500 chars
Clip name200 chars

Clip Lifecycle

1

New clips expire in 24 hours by default

2

Pin to keep permanently (counts against storage)

3

Unpin starts a new 24hr countdown

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

Pricing

Manual clip $0.10
AI auto-clip $0.20/hr
Analyze only $0.10/hr

Auto-clip: +$0.10 per extra segment. Cost reserved upfront, unused amount refunded.

Storage

1 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