API Owl3D Cloud
L'API convertit une vidéo 2D en 3D côte à côte. Votre code obtient un prix, envoie le fichier, attend la tâche et télécharge le résultat. Elle exécute la même conversion qu'Owl3D Cloud.
Un agent de code IA peut lire cette page en texte brut à l'adresse /llms.txt.
Avant de commencer
- Connectez-vous sur app.owl3d.com et vérifiez votre adresse e-mail.
- Prenez un forfait ou achetez des crédits. Une tâche dépense d'abord les crédits mensuels du forfait, puis les crédits achetés. Les crédits d'essai comptent aussi.
- Ouvrez Clés API et créez une clé. La page affiche la clé une seule fois.
Gardez la clé dans la variable d'environnement OWL3D_API_KEY. Tous les exemples de cette page la lisent à cet endroit.
L'utilisation de l'API est soumise aux Owl3D Cloud API Terms (en anglais).
export OWL3D_API_KEY="o3d_live_..."Authentification
| Élément | Valeur |
|---|---|
| URL de base | https://api.owl3d.com |
| Format de la clé | o3d_live_<keyId>_<secret> |
| En-tête d'authentification | Authorization: Bearer <key> |
Écrivez Bearer avec un B majuscule et une seule espace. Les autres formes reçoivent 401.
Chaque route demande un scope : jobs:read ou jobs:write. Le tableau des routes donne le scope de chaque route.
Gardez la clé API secrète. Elle peut dépenser vos crédits. Si une clé fuit, révoquez-la sur la page des clés API. Une clé révoquée cesse de fonctionner en 60 secondes.
GET /v1/jobs/{id}
curl https://api.owl3d.com/v1/jobs/job_... \
-H "Authorization: Bearer $OWL3D_API_KEY"{"id": "job_...", "status": "converting"}Convertir une vidéo
Téléchargez un script et exécutez-le sur votre vidéo. Chaque script effectue toutes les étapes ci-dessous.
| Script | Prérequis | Exécution |
|---|---|---|
| convert.sh | bash, curl, jq, ffprobe | bash convert.sh my-video.mp4 |
| convert.py | Python 3.9+, requests, ffprobe |
python3 convert.py my-video.mp4 |
| convert.mjs | Node.js 20+, ffprobe | node convert.mjs my-video.mp4 |
convert.sh · convert.py · convert.mjs
curl -O https://www.owl3d.com/docs/api/convert.sh
bash convert.sh my-video.mp4Les scripts sont facultatifs. Votre code peut utiliser n'importe quel langage. Il lui faut des appels HTTPS, SHA-256 et les informations du fichier. Avec l'envoi d'abord, il lui faut seulement des appels HTTPS.
Le déroulement comporte six étapes :
- Lire les informations du fichier et calculer l'empreinte.
POST /v1/estimates: obtenir le prix. Rien n'est facturé.POST /v1/jobs: approuver le prix. Cela réserve les crédits.- Envoyer chaque partie par
PUTà son URL d'envoi, puisPOST /v1/jobs/{id}/upload-complete. - Interroger
GET /v1/jobs/{id}toutes les 15 secondes. GET /v1/jobs/{id}/result: obtenir un lien de téléchargement. Le lien est valable 15 minutes et uniquement pourGET.
L'envoi d'abord saute les étapes 1 et 2. Votre code envoie le nom et la taille du fichier à POST /v1/jobs. Après l'envoi, le serveur lit le fichier et fixe le prix.
Erreurs
La plupart des réponses d'erreur ont un corps JSON avec error, un code court.
| Statut | Signification |
|---|---|
400 |
Requête incorrecte, par exemple bad_fingerprint |
401 |
Pas de jeton, un jeton incorrect ou une clé révoquée |
402 |
short_of_credits : ensemble, les crédits du forfait, d'essai et achetés ne couvrent pas le prix |
403 |
insufficient_scope, ou la route n'est pas publique |
404 |
no_job. Les tâches créées dans l'application Cloud ne sont pas visibles pour l'API |
409 |
Conflit d'état, par exemple not_uploading ou endpoint_exists |
410 |
Le résultat a expiré, ou insufficient_credits_expired : la tâche a attendu des crédits pendant 24 heures |
429 |
Une limite est atteinte. Voir Limites |
Règles de nouvelle tentative :
- Sur
429, attendezRetry-Aftersecondes et renvoyez la même requête. - Sur
5xxou une erreur réseau, augmentez progressivement le délai de 1 seconde à 60 secondes. - Sur tout autre
4xx, corrigez la requête.
curl -i https://api.owl3d.com/v1/jobs/job_unknown \
-H "Authorization: Bearer $OWL3D_API_KEY"{"error": "no_job"}Limites
| Limite | Valeur | Réponse |
|---|---|---|
| Tâches en cours | 10 pour toutes les clés API d'un compte ensemble. 15 pour tout le compte, tous canaux ensemble. Limite facultative par clé | 429 concurrency_limit, Retry-After: 15 |
| Requêtes | 120 par minute et par clé | 429, Retry-After: 60 |
| Rafale | Chaque route 10 par seconde, rafale de 20, pour tous les appelants ensemble | 429 throttled, Retry-After: 1 |
| Plafond quotidien de crédits | Facultatif par clé | 429 daily_credit_cap, jusqu'à 00:00 UTC |
| Clés actives | 10 par compte | Création refusée |
| Entrée | 100 Go, 3 heures, 4096 x 2160, 60 fps | Estimation refusée |
| Conteneurs | MP4, MOV, MKV, M4V | Estimation refusée |
Le corps d'une réponse 429 concurrency_limit nomme la limite atteinte. scope vaut api (toutes les clés API du compte), account (toutes les tâches du compte) ou key (cette clé). Lancez la tâche suivante quand une tâche se termine.
{
"error": "concurrency_limit",
"scope": "api",
"limit": 10,
"inProgress": 10
}Tarifs
POST /v1/estimates donne le prix exact d'un fichier avant que vous payiez.
Dans les deux cas, le serveur lit le fichier envoyé et fixe le prix final à partir de ce fichier. Avec l'envoi d'abord, vous voyez le prix seulement après l'envoi. Si le solde ne couvre pas le prix, la tâche attend. Voir Crédits insuffisants.
Routes
| Méthode et chemin | Scope | Fonction |
|---|---|---|
POST /v1/estimates |
jobs:write |
Prix et durée. Rien n'est facturé. Valable 1 heure, pour une seule tâche |
POST /v1/jobs |
jobs:write |
Réserve des crédits et renvoie le plan d'envoi. Avec estimateId, ou avec filename et sizeBytes (envoi d'abord) |
GET /v1/jobs/{id}/upload |
jobs:read |
État de l'envoi : done, missing et de nouvelles URL parts |
POST /v1/jobs/{id}/upload-complete |
jobs:write |
Envoie les ETags et met la tâche en file d'attente |
GET /v1/jobs/{id} |
jobs:read |
Statut, progression, temps restant |
POST /v1/jobs/{id}/cancel |
jobs:write |
Annule la tâche |
POST /v1/jobs/{id}/resume |
jobs:write |
Reprend une tâche insufficient_credits après l'ajout de crédits. Aucun nouvel envoi |
GET /v1/jobs/{id}/result |
jobs:read |
Lien de téléchargement, 15 minutes |
Estimations
POST /v1/estimates donne le prix et la durée d'un fichier. Rien n'est facturé. Une estimation est valable 1 heure, pour une seule tâche.
| Champ | Valeur |
|---|---|
name, size |
Le nom du fichier et sa taille en octets |
durationSeconds, width, height, fps |
Lus dans le fichier avec n'importe quel outil, comme ffprobe ou MediaInfo |
codec |
H.264, HEVC, AV1 ou VP9 |
bitDepth, chroma |
Lus dans le fichier. chroma vaut 4:2:0 |
fingerprint |
{size, headSha256, tailSha256, edgeBytes} |
edgeBytesvaut 4194304, ou la taille du fichier pour un fichier plus petit.headSha256ettailSha256sont les hachages des premiers et des derniersedgeBytesdu fichier.
Dans la réponse, "ok": false signifie que l'estimation est refusée. Lisez reason. Si credits.shortBy est supérieur à 0, le compte a besoin de plus de crédits. credits.fromPlan, credits.fromTrial et credits.fromPurchased indiquent quels crédits paient le prix.
POST /v1/estimates
curl https://api.owl3d.com/v1/estimates \
-H "Authorization: Bearer $OWL3D_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "my-video.mp4",
"size": 104857600,
"durationSeconds": 120,
"width": 1920, "height": 1080, "fps": 24,
"codec": "H.264", "bitDepth": 8, "chroma": "4:2:0",
"fingerprint": {
"size": 104857600,
"headSha256": "<SHA-256 of the first 4194304 bytes>",
"tailSha256": "<SHA-256 of the last 4194304 bytes>",
"edgeBytes": 4194304
}
}'{
"ok": true,
"id": "est_...",
"credits": {
"total": 60,
"fromPlan": 60,
"fromTrial": 0,
"fromPurchased": 0,
"shortBy": 0
},
"etaMinutes": 6,
"hdr": {"detected": false},
"output": {"format": "FULL_SBS", "width": 3840, "height": 1080}
}Tâches
POST /v1/jobs avec {"estimateId": "est_..."} approuve le prix. Cela réserve les crédits. La réponse contient l'id de la tâche et le plan d'envoi : upload.partSize et les URL de upload.parts.
Options de sortie
POST /v1/jobs accepte aussi output: {layout, max_height}.
layout:full_sbs(par défaut).max_height:"source"(par défaut),2160ou1080.
POST /v1/jobs
curl https://api.owl3d.com/v1/jobs \
-H "Authorization: Bearer $OWL3D_API_KEY" \
-H "Content-Type: application/json" \
-d '{"estimateId": "est_...", "output": {"layout": "full_sbs", "max_height": "source"}}'{
"id": "job_...",
"status": "uploading",
"upload": {
"partSize": 67108864,
"parts": ["https://...", "https://..."],
"completeUrl": "/v1/jobs/job_.../upload-complete"
}
}Envoi d'abord
Envoyez POST /v1/jobs sans estimateId. Votre code ne lit pas le fichier et ne crée pas d'empreinte.
| Champ | Valeur |
|---|---|
filename |
Le nom du fichier, 255 caractères au plus. Il se termine par .mp4, .mov, .mkv ou .m4v |
sizeBytes |
La taille du fichier en octets |
output |
Facultatif. Voir Options de sortie |
- Le solde doit couvrir le prix minimum. Sinon, la réponse est
402 short_of_creditset aucune tâche n'est créée. - La réponse
201contient aussireservedCreditset"priceAfterUpload": true. - Après l'envoi, le serveur lit le fichier et fixe le prix.
- Si le serveur ne peut pas convertir le fichier, la tâche passe à
failed. Rien n'est facturé et le fichier est supprimé.
Une vidéo HDR10 a besoin d'une estimation. Sans estimation, la réponse est 400 hdr_needs_estimate.
POST /v1/jobs (upload first)
curl https://api.owl3d.com/v1/jobs \
-H "Authorization: Bearer $OWL3D_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"filename\": \"my-video.mp4\", \"sizeBytes\": $(wc -c < my-video.mp4)}"{
"id": "job_...",
"status": "uploading",
"upload": {
"partSize": 67108864,
"parts": ["https://...", "https://..."],
"completeUrl": "/v1/jobs/job_.../upload-complete"
},
"reservedCredits": 15,
"priceAfterUpload": true
}Crédits insuffisants
Si le prix dépasse le solde, la tâche passe à insufficient_credits. Rien n'est facturé. C'est la même chose avec ou sans estimation.
GET /v1/jobs/{id}afficheshortBy(les crédits à ajouter),neededCredits,deleteAtetresumeUrl.- Ajoutez des crédits, puis envoyez
POST /v1/jobs/{id}/resume. N'envoyez pas le fichier une deuxième fois. - Tant que le solde reste trop bas, resume répond
402 short_of_creditsavecshortBy. - Sans resume avant
deleteAt(24 heures), la tâche passe àfailedavecinsufficient_credits_expired. Le fichier est supprimé.
POST /v1/jobs/{id}/resume
curl -X POST https://api.owl3d.com/v1/jobs/job_.../resume \
-H "Authorization: Bearer $OWL3D_API_KEY"{
"id": "job_...",
"status": "queued"
}{
"error": "short_of_credits",
"shortBy": 120,
"neededCredits": 450,
"deleteAt": "2026-11-04T09:00:00.000Z"
}Envois
- Découpez le fichier en parties de
upload.partSizeoctets. La partie 1 commence à l'octet 0. - Envoyez chaque partie par
PUTà son URL dansupload.parts. Conservez l'en-têteETagde chaque réponse, avec ses guillemets. - Envoyez
POST /v1/jobs/{id}/upload-completeavec{"etags": [...]}dans l'ordre des parties.
Chaque URL est signée pour une longueur exacte. Une partie doit faire partSize octets, et la dernière partie contient le reste. Le stockage répond 403 pour toute autre longueur et pour un corps en transfert fractionné (chunked). Envoyez Content-Length.
Si un envoi s'arrête, ne recommencez pas depuis le début :
- Appelez
GET /v1/jobs/{id}/upload. - Lisez
done. Chaque élément contientpartNumber,etagetsize. - Lisez
missing. Il liste des numéros de partie. - Envoyez chaque partie manquante à
parts[partNumber - 1]. Ces URL sont valables 1 heure. - Terminez avec tous les ETags dans l'ordre des parties. Utilisez le même fichier.
Terminez un envoi dans les 24 heures. Ensuite, le stockage supprime les parties d'un envoi non terminé.
PUT upload.parts[n] · POST /v1/jobs/{id}/upload-complete
split -b 67108864 my-video.mp4 part_
curl -sS -D - -o /dev/null -X PUT --upload-file part_aa "<upload.parts[0]>" | grep -i '^etag'
curl -sS -D - -o /dev/null -X PUT --upload-file part_ab "<upload.parts[1]>" | grep -i '^etag'
curl https://api.owl3d.com/v1/jobs/job_.../upload-complete \
-H "Authorization: Bearer $OWL3D_API_KEY" \
-H "Content-Type: application/json" \
-d '{"etags": ["\"<etag of part 1>\"", "\"<etag of part 2>\""]}'{"id": "job_...", "status": "queued"}GET /v1/jobs/{id}/upload
curl https://api.owl3d.com/v1/jobs/job_.../upload \
-H "Authorization: Bearer $OWL3D_API_KEY"{
"id": "job_...",
"partSize": 67108864,
"parts": ["https://...", "https://..."],
"completeUrl": "/v1/jobs/job_.../upload-complete",
"done": [{"partNumber": 1, "etag": "\"...\"", "size": 67108864}],
"missing": [2]
}Statut de la tâche
GET /v1/jobs/{id} donne le statut, la progression et le temps restant. Interrogez-le toutes les 15 secondes.
status |
Signification |
|---|---|
uploading |
La tâche attend le fichier |
queued |
La tâche attend un GPU. Une tâche peut repasser brièvement de converting à queued |
converting |
progress va de 0 à 1 |
delayed |
Retenue de notre côté. Les crédits restent réservés |
insufficient_credits |
Le prix dépasse le solde. Ajoutez shortBy crédits, puis appelez resume. Voir Crédits insuffisants |
done |
Terminée. result contient le nom, la taille et le chemin du lien |
failed |
error contient un code. Les crédits ont été restitués |
cancelled |
Annulée par l'appelant |
GET /v1/jobs/{id}
curl https://api.owl3d.com/v1/jobs/job_... \
-H "Authorization: Bearer $OWL3D_API_KEY"{
"id": "job_...",
"status": "converting",
"progress": 0.42,
"etaSeconds": 310
}Résultats
GET /v1/jobs/{id}/result donne un lien de téléchargement pour une tâche done. Le lien est valable 15 minutes et uniquement pour GET. Appelez de nouveau la route pour obtenir un nouveau lien. Quand le résultat a expiré, la réponse est 410.
GET /v1/jobs/{id}/result
curl https://api.owl3d.com/v1/jobs/job_.../result \
-H "Authorization: Bearer $OWL3D_API_KEY"
curl -o output.mp4 "<url>"{
"url": "https://...",
"filename": "...",
"sizeBytes": 209715200,
"expiresInS": 900
}Annulation
POST /v1/jobs/{id}/cancel annule une tâche. Une tâche annulée avant le début de la conversion ne coûte rien. Ensuite, elle coûte le prix x progress, arrondi à l'unité supérieure, au minimum 1 crédit.
POST /v1/jobs/{id}/cancel
curl -X POST https://api.owl3d.com/v1/jobs/job_.../cancel \
-H "Authorization: Bearer $OWL3D_API_KEY"{"id": "job_...", "status": "cancelled"}