Ajouter un fichier à la bibliothèque de votre organisation se fait en deux temps :
vous ouvrez un upload avec POST /v1/uploads, puis vous envoyez les
octets à l’adresse qu’il vous renvoie. L’asset existe dès le premier temps —
vous avez son identifiant tout de suite — et devient lisible une fois le fichier
arrivé et traité.
Le fichier est déjà en ligne, dans votre bucket ou sur votre serveur de médias ?
Passez le second temps : donnez-nous son lien,
nous le téléchargeons pour vous. C’est une vidéo publiée sur YouTube, Vimeo ou
Dailymotion ? Il n’y a pas de fichier à ajouter :
POST /v1/assets crée
un asset qui la lit depuis le site de son fournisseur.
Avant de commencer
- Une clé qui peut ajouter des fichiers. Dans l’application Videas, ouvrez Paramètres → Accès API et créez une clé. Sous Ce que la clé peut faire, cochez Ajouter des fichiers à la bibliothèque par l’API : la case est décochée par défaut, pour qu’une clé qui écrit soit toujours créée délibérément. Cochez aussi Lire la bibliothèque par l’API : il vous la faut pour lister les workspaces, suivre l’asset et relancer sans risque.
- Un workspace : l’
idde l’un de ceux que renvoieGET /v1/workspaces. L’asset arrive à sa racine.
Chaque appel part vers https://api.videas.com/v1, avec la clé en jeton Bearer :
Authorization: Bearer ak_…
Attention : la clé reste sur votre serveur.
/v1n’envoie aucun en-tête CORS, un navigateur ne peut donc pas l’appeler de toute façon ; une page qui envoie un fichier utilise le jeton propre à l’upload — voir Depuis le navigateur.
Étape 1 — ouvrir l’upload
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",
"filename": "cours-magistral.mp4",
"size_bytes": 734003200,
"name": "Cours d’ouverture",
"external_ref": "cours-42/seance-1"
}'
| Champ | |
|---|---|
workspace_id |
Obligatoire. Où l’asset arrive. |
filename |
Obligatoire. Le nom du fichier chez vous, extension comprise — c’est d’abord de lui qu’on déduit la nature de l’asset. |
size_bytes |
Obligatoire. La taille exacte du fichier en octets, 10 Gio au plus. |
content_type |
Facultatif. Noté, jamais cru sur parole : le fichier est analysé une fois arrivé. |
name, description, language |
Facultatifs. Le titre de l’asset (le nom du fichier à défaut), sa description et sa langue en balise BCP 47 (fr, en-GB). Le titre fait 500 caractères au plus, la description 50 000. |
external_ref |
Facultatif, et recommandé : votre propre identifiant de l’asset, gardé sans les espaces qui l’entourent et de 500 caractères au plus. C’est lui qui rend une relance sûre. |
La réponse est un 201 :
{
"id": "0192a6b2-4f1d-7e21-b3c8-6d2f0a9e1c57",
"asset_id": "0192a6b2-4f1a-7b90-8e0d-51c3a7f2b046",
"workspace_id": "0192a6b1-7c3e-7d40-9a51-3f0e2b6c8d10",
"source": "client",
"status": "pending",
"size_bytes": 734003200,
"received_bytes": 0,
"upload_url": "https://api.videas.com/api/visitor/uploads/0192a6b2-4f19-7c02-a4e5-9b8d3c1f6e20",
"max_put_bytes": 52428800,
"expires_at": "2026-09-27T09:12:44Z",
"source_url": null,
"failure": null,
"created_at": "2026-09-26T09:12:44Z",
"updated_at": "2026-09-26T09:12:44Z",
"finished_at": null,
"upload_token": "…"
}
asset_iddésigne l’asset, qui existe déjà, à l’étatuploading.iddésigne l’upload : gardez-le pour le suivre.upload_urletupload_tokendisent où et comment envoyer le fichier. Le jeton n’est donné qu’une fois, ici : aucune lecture ultérieure ne le renvoie.upload_urlest opaque — utilisez-la telle quelle, ne la construisez jamais.
Étape 2 — envoyer le fichier
Chaque requête vers upload_url porte le jeton dans X-Upload-Token — jamais
votre clé API.
Jusqu’à max_put_bytes : un seul PUT
curl -T cours-magistral.mp4 "$UPLOAD_URL" -H "X-Upload-Token: $UPLOAD_TOKEN"
# → 204 No Content
# X-Checksum-Sha256: 5e8f… (le SHA-256 de ce qui a été stocké)
Le corps est le fichier entier, Content-Length sa taille exacte — curl s’en
charge — et n’importe quel Content-Type convient. L’upload se termine dans cette
même requête. Si elle est coupée, rien n’est enregistré : renvoyez le même PUT.
Un 409 portant Upload-Offset signifie que l’upload a déjà reçu des octets —
si ce décalage égale la taille, le fichier est déjà là.
Toutes tailles : tus, reprenable
Au-delà de max_put_bytes, envoyez le fichier à la même upload_url avec le
cœur de tus 1.0 : un HEAD indique
où reprendre, chaque PATCH envoie un chunk. Un chunk fait au plus
max_put_bytes, et au moins 5 Mio sauf le dernier. N’importe quel client tus
standard s’en charge, avec l’adresse et l’en-tête — par exemple
tuspy :
from tusclient import client
tus = client.TusClient(upload["upload_url"], headers={"X-Upload-Token": upload["upload_token"]})
uploader = tus.uploader(file_path="cours-magistral.mp4", url=upload["upload_url"], chunk_size=32 * 1024 * 1024)
uploader.upload() # après une coupure, relancez-le : il reprend au décalage du serveur
La réponse au dernier chunk porte elle aussi X-Checksum-Sha256.
Depuis le navigateur
Vos utilisateurs peuvent envoyer leurs fichiers directement de leur navigateur vers Videas, sans que le fichier transite par votre serveur — et sans que votre clé le quitte. Votre serveur ouvre l’upload ; la page ne reçoit que son adresse et son jeton.
1. Votre serveur ouvre l’upload et transmet à la page ce dont elle a besoin :
// serveur — POST /uploads { filename, size }
app.post('/uploads', async (req, res) => {
const answer = await fetch('https://api.videas.com/v1/uploads', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VIDEAS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
workspace_id: process.env.VIDEAS_WORKSPACE_ID,
filename: req.body.filename,
size_bytes: req.body.size,
}),
})
const upload = await answer.json()
if (!answer.ok) return res.status(answer.status).json(upload)
res.json({
upload_url: upload.upload_url,
upload_token: upload.upload_token,
max_put_bytes: upload.max_put_bytes,
})
})
2. La page envoie le fichier — en un seul PUT s’il est assez petit, avec
tus-js-client sinon :
<script type="module">
import * as tus from 'https://cdn.jsdelivr.net/npm/tus-js-client@4/+esm'
const file = input.files[0]
const upload = await (await fetch('/uploads', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: file.name, size: file.size }),
})).json()
if (file.size <= upload.max_put_bytes) {
const sent = await fetch(upload.upload_url, {
method: 'PUT',
headers: { 'X-Upload-Token': upload.upload_token },
body: file,
})
if (sent.status !== 204) console.error('refusé', sent.status)
} else {
new tus.Upload(file, {
uploadUrl: upload.upload_url, // l'upload est déjà ouvert : pas de requête de création
headers: { 'X-Upload-Token': upload.upload_token },
chunkSize: 32 * 1024 * 1024, // au plus max_put_bytes, au moins 5 Mio
retryDelays: [0, 3000, 10000],
onProgress: (sent, total) => console.log(`${Math.round((sent / total) * 100)} %`),
onSuccess: () => console.log('stocké'),
onError: (error) => console.error(error),
}).start()
}
</script>
upload_url accepte les requêtes de n’importe quelle origine. Le jeton ouvre à
qui le détient cet upload-là et rien d’autre, jusqu’à ce que l’upload se termine.
Le suivre jusqu’à ce qu’il soit prêt
Il n’y a pas encore de notification : interrogez l’upload à intervalles réguliers.
curl https://api.videas.com/v1/uploads/$UPLOAD_ID \
-H "Authorization: Bearer $VIDEAS_API_KEY"
status |
Signification |
|---|---|
pending |
Ouvert, rien reçu encore. |
receiving |
Les octets arrivent — received_bytes dit combien. |
completed |
Le fichier est stocké. L’asset passe à processing, puis ready. |
cancelled |
Abandonné : par DELETE /v1/uploads/{id}, ou par la terminaison tus sur upload_url. |
expired |
Le fichier n’était pas complet avant expires_at. |
cancelled et expired sont définitifs, et l’asset finit alors en error. Une
fois l’upload completed, suivez l’asset lui-même avec
GET /v1/assets/{asset_id} : son status passe de uploading à processing,
puis ready — ou error si le fichier est inexploitable. Ne le lisez ni ne
l’intégrez avant ready.
Pour abandonner un upload, DELETE /v1/uploads/{id} répond 204 ; sur un upload
terminé, il répond 409, le fichier appartenant désormais à l’asset.
Relancer sans risque
POST /v1/uploads n’est pas idempotent : envoyé deux fois, il crée deux
assets. Une requête qui a expiré ou répondu une 5xx peut pourtant en avoir créé
un. Donc :
-
renseignez
external_refà chaque upload ; -
après une expiration ou une
5xx, cherchez-le avant de renvoyer la requête — avec une clé qui lit la bibliothèque :curl "https://api.videas.com/v1/assets?external_ref=cours-42%2Fseance-1" \ -H "Authorization: Bearer $VIDEAS_API_KEY" # → { "data": [ … ], "next": null } -
rien trouvé : renvoyez-la. Trouvé : la première requête a bien créé l’asset — mais son jeton était dans la réponse perdue, son fichier ne peut donc plus être envoyé. Ouvrez un nouvel upload ; le premier asset finit en
errorà l’expiration de son upload. Un asset enerrorsans fichier est une tentative abandonnée de ce genre.
Les requêtes qui envoient les octets n’ont besoin de rien de tout cela : un PUT
échoué n’a rien enregistré, et tus reprend là où le serveur l’indique.
Limites
| Limite | Valeur |
|---|---|
| Taille d’un fichier | 10 Gio |
Un PUT, un chunk tus |
max_put_bytes — 50 Mio |
| Un chunk tus, sauf le dernier | 5 Mio au moins |
| Délai pour envoyer le fichier | jusqu’au expires_at de l’upload |
Erreurs
Toutes les erreurs ont le même corps — {"code": "…", "message": "…", "request_id": "…"}.
Testez code ; message s’adresse à un humain et peut changer.
| Statut | code |
À l’ouverture d’un upload |
|---|---|---|
400 |
validation |
size_bytes et source_url ensemble, ou aucun des deux ; pas de filename ; un champ vide ; un name ou une external_ref de plus de 500 caractères, ou une description de plus de 50 000 ; une language mal formée. Le message nomme le champ. |
401 |
unauthorized |
Pas de clé, ou une clé inconnue, expirée ou révoquée. |
403 |
forbidden |
La clé ne peut pas ajouter de fichiers à la bibliothèque. |
404 |
not_found |
Aucun workspace de votre organisation n’a cet identifiant. |
413 |
payload_too_large |
size_bytes dépasse 10 Gio. |
429 |
rate_limited |
Trop de requêtes : attendez le nombre de secondes indiqué par Retry-After. |
Pour aller plus loin
- Ajouter un fichier depuis un lien — quand le fichier est déjà en ligne.
- Ajouter une vidéo hébergée ailleurs — YouTube, Vimeo ou Dailymotion.
- Erreurs, limites et versions — chaque refus, et ce qu’il faut relancer.
- La référence de l’API, générée depuis son contrat.