# How to Use ReClip API Endpoints: A Complete Guide to Video Download Automation

> Automate video downloads with ReClip API endpoints. This guide shows you how to fetch metadata, download videos or audio, and track jobs using yt-dlp.

- Repository: [Avery Gan/reclip](https://github.com/averygan/reclip)
- Tags: how-to-guide
- Published: 2026-09-03

---

**ReClip exposes five JSON-based HTTP endpoints that let you fetch video metadata, download videos or audio, and track asynchronous jobs using yt-dlp.**

ReClip is a lightweight Flask microservice built by **averygan/reclip** that wraps **yt-dlp** into a programmable API. Whether you're building a video pipeline, a browser extension, or a media archiving tool, these endpoints let you automate video downloads without managing yt-dlp subprocesses directly. This guide covers every endpoint, shows working code examples in Python, and references the actual source at [`app.py`](https://github.com/averygan/reclip/blob/main/app.py).

---

## Getting Started with the ReClip API

The service runs on a host and port defined by `HOST` and `PORT` environment variables, defaulting to `127.0.0.1:8899`. See the startup block at the end of [`app.py`](https://github.com/averygan/reclip/blob/main/app.py) (lines 107-110) to configure your deployment.

All endpoints return JSON except for `/api/file/<job_id>`, which streams binary data. Error responses use standard HTTP status codes: **400** for malformed requests, **404** for missing jobs, and **500** for processing failures.

---

## How to Fetch Video Metadata with `/api/info`

The **`/api/info`** endpoint retrieves comprehensive metadata for a single video URL. Internally, it runs `yt-dlp -j <url>` and parses the output using `parse_ytdlp_json` (lines 16-28 in [`app.py`](https://github.com/averygan/reclip/blob/main/app.py)).

The response includes title, thumbnail, duration, uploader, and available formats. The handler also computes the **best format per resolution** using `best_by_height` logic (lines 14-20) to simplify client-side selection.

```python
import requests

BASE = "http://127.0.0.1:8899"

def get_info(video_url):
    resp = requests.post(f"{BASE}/api/info", json={"url": video_url})
    resp.raise_for_status()
    return resp.json()

# Example usage

info = get_info("https://www.youtube.com/watch?v=abc123")
print(info["title"], info["duration"])
print([f["id"] for f in info["formats"]])  # available format IDs

```

The `format_id` values returned here feed directly into the download endpoint below.

---

## How to Extract Playlist URLs with `/api/playlist`

The **`/api/playlist`** endpoint flattens a playlist into individual video URLs. It executes `yt-dlp --flat-playlist -J <url>` (lines 50-58) and returns a simple list:

```python
def get_playlist(playlist_url):
    resp = requests.post(f"{BASE}/api/playlist", json={"url": playlist_url})
    resp.raise_for_status()
    return resp.json()["urls"]  # list of video URLs

urls = get_playlist("https://www.youtube.com/playlist?list=...")
for url in urls:
    # Process each video individually

    info = get_info(url)

```

This avoids parsing complex playlist structures yourself—ReClip handles yt-dlp's JSON output and extracts only the `url` field from each entry.

---

## How to Start Asynchronous Downloads with `/api/download`

The **`/api/download`** endpoint initiates a background download job and returns immediately. This is the core of the ReClip API workflow.

**Key parameters:**
- `url` (required): The video URL
- `format`: Either `"video"` or `"audio"` — audio forces MP3 output
- `format_id`: A specific yt-dlp format ID from `/api/info`
- `title`: Suggested filename (sanitized by `safe_title` logic at lines 79-81)

The handler generates a 10-character hex job ID using `uuid.uuid4().hex[:10]` (line 77), stores request data in the global `jobs` dictionary (lines 78-80), and spawns a daemon thread running `run_download` (lines 32-90):

```python
def start_download(video_url, format_id=None, fmt="video", title=""):
    payload = {
        "url": video_url,
        "format": fmt,
        "format_id": format_id,
        "title": title
    }
    resp = requests.post(f"{BASE}/api/download", json=payload)
    resp.raise_for_status()
    return resp.json()["job_id"]  # e.g., "a3f7b2c9d1"

# Start with best format from /api/info

info = get_info(video_url)
best_format = info["formats"][0]["id"]
job_id = start_download(
    video_url,
    format_id=best_format,
    fmt="video",
    title=info["title"]
)

```

If no `format_id` is provided, `run_download` defaults to `bestvideo+bestaudio/best` for maximum quality.

---

## How to Track Download Progress with `/api/status/<job_id>`

The **`/api/status/<job_id>`** endpoint polls job state. It looks up the job in the `jobs` dictionary and returns `status`, `error` (if any), and `filename` when complete (lines 89-96):

```python
def check_status(job_id):
    resp = requests.get(f"{BASE}/api/status/{job_id}")
    resp.raise_for_status()
    return resp.json()

# Poll until completion

import time
while True:
    status = check_status(job_id)
    if status["status"] == "done":
        print(f"Ready: {status['filename']}")
        break
    elif status["status"] == "error":
        raise RuntimeError(status["error"])
    time.sleep(1)

```

Job states are: `"starting"`, `"downloading"`, `"done"`, or `"error"`.

---

## How to Retrieve Finished Files with `/api/file/<job_id>`

The **`/api/file/<job_id>`** endpoint streams the completed file. It validates that the job exists and `status == "done"`, then uses Flask's `send_file` with the sanitized download name (lines 100-105):

```python
def download_file(job_id, dest_path):
    resp = requests.get(f"{BASE}/api/file/{job_id}", stream=True)
    resp.raise_for_status()
    with open(dest_path, "wb") as f:
        for chunk in resp.iter_content(chunk_size=8192):
            f.write(chunk)

# Complete workflow

status = check_status(job_id)
if status["status"] == "done":
    download_file(job_id, f"./{status['filename']}")

```

The `Content-Disposition` header ensures the browser receives a clean filename without illegal characters.

---

## Complete ReClip API Integration Example

Here's a full workflow combining all five ReClip API endpoints:

```python
import requests
import time

BASE = "http://127.0.0.1:8899"

def full_download(video_url, output_dir="./"):
    # 1. Get metadata

    info = requests.post(f"{BASE}/api/info", json={"url": video_url}).json()
    
    # 2. Select best format

    best = info["formats"][0]  # highest resolution per /api/info logic

    
    # 3. Start download

    job = requests.post(f"{BASE}/api/download", json={
        "url": video_url,
        "format": "video",
        "format_id": best["id"],
        "title": info["title"]
    }).json()["job_id"]
    
    # 4. Poll status

    while True:
        status = requests.get(f"{BASE}/api/status/{job}").json()
        if status["status"] == "done":
            break
        elif status["status"] == "error":
            raise RuntimeError(status["error"])
        time.sleep(0.5)
    
    # 5. Download file

    r = requests.get(f"{BASE}/api/file/{job}", stream=True)
    path = f"{output_dir}/{status['filename']}"
    with open(path, "wb") as f:
        for chunk in r.iter_content(8192):
            f.write(chunk)
    return path

```

---

## Architecture and Implementation Details

Understanding how ReClip handles requests helps you build robust integrations.

**In-Memory Job State** — Jobs live in a global `jobs` dictionary. The service is stateless across requests; only this shared dict maintains download progress. Restarting the Flask process clears all job history.

**Background Threading** — The `run_download` function executes in a daemon `threading.Thread`, allowing `/api/download` to return instantly with a job ID. This prevents HTTP timeouts on slow downloads.

**Filename Sanitization** — The `safe_title` transform (lines 79-81) strips characters that would break filesystems or HTTP headers.

**Format Selection Logic** — When clients request `"audio"`, ReClip forces MP3 conversion via yt-dlp's format arguments. For video, explicit `format_id` values override the default `bestvideo+bestaudio/best` selector.

---

## Key Files in the ReClip Repository

| File | Purpose |
|------|---------|
| [`app.py`](https://github.com/averygan/reclip/blob/main/app.py) | Core Flask application containing all five API route handlers and download logic |
| [`templates/index.html`](https://github.com/averygan/reclip/blob/main/templates/index.html) | Interactive web UI that consumes the same API endpoints |
| [`requirements.txt`](https://github.com/averygan/reclip/blob/main/requirements.txt) | Python dependencies (`flask`, `yt-dlp`, etc.) |
| `Dockerfile` / [`docker-compose.yml`](https://github.com/averygan/reclip/blob/main/docker-compose.yml) | Container deployment configuration |

---

## Summary

- **`/api/info`** — POST a URL, get metadata and format options
- **`/api/playlist`** — POST a playlist URL, get flat video URL list
- **`/api/download`** — POST download parameters, receive job ID for async processing
- **`/api/status/<job_id>`** — GET current job state, error messages, and final filename
- **`/api/file/<job_id>`** — GET the finished file as a streamed attachment

The ReClip API abstracts yt-dlp's complexity into five predictable HTTP endpoints. All state management happens through the `jobs` dictionary in [`app.py`](https://github.com/averygan/reclip/blob/main/app.py), with background threads handling long-running downloads without blocking clients.

---

## Frequently Asked Questions

### Does ReClip require authentication?

No. The ReClip API is unauthenticated by default. Deploy behind a reverse proxy or VPN if you need access control. The Flask server binds to `127.0.0.1` by default (line 110), which limits exposure to localhost only.

### What happens if I restart the ReClip server during a download?

Active downloads terminate because job state lives only in the in-memory `jobs` dictionary. The partially downloaded file may remain in the temporary directory. Re-query `/api/status` for any job ID will return 404 after restart.

### Can I download audio-only files with ReClip?

Yes. Pass `"format": "audio"` to `/api/download`. This triggers yt-dlp with audio-specific parameters that force MP3 output. You may still specify a `format_id` from `/api/info` if you need a particular audio quality.

### How do I deploy ReClip in production?

Use the provided `Dockerfile` and [`docker-compose.yml`](https://github.com/averygan/reclip/blob/main/docker-compose.yml) for containerized deployment. Set the `HOST` and `PORT` environment variables to control binding. The default `127.0.0.1:8899` is suitable for local development; production deployments typically use `0.0.0.0` behind a reverse proxy.