You can put a video on UFOLOAD without ever opening the dashboard. The REST API accepts uploads over HTTPS, the server can fetch a file from a link for you, and the same account is reachable over WebDAV and FTP for tools that already speak those protocols. This guide goes through all five routes with working commands, explains what the server answers, and shows how to wait for encoding to finish. The endpoints below were checked against the public OpenAPI specification; the API documentation has the full reference.
Create an API key
Create a key in the Developers section of your dashboard. Every request carries it as a bearer token in the Authorization header. The key is displayed only once when it is created, so store it in an environment variable or a secrets manager and revoke it from the dashboard if it leaks. The same key works as the password for WebDAV and FTP, with your account email as the login. If you do not have an account yet, create a free one; the pricing page lists the file size limit and the storage and traffic quotas that apply to your plan.
export UFOLOAD_API_KEY="ufo_..."Upload with curl
The simplest upload sends the file as the raw request body and puts its name into the X-File-Name header. Send the name percent-encoded (UTF-8) if it contains spaces or non-ASCII characters. By default the file lands in the root folder; add ?folder_id= or an X-Folder-Id header to choose another one.
curl -X POST "https://ufoload.com/api/v1/files?folder_id=root" \
-H "Authorization: Bearer $UFOLOAD_API_KEY" \
-H "X-File-Name: holiday-2026.mp4" \
-H "Content-Type: video/mp4" \
--data-binary @holiday-2026.mp4A multipart form works as well. The file goes into the file field and the folder can be given as a folder_id form field:
curl -X POST "https://ufoload.com/api/v1/files" \
-H "Authorization: Bearer $UFOLOAD_API_KEY" \
-F "file=@holiday-2026.mp4"A successful upload answers 201 with a JSON body that contains the file object. The fields you will use most are id, public_id (the short code that goes into player addresses), status, duration_ms and max_quality. The server does not use extra space for content it already has: if you upload the same bytes again, it answers 200 with the existing file and "duplicate": true, matching files by their SHA-256 hash.
| Status | Meaning |
|---|---|
201 | The file was accepted and queued for processing |
200 | The same content is already in your library; duplicate is true |
401 | The key is missing or invalid |
404 | The destination folder does not exist |
409 | Not enough storage |
413 | The file is larger than your plan allows |
429 | Too many requests; wait and retry |
Large uploads are not subject to the short request timeouts that apply to ordinary API calls. The server accepts the body for as long as it keeps arriving, so a slow connection does not break a multi-gigabyte upload, though a pause between blocks that is too long can still be cut by the reverse proxy.
Wait until the video is ready
Uploading is only the first half. After it, the file is queued and encoded into an adaptive HLS ladder (see how the ladder is built). Its status moves through uploading, queued and processing and ends at ready or failed; a failed file carries an error message. Poll the file every few seconds, and never in a tight loop: the API is rate limited and answers 429.
curl "https://ufoload.com/api/v1/files/FILE_ID" \
-H "Authorization: Bearer $UFOLOAD_API_KEY"The answer has two parts: file with the metadata, and links with signed addresses for the master playlist (playback), the cover (poster), the hover-preview sprite (sprite), every rendition and expires_at. If you need a different lifetime, POST /files/{id}/links issues links valid from 60 seconds to 30 days, optionally bound to an IP address or a cookie.
Python
With the requests library, passing the open file object streams it from disk, so memory stays flat even for large videos. The function below uploads, then waits for the result.
import os
import time
import urllib.parse
import requests
BASE = "https://ufoload.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['UFOLOAD_API_KEY']}"}
def upload(path, folder_id="root"):
name = urllib.parse.quote(os.path.basename(path))
with open(path, "rb") as f:
r = requests.post(
f"{BASE}/files",
params={"folder_id": folder_id},
data=f,
headers={**HEADERS, "X-File-Name": name, "Content-Type": "application/octet-stream"},
)
r.raise_for_status()
return r.json()["file"]
def wait_ready(file_id, timeout=3600):
deadline = time.time() + timeout
while time.time() < deadline:
r = requests.get(f"{BASE}/files/{file_id}", headers=HEADERS)
r.raise_for_status()
data = r.json()
status = data["file"]["status"]
if status == "ready":
return data
if status == "failed":
raise RuntimeError(data["file"].get("error") or "encoding failed")
time.sleep(5)
raise TimeoutError("video is still processing")
file = upload("holiday-2026.mp4")
data = wait_ready(file["id"])
print("embed:", f"https://ufoload.com/e/{file['public_id']}")
print("playlist:", data["links"]["playback"])Node.js
Node.js 18 and newer has fetch built in, so no dependencies are needed. This version reads the file into memory, which is fine for clips and short videos; for multi-gigabyte files prefer curl, WebDAV or FTP, which stream from disk.
import { readFile } from 'node:fs/promises'
import { basename } from 'node:path'
import { setTimeout as sleep } from 'node:timers/promises'
const BASE = 'https://ufoload.com/api/v1'
const headers = { Authorization: `Bearer ${process.env.UFOLOAD_API_KEY}` }
async function upload(path) {
const res = await fetch(`${BASE}/files?folder_id=root`, {
method: 'POST',
headers: { ...headers, 'X-File-Name': encodeURIComponent(basename(path)), 'Content-Type': 'application/octet-stream' },
body: await readFile(path),
})
if (!res.ok) throw new Error(`upload failed: ${res.status}`)
return (await res.json()).file
}
async function waitReady(id) {
for (;;) {
const res = await fetch(`${BASE}/files/${id}`, { headers })
if (!res.ok) throw new Error(`status check failed: ${res.status}`)
const data = await res.json()
if (data.file.status === 'ready') return data
if (data.file.status === 'failed') throw new Error(data.file.error || 'encoding failed')
await sleep(5000)
}
}
const file = await upload('holiday-2026.mp4')
const data = await waitReady(file.id)
console.log('embed:', `https://ufoload.com/e/${file.public_id}`)
console.log('playlist:', data.links.playback)Import by URL
If the video is already online at a direct link, let the server download it instead of pulling it to your machine and pushing it again. The call returns 202 right away with an import task; the server fetches the file and then queues it for encoding. Links to internal networks, loopback and service address ranges are rejected, you can have up to ten active imports, and GET /files/import shows progress.
curl -X POST "https://ufoload.com/api/v1/files/import" \
-H "Authorization: Bearer $UFOLOAD_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/video.mp4", "name": "video.mp4"}'WebDAV and FTP
For bulk transfers and for tools that cannot call an API, the same library is available over WebDAV and FTP. The login is your account email and the password is an API key. Both stream from disk and are the most robust option for very large files.
curl -u "you@example.com:$UFOLOAD_API_KEY" https://ufoload.com/dav/
curl -u "you@example.com:$UFOLOAD_API_KEY" -T holiday-2026.mp4 https://ufoload.com/dav/holiday-2026.mp4curl -u "you@example.com:$UFOLOAD_API_KEY" ftp://ufoload.com/
curl -u "you@example.com:$UFOLOAD_API_KEY" --ftp-ssl -T holiday-2026.mp4 ftp://ufoload.com/Use the encrypted variants whenever your client supports them, since the API key is the password. Any client that speaks WebDAV or FTPS, from file managers to sync tools, can use the same details. Files copied this way appear in your library and are encoded like any other upload.
Which route to choose
| Route | Best for | Watch out for |
|---|---|---|
| API upload | Applications and backends that upload on behalf of users | Memory use if the client reads the whole file first |
| Import by URL | Files that already have a public direct link | Only direct http(s) links; limit of ten active imports |
| WebDAV | Large files, mounting the library as a drive | Use your email and API key as credentials |
| FTP / FTPS | Existing tools and scripts, bulk transfer | Use the explicit TLS option |
Use the video
Once the status is ready, the player address is https://ufoload.com/e/ followed by public_id, which is exactly what you put in an iframe (the long id works too and is redirected to the short address); the embedding guide shows the markup and the hls.js alternative. You can restrict where it plays with domain and country rules, and enable ads to earn from views. Keep the key out of browser code, rotate it when people leave your team, and remember that the API is available on every plan, so you can start building today.
Upload a video, get an HLS player and an API key in minutes. Plan limits are listed on the pricing page.