Une chaîne est un site public où votre organisation publie des vidéos pour son audience. L’API en lit trois choses et en écrit une :
| Ressource | Ce qu’elle est | Lecture | Écriture |
|---|---|---|---|
GET /v1/channels |
les chaînes de l’organisation | websites.catalogue.read |
— |
GET /v1/categories |
l’arbre de catégories de chaque chaîne | websites.catalogue.read |
depuis Videas |
GET /v1/publications |
ce que chaque chaîne publie | websites.catalogue.read |
websites.catalogue.publish |
Les deux permissions s’appellent, dans l’écran des clés, Lire les chaînes par l’API et Publier sur les chaînes par l’API. Aucune n’implique l’autre, ni aucune permission de la bibliothèque : une clé qui publie ne modifie pas un asset, et ne lit pas la bibliothèque.
Avant de commencer
- Votre organisation doit publier des chaînes. Pour une organisation qui n’en
publie pas, chaque endpoint de cette page répond
403à toutes ses clés — comme à une clé sans ces permissions. Une clé peut les porter d’avance : elle fonctionne sans rien changer le jour où l’organisation a ses chaînes. - Une organisation dont les chaînes sont en cours de transfert vers Videas les
lit normalement, mais chaque écriture y est refusée par un
409jusqu’à la fin du transfert : elles se modifient depuis la plateforme d’où elles viennent tant qu’il n’est pas terminé. - Les assets à publier sont dans la bibliothèque : un fichier ajouté et traité, ou une vidéo hébergée ailleurs.
Les chaînes
curl https://api.videas.com/v1/channels \
-H "Authorization: Bearer $VIDEAS_API_KEY"
{
"data": [
{
"id": "0192a6a0-5d6e-7f80-9a1b-2c3d4e5f6a7b",
"name": "Matériaux TV",
"url": "https://webtv.example.com",
"language": "fr",
"created_at": "2026-02-10T09:00:00Z",
"updated_at": "2026-09-30T16:12:45Z"
}
],
"next": null
}
| Champ | |
|---|---|
id |
L’identifiant de la chaîne — ce que nomment le channel_id d’une catégorie et celui d’une publication. |
name |
Son nom, tel que le lit son audience. |
url |
L’adresse de sa page d’accueil : son domaine principal, suivi d’un segment de langue comme /fr quand ce domaine sert plusieurs chaînes, une par langue. Toutes les adresses de la chaîne commencent par elle. null tant qu’aucun domaine de la chaîne n’est activé. |
language |
La langue dans laquelle elle est écrite, en balise BCP 47. |
created_at, updated_at |
Sa création et sa dernière modification. Un changement de ses seuls domaines, qui déplace son url, ne fait pas bouger updated_at. |
La liste porte toutes les chaînes de l’organisation, en ligne ou non : url
dit où la chaîne répond une fois en ligne, pas si elle l’est. GET /v1/channels/count
les compte, et GET /v1/channels/{channel_id} en lit une.
Les catégories
Chaque chaîne range ses vidéos dans un arbre de catégories, qui s’organise
depuis Videas et se lit ici. GET /v1/categories?channel_id= liste celles d’une
chaîne — sans channel_id, celles de toutes les chaînes :
curl "https://api.videas.com/v1/categories?channel_id=0192a6a0-5d6e-7f80-9a1b-2c3d4e5f6a7b" \
-H "Authorization: Bearer $VIDEAS_API_KEY"
{
"data": [
{
"id": "0192a6a1-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
"channel_id": "0192a6a0-5d6e-7f80-9a1b-2c3d4e5f6a7b",
"parent_id": null,
"name": "Procédés",
"slug": "procedes",
"position": 0,
"is_active": true,
"url": "https://webtv.example.com/categories/procedes/",
"created_at": "2026-02-10T09:12:00Z",
"updated_at": "2026-02-10T09:12:00Z"
},
{
"id": "0192a6a1-2b3c-7d4e-9f5a-6b7c8d9e0f1a",
"channel_id": "0192a6a0-5d6e-7f80-9a1b-2c3d4e5f6a7b",
"parent_id": "0192a6a1-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
"name": "Recyclage",
"slug": "recyclage",
"position": 1,
"is_active": true,
"url": "https://webtv.example.com/categories/recyclage/",
"created_at": "2026-02-10T09:12:40Z",
"updated_at": "2026-02-10T09:12:40Z"
},
{
"id": "0192a6a1-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
"channel_id": "0192a6a0-5d6e-7f80-9a1b-2c3d4e5f6a7b",
"parent_id": null,
"name": "Salons",
"slug": "salons",
"position": 1,
"is_active": true,
"url": "https://webtv.example.com/categories/salons/",
"created_at": "2026-02-10T09:13:05Z",
"updated_at": "2026-02-10T09:13:05Z"
}
],
"next": null
}
| Champ | |
|---|---|
parent_id |
La catégorie sous laquelle elle se trouve, de la même chaîne ; null à la racine. |
position |
Sa place parmi les catégories de même parent, la plus basse d’abord. Pas unique : à position égale, l’ordre est celui des noms. |
slug |
Le nom sur lequel son adresse est construite, unique dans sa chaîne. |
is_active |
Si l’audience la voit. Une catégorie désactivée est masquée avec tout ce qu’elle contient ; elle garde ce qui y est classé et le remontre une fois réactivée. |
url |
L’adresse de sa page : l’url de la chaîne suivie de /categories/{slug}/ ; null quand celle de la chaîne l’est. Elle répond 404 tant que la catégorie, ou l’une de celles au-dessus, est désactivée. |
updated_at |
Sa dernière modification, déplacement sous un autre parent compris. |
Reconstruire l’arbre
La liste est plate, dans l’ordre de création des catégories — ce n’est pas
l’ordre dans lequel la chaîne les montre. Reconstruisez l’arbre à partir de
parent_id, et triez les catégories de même parent par position, puis par
name :
from collections import defaultdict
def category_tree(categories):
"""Les catégories d'une chaîne en arbre : [(catégorie, [sous-arbre…]), …]."""
children = defaultdict(list)
for category in categories:
children[category["parent_id"]].append(category)
def branch(parent_id):
siblings = sorted(children[parent_id], key=lambda c: (c["position"], c["name"]))
return [(category, branch(category["id"])) for category in siblings]
return branch(None)
Une catégorie désactivée figure dans la liste, et une publication peut y être
classée — c’est ainsi qu’une équipe prépare une rubrique avant de l’ouvrir.
channel_id ne prend qu’un identifiant ; GET /v1/categories/count compte
sous le même filtre.
Les publications
Une publication est un asset publié sur une chaîne, à sa propre adresse, et classé dans les catégories de cette chaîne. Un asset est publié au plus une fois par chaîne.
{
"id": "0192a6e5-4c5d-7e6f-9a0b-1c2d3e4f5a6b",
"channel_id": "0192a6a0-5d6e-7f80-9a1b-2c3d4e5f6a7b",
"asset_id": "0192a6e4-3b5c-7d6e-8f90-a1b2c3d4e5f6",
"slug": "recycler-la-fibre-de-carbone-trois-procedes-compares",
"url": "https://webtv.example.com/recycler-la-fibre-de-carbone-trois-procedes-compares/",
"status": "published",
"published_at": "2026-10-15T08:30:21Z",
"is_listed": true,
"access": "public",
"category_ids": [
"0192a6a1-2b3c-7d4e-9f5a-6b7c8d9e0f1a",
"0192a6a1-1a2b-7c3d-8e4f-5a6b7c8d9e0f"
],
"primary_category_id": "0192a6a1-2b3c-7d4e-9f5a-6b7c8d9e0f1a",
"created_at": "2026-10-15T08:30:21Z",
"updated_at": "2026-10-15T08:30:21Z"
}
| Champ | |
|---|---|
channel_id, asset_id |
La chaîne et l’asset. Ils ne changent jamais. |
slug, url |
Son adresse : l’url de la chaîne suivie de /{slug}/ ; null quand celle de la chaîne l’est. |
status, published_at |
Où elle en est, et quand elle passe sur la chaîne — voir Statut et programmation. |
access |
À qui elle est destinée — voir Qui y a accès. |
is_listed |
Si la chaîne la montre dans son catalogue, ses catégories, sa recherche et son plan de site. Une publication non listée répond quand même à son url. |
category_ids |
Les catégories où elle est classée — un ensemble : sa catégorie principale vient d’abord quand elle en a une, les autres suivent dans l’ordre où GET /v1/categories les liste. Vide si elle n’est classée nulle part. |
primary_category_id |
Sa catégorie principale, celle que nomment sa page et sa carte — l’une de category_ids ; null si elle n’en a pas. |
updated_at |
La dernière modification de ses propres réglages — son adresse, son statut, sa date, sa visibilité dans les listes, son accès. La classer dans d’autres catégories ne le fait pas bouger. |
Lister et retrouver
GET /v1/publications liste les publications de toutes les chaînes, quel que soit
leur statut, page par page, et GET /v1/publications/count les compte sous les
mêmes filtres :
| Paramètre | Garde | Exemple |
|---|---|---|
channel_id |
les publications de cette chaîne | ?channel_id=0192a6a0-… |
asset_id |
les publications de cet asset — où il est publié | ?asset_id=0192a6e4-… |
status |
ces statuts, séparés par des virgules | ?status=draft,published |
?asset_id= dit où un asset est publié ; avec channel_id à côté, s’il est
publié sur cette chaîne — la réponse porte alors une publication ou aucune.
Seules figurent les publications dont l’asset est dans GET /v1/assets.
Statut et programmation
status |
Signification |
|---|---|
draft |
Pas sur la chaîne. |
published |
Sur la chaîne à partir de son published_at — qui peut être à venir : une publication programmée est published avec une date future, et la chaîne la montre à partir de cet instant. |
archived |
Retirée de la chaîne, et conservée avec sa date. |
published_at vaut null pour un brouillon jamais publié ; un brouillon ou une
publication archivée garde la date de sa dernière sortie. Son url répond 404
tant qu’elle n’est pas published et que son published_at n’est pas arrivé.
La liste des statuts peut s’allonger : lisez une valeur inconnue comme une
publication que la chaîne ne montre pas.
Qui y a accès
access dit à qui la publication est destinée. Il se lit en cinq mots :
access |
Pour qui |
|---|---|
public |
quiconque la chaîne laisse entrer |
authenticated |
quiconque s’est connecté à la chaîne, ou a donné son mot de passe |
restricted |
les seuls membres à qui elle a été accordée |
paid |
les membres qui l’ont achetée, ou dont l’abonnement la couvre |
on_request |
les membres qui l’ont demandée et l’ont obtenue |
Et il s’écrit en deux : public et authenticated. Les trois autres
correspondent à un droit ou à une offre réglés depuis Videas : une requête ne peut
ni en poser un (400), ni en faire sortir une publication (409), puisque la
quitter déferait l’offre ou l’accès accordé. La liste peut s’allonger : lisez
une valeur inconnue comme restricted.
- C’est le réglage propre de la publication, pas ce qu’obtient un visiteur :
sur une chaîne qui demande à chaque visiteur de se connecter, une publication
publicreste derrière cette connexion. - Sans
accessà la création, la chaîne décide : sur une chaîne dont les visiteurs ouvrent eux-mêmes un compte et dont le site est par ailleurs ouvert, une nouvelle publication demande ce compte (authenticated) ; sur toute autre, elle est ouverte à tous ceux que la chaîne laisse entrer (public). Envoyezaccesssi vous voulez être sûr du résultat. - Créée
authenticated— que la requête l’ait demandé ou que la chaîne en ait décidé —, une publication se montre quand même, verrouillée, aux visiteurs à qui elle n’est pas destinée — dans les listes de la chaîne si elle est listée, et à sa page —, et la lire leur demande de se connecter. Passée àauthenticatedplus tard par unPATCH, une publication crééepublicne se montre pas ainsi : ces visiteurs ne la voient pas du tout. UnPATCHne déplace que le niveau d’accès, rien d’autre.
Son adresse
slug est le nom sur lequel l’url de la publication est construite : des mots
en minuscules et en chiffres reliés par des tirets simples, 255 caractères au
plus, unique parmi les publications de la chaîne.
- Absent à la création, il est tiré de l’adresse propre de l’asset s’il en a
une, sinon de son titre, suivi de
-2,-3, etc. là où l’adresse est déjà prise. - Envoyé, il est pris tel quel :
400si le site de la chaîne répond lui-même à cette adresse,409si une autre publication ou une page de la chaîne la tient — le message nomme l’adresse. - Un nouveau
sluglaisse l’ancienne adresse répondre, en renvoyant les visiteurs vers la nouvelle.
Son classement
category_ids liste les catégories de la chaîne où la publication est classée, et
primary_category_id sa catégorie principale, qui doit en faire partie.
- 100 catégories au plus, toutes de la chaîne de la publication — désactivées comprises ; un identifiant envoyé deux fois compte une fois.
- C’est un ensemble. À la lecture, son ordre ne dit qu’une chose : la catégorie principale vient d’abord. À l’envoi, l’ordre est ignoré.
- Envoyée, la liste remplace l’ensemble. Une catégorie où la publication reste
garde la place et la mise en avant que l’équipe lui y a données dans Videas ; une
catégorie où elle entre la place en dernier ;
[]ne la classe nulle part. - L’ordre à l’intérieur d’une catégorie et la mise en avant se règlent dans Videas, pas par l’API.
Publier un asset
curl -X POST https://api.videas.com/v1/publications \
-H "Authorization: Bearer $VIDEAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel_id": "0192a6a0-5d6e-7f80-9a1b-2c3d4e5f6a7b",
"asset_id": "0192a6e4-3b5c-7d6e-8f90-a1b2c3d4e5f6",
"status": "published",
"access": "public",
"category_ids": [
"0192a6a1-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
"0192a6a1-2b3c-7d4e-9f5a-6b7c8d9e0f1a"
],
"primary_category_id": "0192a6a1-2b3c-7d4e-9f5a-6b7c8d9e0f1a"
}'
| Champ | |
|---|---|
channel_id |
Obligatoire. La chaîne. |
asset_id |
Obligatoire. L’asset. Il doit être ready, et une chaîne publie une vidéo, un son, une image ou une vidéo hébergée ailleurs — pas un document. |
status |
draft, la valeur par défaut, ou published. Une publication ne naît jamais archived. |
published_at |
Avec status: published, quand elle passe sur la chaîne (RFC 3339) — une date future la programme. Absente : maintenant. Refusée sur un brouillon. |
access |
public ou authenticated. Absent : le choix de la chaîne. |
slug |
Son adresse. Absent : tirée de l’asset. |
is_listed |
Si la chaîne la montre dans ses listes — true sauf si vous envoyez false. |
category_ids, primary_category_id |
Son classement — chaque catégorie la reçoit en dernier. Absents : classée nulle part, sans catégorie principale. |
La réponse est un 201, avec Location: /v1/publications/{publication_id} et la
publication entière, comme plus haut.
- Un asset pas encore
ready— un fichier encore en traitement — vaut un409: renvoyez la requête une fois l’asset prêt. Une vidéo hébergée ailleurs est prête dès sa création. - Un asset déjà publié sur cette chaîne vaut un
409lui aussi :GET /v1/publications?channel_id=…&asset_id=…répond sa publication.
Modifier une publication
PATCH /v1/publications/{publication_id} écrit ce que vous envoyez, et laisse le
reste tel quel. Sa chaîne et son asset ne changent jamais : les envoyer vaut un
422.
# La programmer pour le 2 novembre à 9 h, heure de Paris
curl -X PATCH https://api.videas.com/v1/publications/0192a6e5-4c5d-7e6f-9a0b-1c2d3e4f5a6b \
-H "Authorization: Bearer $VIDEAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "published", "published_at": "2026-11-02T08:00:00Z"}'
status: publishedla met sur la chaîne — à la date envoyée, sinon maintenant : une publication retirée puis remise revient comme nouvelle, et non à la date de sa première sortie.draftouarchivedl’en retire, en gardant sa date.published_atenvoyé seul déplace la date d’une publication déjà publiée ; sur une autre, ou à côté destatus: draftouarchived, il vaut un400.slugla renomme, et l’ancienne adresse renvoie vers la nouvelle.category_idsremplace son classement ;primary_category_idchange sa catégorie principale, ou l’enlève avecnull. Absent, la catégorie principale reste la même tant que la publication y est encore classée, et il n’y en a plus sinon.accessla déplace entrepublicetauthenticated.is_listedla montre dans les listes de la chaîne, ou l’en retire.- Seul
primary_category_idacceptenull: sur tout autre champ,nullvaut un400.
La réponse est la publication entière. Un PATCH qui ne change rien n’écrit
rien, updated_at compris, et la dernière requête arrivée l’emporte.
Retirer une publication
Rien ne supprime une publication. Pour la retirer de la chaîne, archivez-la —
{"status": "archived"} — : elle est conservée avec tout ce qui s’y rattache, et
se republie d’un PATCH. Pour libérer son adresse, donnez-lui un autre slug.
Relancer sans risque
PATCHse renvoie tel quel après une expiration ou une5xx: envoyé deux fois, le second trouve la publication déjà dans l’état demandé.POST /v1/publicationspublie un asset une seule fois sur une chaîne : renvoyé, il est refusé par un409. Après une expiration ou une5xx, cherchez la publication que la première requête a pu créer avecGET /v1/publications?channel_id=…&asset_id=…avant de la renvoyer.
De bout en bout
Ce script synchronise une vidéo d’un tableur vers une chaîne : il crée la vidéo YouTube dans la bibliothèque, la tague, remplit un champ « Lien utile », et la publie dans deux catégories. Lancé une seconde fois, il ne change rien : chaque étape cherche d’abord ce qui existe, et chaque écriture dit l’état voulu plutôt qu’un changement.
Il lui faut une clé qui porte six permissions : lire la bibliothèque, y ajouter des fichiers, importer des vidéos d’autres plateformes, modifier ses contenus, lire les chaînes et y publier.
import os
import requests
API = "https://api.videas.com/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['VIDEAS_API_KEY']}"
WORKSPACE_ID = "0192a6b1-7c3e-7d40-9a51-3f0e2b6c8d10"
CHANNEL_ID = "0192a6a0-5d6e-7f80-9a1b-2c3d4e5f6a7b"
USEFUL_LINK_FIELD_ID = "0192a680-1d2e-7f3a-8b4c-5d6e7f8a9b0c"
# Une ligne du tableur.
VIDEO = {
"external_ref": "webtv/2026/recyclage-fibre-carbone",
"youtube_url": "https://www.youtube.com/watch?v=Xk3Ua9yQe7M",
"tags": ["Fibre de carbone", "Recyclage"],
"useful_link": {
"url": "https://example.com/recyclage-fibre-carbone",
"label": "Le dossier complet",
},
"categories": ["procedes", "recyclage"], # les slugs des catégories de la chaîne
"main_category": "recyclage",
}
def call(method, path, **kwargs):
response = session.request(method, f"{API}{path}", **kwargs)
if not response.ok:
error = response.json()
raise RuntimeError(f"{method} {path} → {response.status_code} {error['code']}: {error['message']}")
return response.json()
def all_pages(path, **params):
while True:
page = call("GET", path, params=params)
yield from page["data"]
if page["next"] is None:
return
params["after"] = page["next"]
def find_or_create_video(video):
"""L'asset de cette ligne : retrouvé par sa référence, sinon créé."""
found = call("GET", "/assets", params={"external_ref": video["external_ref"]})["data"]
if found:
return found[0]
return call("POST", "/assets", json={
"workspace_id": WORKSPACE_ID,
"third_party_url": video["youtube_url"],
"external_ref": video["external_ref"],
})
def publish(asset_id, category_ids, main_category_id):
"""La publication de l'asset sur la chaîne, dans l'état voulu."""
wanted = {
"status": "published",
"access": "public",
"category_ids": category_ids,
"primary_category_id": main_category_id,
}
found = call("GET", "/publications", params={"channel_id": CHANNEL_ID, "asset_id": asset_id})["data"]
if found:
return call("PATCH", f"/publications/{found[0]['id']}", json=wanted)
return call("POST", "/publications", json={"channel_id": CHANNEL_ID, "asset_id": asset_id, **wanted})
asset = find_or_create_video(VIDEO)
call("PUT", f"/assets/{asset['id']}/tags", json={"tags": VIDEO["tags"]})
call("PATCH", f"/assets/{asset['id']}/custom-fields", json={
"values": [{"field_id": USEFUL_LINK_FIELD_ID, "value": VIDEO["useful_link"]}],
})
categories = {c["slug"]: c["id"] for c in all_pages("/categories", channel_id=CHANNEL_ID)}
publication = publish(
asset["id"],
[categories[slug] for slug in VIDEO["categories"]],
categories[VIDEO["main_category"]],
)
print(publication["status"], publication["url"])
Le premier passage crée l’asset (201), pose ses deux tags, remplit son lien,
et publie la vidéo (201) :
published https://webtv.example.com/recycler-la-fibre-de-carbone-trois-procedes-compares/
Le second retrouve l’asset par son external_ref et la publication par
?channel_id=…&asset_id=… : il n’envoie ni POST /v1/assets ni
POST /v1/publications. Ses trois écritures — les tags, le lien, la publication —
envoient ce que l’asset et la publication portent déjà, et n’écrivent rien :
mêmes réponses, et aucun updated_at qui bouge. Il affiche la même ligne.
Deux choses à savoir avant d’en faire votre synchronisation :
- Le script dit l’état voulu, et le rétablit : une catégorie ajoutée à la main dans Videas, ou une publication archivée depuis, est défaite au passage suivant. Ne lui faites envoyer que les champs dont votre source de données est la référence.
- Le
POST /v1/assetsd’une ligne déjà créée n’est jamais renvoyé, parce que la recherche parexternal_refle précède. Sans elle, une relance créerait un second asset — Relancer sans risque.
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 |
Ce qui s’est passé |
|---|---|---|
400 |
validation |
Création ou modification : status: archived à la création, ou un published_at à côté d’un brouillon ; un published_at seul sur une publication qui n’est pas publiée ; un slug mal formé, trop long, ou qu’utilise le site de la chaîne lui-même ; un asset d’un type qu’une chaîne ne publie pas ; plus de 100 category_ids, une catégorie d’une autre chaîne, ou une catégorie principale qui n’est pas parmi elles ; un access autre que public ou authenticated ; null sur un champ qui ne peut pas être vidé. Lectures : un channel_id ou un asset_id qui n’est pas un seul uuid, un status inconnu, un paramètre inconnu. 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 porte pas websites.catalogue.read (lectures) ou websites.catalogue.publish (écritures) — ou l’organisation ne publie pas de chaînes. |
404 |
not_found |
Aucune chaîne de GET /v1/channels, aucun asset de GET /v1/assets ou aucune publication de GET /v1/publications n’a cet identifiant — dans le chemin comme dans le corps. |
409 |
conflict |
L’asset est déjà publié sur cette chaîne ; il n’est pas encore ready ; le slug est pris par une autre publication ou une page de la chaîne ; access ferait sortir une publication restricted, paid ou on_request ; ou les chaînes de l’organisation sont en cours de transfert vers Videas. |
415 |
unsupported_media_type |
Le corps n’est pas envoyé en application/json. |
422 |
validation |
channel_id ou asset_id manquant à la création, ou envoyé à une modification ; un champ mal typé ; un status ou un access qui ne nomme rien ; un champ que le corps ne connaît pas. |
429 |
rate_limited |
Trop de requêtes : attendez le nombre de secondes indiqué par Retry-After. |
Pour aller plus loin
- Ajouter une vidéo hébergée ailleurs et modifier un asset — ce que le script ci-dessus enchaîne.
- Les champs personnalisés — un champ
publicest montré sur la page de la publication. - Erreurs, limites et versions.
- La référence de l’API, générée depuis son contrat.