# Local Video Upload Service

Upload a local video file and use it as input to any Bria video editing endpoint.

Most Bria video endpoints accept a `video` parameter that points to a publicly accessible URL. When your video lives on your own machine rather than at a public URL, use the upload service to get a temporary, hosted URL you can pass straight into those endpoints.

The flow is three steps:

1. **Request a presigned upload URL** — call `POST /v2/video/upload` to get an `upload_url`, the `upload_fields` needed to authenticate the upload, and the `file_url` your video will live at.
2. **Upload the file** — `POST` the video directly to `upload_url` as multipart form data.
3. **Process the video** — pass `file_url` as the `video` input to any video editing endpoint.


> **Using the Python SDK?** The Bria Python client wraps this whole flow — see the [SDK docs](#) for the one-call helper. The instructions below describe the underlying HTTP API for everyone else.


## Step 1 — Request a presigned upload URL


```
POST /v2/video/upload
```

**Body** (JSON)

| Field | Type | Required | Description |
|  --- | --- | --- | --- |
| `media_type` | string | No | MIME type of the video you intend to upload, e.g. `"video/mp4"`. When omitted, the server falls back to a `"video/"` content-type prefix. |



```json
{ "media_type": "video/mp4" }
```

An empty body (`{}`) is also valid.

**Response** (`result`)

| Field | Type | Description |
|  --- | --- | --- |
| `upload_url` | string | Presigned URL to upload your video to. Valid for **1 hour**. |
| `upload_fields` | dict | Presigned form fields (`key`, `policy`, `signature`, etc.) that must be included in the upload `POST` to authenticate it. |
| `file_url` | string | The URL your video will be available at after upload. Pass this as the `video` input to any video endpoint. Valid for **1 day**. |


## Step 2 — Upload the file

`POST` the video to `upload_url` as `multipart/form-data`. The `upload_fields` must be sent as form fields **before** the file field — they are validated in order, so the actual file must come last.


```python
with open(video_path, "rb") as f:
    # All presigned fields first, then the file last
    files = {k: (None, v) for k, v in upload_fields.items()}
    files["file"] = (video_path, f, "video/mp4")

    resp = requests.post(upload_url, files=files)
```

A successful upload returns **HTTP 204 No Content**.

## Step 3 — Process the video

Pass `file_url` as the `video` input to any video editing endpoint.


```
POST /v2/video/edit/remove_background
```


```json
{ "video": "<file_url>" }
```

Video editing endpoints are **asynchronous**: a successful call returns `HTTP 202` with a `request_id` and a `status_url`. Poll `status_url` until `status` is `COMPLETED` (or `FAILED`); the finished video is then available at `result.video_url`.


```json
{
  "request_id": "04e104bab7e44319bbf2427d64d8c313",
  "status_url": "https://engine.prod.bria-api.com/v2/status/04e104bab7e44319bbf2427d64d8c313"
}
```

## Limits & expiry

| Constraint | Value |
|  --- | --- |
| Content type | Video files only |
| Max file size | 1 GB |
| `upload_url` validity | 1 hour |
| `file_url` validity | 1 day |


> **Security note:** Both `upload_url` and `file_url` are unauthenticated — anyone holding either can upload to or download from your file. Treat them as secrets and don't share them beyond your processing pipeline.


## Full example

This script runs the complete flow: request a presigned URL, upload a local video, submit it to `remove_background`, and poll until the output video is ready.


```python
import time
import requests

BASE_URL = "https://engine.prod.bria-api.com"
API_TOKEN = "<YOUR_API_TOKEN>"
HEADERS = {"api_token": API_TOKEN}


def get_presigned_url(media_type: str = "video/mp4") -> dict:
    """Step 1 — request a presigned POST URL to upload the video."""
    resp = requests.post(
        f"{BASE_URL}/v2/video/upload",
        json={"media_type": media_type},
        headers=HEADERS,
    )
    resp.raise_for_status()
    return resp.json()["result"]


def upload_video(upload_url: str, upload_fields: dict, video_path: str) -> None:
    """Step 2 — upload the video directly using the presigned POST URL.

    The upload_fields (key, policy, signature, etc.) must be sent as
    multipart form fields *before* the file field — they are validated
    in order, so the file must come last.
    """
    with open(video_path, "rb") as f:
        files = {k: (None, v) for k, v in upload_fields.items()}
        files["file"] = (video_path, f, "video/mp4")
        resp = requests.post(upload_url, files=files)

    # A presigned POST returns 204 No Content on success
    if resp.status_code != 204:
        raise RuntimeError(f"Upload failed: HTTP {resp.status_code}\n{resp.text}")


def submit_remove_background(file_url: str) -> str:
    """Step 3a — submit the uploaded video to a video editing endpoint.

    Video editing endpoints are async — this returns a status_url to poll.
    """
    resp = requests.post(
        f"{BASE_URL}/v2/video/edit/remove_background",
        json={"video": file_url},
        headers=HEADERS,
    )
    resp.raise_for_status()
    return resp.json()["status_url"]


def wait_for_result(status_url: str, interval: float = 5.0, timeout: float = 600.0) -> dict:
    """Step 3b — poll status_url until the job is COMPLETED or FAILED."""
    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        resp = requests.get(status_url, headers=HEADERS)
        resp.raise_for_status()
        data = resp.json()
        if data["status"] == "COMPLETED":
            return data["result"]
        if data["status"] in ("FAILED", "ERROR"):
            raise RuntimeError(f"Job failed: {data}")
        time.sleep(interval)
    raise TimeoutError(f"Job did not complete within {timeout}s")


def main():
    video_path = "path/to/video.mp4"

    upload     = get_presigned_url()
    upload_video(upload["upload_url"], upload["upload_fields"], video_path)
    status_url = submit_remove_background(upload["file_url"])
    result     = wait_for_result(status_url)

    print(result["video_url"])


if __name__ == "__main__":
    main()
```