If the file is already online — in a bucket, on a CDN, on your media server — you
don’t need to send it: give us its link, and we download it into your library in
the background. It is the same resource as
a file you send, POST /v1/uploads,
with source_url in place of size_bytes.
Open the upload with a link
curl -X POST https://api.videas.com/v1/uploads \
-H "Authorization: Bearer $VIDEAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "0192a6b1-7c3e-7d40-9a51-3f0e2b6c8d10",
"source_url": "https://media.example.com/exports/lecture.mp4?X-Amz-Expires=86400&X-Amz-Signature=…",
"sha256": "5e8f4c0d9b1a…",
"external_ref": "course-42/lecture-1"
}'
The key, the workspace and the optional name, description, language and
external_ref are as for a file you send.
Then:
| Field | |
|---|---|
source_url |
Required. The link to the file. |
filename |
Optional. Without it, the file is named from the server’s Content-Disposition, else from the link’s last segment. |
sha256 |
Optional. The file’s SHA-256, as 64 hexadecimal characters: the file we download must match it. |
The answer is a 201 — and there is nothing to send: no address, no token.
{
"id": "0192a6c0-1e2f-7a34-9b56-7c8d9e0f1a2b",
"asset_id": "0192a6c0-1e2b-7c01-8d23-4e5f6a7b8c9d",
"workspace_id": "0192a6b1-7c3e-7d40-9a51-3f0e2b6c8d10",
"source": "link",
"status": "pending",
"size_bytes": null,
"received_bytes": 0,
"upload_url": null,
"max_put_bytes": null,
"expires_at": null,
"source_url": "https://media.example.com/exports/lecture.mp4",
"failure": null,
"created_at": "2026-09-26T09:20:03Z",
"updated_at": "2026-09-26T09:20:03Z",
"finished_at": null,
"upload_token": null
}
What a link must be
httporhttps, on the scheme’s own port: 80 forhttp, 443 forhttps.- No user name or password in it —
https://user:secret@…is refused. - On a public server: not
localhost, not a private or reserved address. - The file itself, not a page about it. A YouTube, Vimeo or Dailymotion page
is refused at once: it is a player, not a file, and
POST /v1/assetscreates the asset that plays it. A web page (text/html) where the file should be failsnot_a_file. - At most 10 GiB.
What can be seen in the link is refused at once, with a 400 naming the reason.
The rest is found by the download.
How we download it: GET requests carrying
User-Agent: Videas-Fetch/… (+https://videas.com/bot; support@videas.com), at
most five redirects followed — never from https to http — and a download that
broke off resumed with a Range request from where it stopped. A server that
ignores Range has the file read again from the start.
Allow our addresses
Links are fetched from these four addresses, and from no others. If your server, your firewall or your bucket’s policy only answers known addresses, allow all four — either family may be used:
| Addresses | |
|---|---|
| IPv4 | 193.104.118.42, 193.104.118.43 |
| IPv6 | 2a12:3440:102::3:42, 2a12:3440:102::3:43 |
Warning: allowing these addresses is not a credential. They fetch the links of every Videas customer, so a file readable by these addresses alone is readable by anyone able to hand us its URL. Keep the link signed — a presigned URL — and use the addresses to narrow access, never to open it.
A presigned link is a credential, and we treat it as one
- It is sealed — encrypted — while the download needs it, and erased when the upload ends.
- It is never shown back whole:
source_urlin our answers is your link without its query, so without its signature. Our logs name it the same way. - Sign it for long enough. The download may wait its turn and be retried from
where it stopped; a signature that expired meanwhile fails the upload
refused. Sign it for longer than the download may take, retries included.
Check the file with sha256
Give the file’s SHA-256 and we check the file against it once it is downloaded.
A copy that does not match is downloaded once more, from the start; if it still
does not match, the upload fails checksum_mismatch. Over plain http, it is the
only thing that proves the file is the one you meant.
Follow the download
There is no notification yet: poll GET /v1/uploads/{id}.
status |
Meaning |
|---|---|
pending |
Queued: the download has not started. |
receiving |
Downloading. received_bytes grows, and size_bytes appears once the server has said. |
completed |
The file is stored. The asset goes on to processing, then ready. |
failed |
The download failed, and failure.code says why. |
cancelled |
Given up on with DELETE /v1/uploads/{id} — or the asset was deleted before the file arrived. |
expired |
Rare: a download nobody was fetching any more. |
failed, cancelled and expired are final, and the asset then ends error. A
failed upload says why:
{
"status": "failed",
"failure": {
"code": "refused",
"message": "the link's server refused the request: answered 403 Forbidden"
}
}
Branch on failure.code; message is for a person and may change.
failure.code |
What happened | What to do |
|---|---|---|
address_refused |
We do not connect to where the link leads: an address of ours, or a private or reserved one. Over https, it is also the answer when the connection could not be opened, twice in a row — a name that does not resolve, a server that refuses us. |
Check the link’s host, and that it accepts connections from our addresses. |
unreachable |
No answer, after trying again for a while: over http, a name that does not resolve or a failed connection; a TLS failure or a timeout; a server that kept answering 5xx or 429. |
Check that the server is up and reachable from our addresses. |
not_found |
404 or 410. |
Fix the link. |
refused |
Any other 4xx — typically a presigned link that expired — or a redirect we do not follow: a sixth one, or from https to http. |
Sign the link for longer, or link the final address. |
not_a_file |
A web page where the file should be. | Link the file itself. |
too_large |
Larger than 10 GiB. | — |
interrupted |
The download broke off more times than it is retried. | Check the server’s stability, then try again. |
checksum_mismatch |
Downloaded twice, and twice not the sha256 you gave. |
Check the digest, or the file behind the link. |
Each is final: once the cause is fixed, open a new upload. A code you don’t know is a failure your code does not handle yet — the list may grow.
Limits and retries
- At most 10 GiB per file.
- An organization may only have so many links waiting or downloading at once.
Past that, the request is refused with a
429andRetry-After: wait that many seconds, then send it again. An upload that has ended frees its place. - Retrying after a timeout or a
5xxworks as for a file you send: setexternal_ref, and look it up before sending again. A link found that way is already being downloaded — don’t send it again. - To give up,
DELETE /v1/uploads/{id}: the download stops at its next part, the link is forgotten, and the asset endserror.
Next steps
- Add a file with the API — sending the file yourself, from a server or a browser.
- Add a video hosted elsewhere — when the link is a YouTube, Vimeo or Dailymotion page.
- Errors, limits and versions — every refusal, and what to retry.
- The API reference, generated from the API’s contract.