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:
- Request a presigned upload URL — call
POST /v2/video/uploadto get anupload_url, theupload_fieldsneeded to authenticate the upload, and thefile_urlyour video will live at. - Upload the file —
POSTthe video directly toupload_urlas multipart form data. - Process the video — pass
file_urlas thevideoinput 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.
POST /v2/video/uploadBody (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. |
{ "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. |
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.
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.
Pass file_url as the video input to any video editing endpoint.
POST /v2/video/edit/remove_background{ "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.
{
"request_id": "04e104bab7e44319bbf2427d64d8c313",
"status_url": "https://engine.prod.bria-api.com/v2/status/04e104bab7e44319bbf2427d64d8c313"
}| 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_urlandfile_urlare 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.
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.
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()