A video already published on YouTube, Vimeo or Dailymotion does not need to be
downloaded to enter your library: POST /v1/assets creates an asset that plays
it from its provider’s site. The address of its page is all it takes. The asset
is ready at once — there is no file to receive or process — and you can then
edit it and
publish it on a channel like
any other.
A file is added with POST /v1/uploads instead:
sent by you or
fetched from a link.
Before you start
- A key carrying two permissions: Add files to the library through the API
(
assets.library.upload) and Import videos from other platforms (assets.third_party_video.import). Either without the other is a403. Tick Read the library through the API as well, to retry safely. - Videos hosted elsewhere turned on for your organization. It is turned on
organization by organization, on request to Videas: until it is,
POST /v1/assetsanswers404. A key created beforehand works with no change once it is. - A workspace: the
idof one of thoseGET /v1/workspacesreturns, private ones included. The asset lands at its root.
Create the asset
curl -X POST https://api.videas.com/v1/assets \
-H "Authorization: Bearer $VIDEAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "0192a6b1-7c3e-7d40-9a51-3f0e2b6c8d10",
"third_party_url": "https://www.youtube.com/watch?v=Xk3Ua9yQe7M",
"language": "en",
"external_ref": "webtv/2026/carbon-fibre-recycling"
}'
| Field | |
|---|---|
workspace_id |
Required. Where the asset lands. |
third_party_url |
Required. The video’s address, as its page or its share button gives it. |
name |
Optional. Its title — the provider’s when absent. Never blank, and at most 500 characters. |
description |
Optional. Its description — the provider’s when absent, if it gives one. At most 50,000 characters. |
excerpt |
Optional. A short summary, shown under the title where the description would not fit. At most 1,000 characters. |
copyright |
Optional. The notice a reuser has to carry — who holds the rights, under what terms. At most 1,000 characters. |
language |
Optional. The video’s language, as a BCP 47 tag (fr, en-GB). |
external_ref |
Optional, and recommended: your own identifier for the asset, kept without the spaces around it and at most 500 characters. It is what makes a retry safe. |
POST /v1/assets takes no query parameter, and its body no other field.
The answer is a 201, with Location: /v1/assets/{asset_id} and the asset as
GET /v1/assets/{asset_id} reads it:
{
"id": "0192a6e4-3b5c-7d6e-8f90-a1b2c3d4e5f6",
"name": "Recycling carbon fibre: three processes compared",
"type": "third_party",
"status": "ready",
"workspace_id": "0192a6b1-7c3e-7d40-9a51-3f0e2b6c8d10",
"description": "Pyrolysis, solvolysis and mechanical grinding, put side by side on the same production offcuts.",
"external_ref": "webtv/2026/carbon-fibre-recycling",
"language": "en",
"duration_ms": null,
"created_at": "2026-10-15T08:30:12Z",
"updated_at": "2026-10-15T08:30:12Z",
"third_party": {
"provider": "youtube",
"video_id": "Xk3Ua9yQe7M",
"player_url": "https://www.youtube.com/embed/Xk3Ua9yQe7M"
},
"thumbnail_url": "https://app.videas.com/images/assets/0192a6e4-3b5c-7d6e-8f90-a1b2c3d4e5f6/thumbnail.jpg",
"excerpt": null,
"copyright": null,
"seo_title": null,
"seo_description": null,
"tags": [],
"custom_fields": []
}
typeisthird_party, andstatusisreadyfrom the moment it exists.third_partysays which video the asset plays:provider(youtube,vimeoordailymotion),video_id, the video’s identifier at its provider — thevof a YouTube address —, andplayer_url, the address of its player, to put in an<iframe>.thumbnail_urlis the poster the provider gave, if it arrived in time (see below).
The addresses accepted
third_party_url is the address of a video on one of the three sites, in the form
its page or its share button gives:
| Provider | For example |
|---|---|
| YouTube | https://www.youtube.com/watch?v=…, https://youtu.be/… |
| Vimeo | https://vimeo.com/… |
| Dailymotion | https://www.dailymotion.com/video/… |
Nothing else is taken, and everything else answers a 400 that gives the
reason: an embed code (<iframe …>), an address on another site, or a link to a
file — which is added with
POST /v1/uploads.
The provider is asked while your request waits
Before anything is created, Videas asks the provider what the video is, and your request waits for its answer. It is used for three things:
- naming and describing the asset — its title becomes the asset’s
nameunless you send one, its description thedescriptionunless you send one; - checking that the video may be played outside its site: a private, deleted
or non-embeddable video is refused with a
400, and nothing is created; - giving the asset its poster: it becomes its thumbnail if it arrives in time.
Otherwise the asset is created without one, and
PUT /v1/assets/{asset_id}/thumbnailgives it one.
A provider that does not answer in time, or answers with a failure of its
own, is a 502 with the code upstream_error: nothing is created, send the
request again in a moment.
{
"code": "upstream_error",
"message": "YouTube could not be asked about this video; try again in a moment",
"request_id": "0192a6e4-1f2a-7b3c-8d4e-5f6a7b8c9d0e"
}
Finding your videos again
third_party is on every row of GET /v1/assets, not only on the read of one
asset. To match a library’s videos with your own records — including those added
before you set external_refs —, walk the assets of type third_party and
compare third_party.video_id with the video’s identifier on your side:
import os
import requests
API = "https://api.videas.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['VIDEAS_API_KEY']}"}
def videos_by_provider_id():
"""{(provider, video_id): asset} for every video hosted elsewhere."""
found, params = {}, {"type": "third_party"}
while True:
page = requests.get(f"{API}/assets", headers=HEADERS, params=params)
page.raise_for_status()
body = page.json()
for asset in body["data"]:
video = asset["third_party"]
if video and video["video_id"]:
found[(video["provider"], video["video_id"])] = asset
if body["next"] is None:
return found
params["after"] = body["next"]
provider may also be other — a player from another site, added from the
Videas app — and then has no video_id. The list of providers may grow: read
a value you do not know as other.
Retrying safely
POST /v1/assets is not idempotent: sent twice, it creates two assets. A
request that timed out or answered a 5xx may have created one. So:
-
set
external_refon every request; -
after a timeout or a
5xx, look it up before sending the request again — with a key that reads the library:curl "https://api.videas.com/v1/assets?external_ref=webtv%2F2026%2Fcarbon-fibre-recycling" \ -H "Authorization: Bearer $VIDEAS_API_KEY" -
nothing found: send it again. Found: it is the asset the first request created, and it is complete — ready from the moment it exists. There is nothing to resume.
A 502 created nothing: it is sent again as it is.
Errors
Every error has the same body — {"code": "…", "message": "…", "request_id": "…"}.
Branch on code; message is written for a human and may change.
| Status | code |
On POST /v1/assets |
|---|---|---|
400 |
validation |
A third_party_url that is not a YouTube, Vimeo or Dailymotion video; a private, deleted or non-embeddable video; a blank name or external_ref; a name or an external_ref longer than 500 characters, an excerpt or a copyright longer than 1,000, or a description longer than 50,000; a malformed language; a query parameter. The message names the field. |
401 |
unauthorized |
No key, or a key that is unknown, expired or revoked. |
403 |
forbidden |
The key does not carry both assets.library.upload and assets.third_party_video.import. |
404 |
not_found |
Videos hosted elsewhere are not turned on for your organization — ask Videas —, or no workspace of your organization has this workspace_id. |
415 |
unsupported_media_type |
The body is not sent as application/json. |
422 |
validation |
workspace_id or third_party_url missing, a field of the wrong type, or a field the body does not list. |
429 |
rate_limited |
Too many requests: wait the number of seconds Retry-After gives. |
502 |
upstream_error |
The provider did not answer in time, or answered with a failure. Nothing is created: send the request again in a moment. |
Next steps
- Edit an asset — its words and its tags, once created.
- An asset’s thumbnail — when the provider’s poster did not arrive, or does not suit.
- Publish on a channel — with an end-to-end example that starts from a YouTube video.
- The API reference, generated from its contract.