API de Owl3D Cloud

La API convierte un vídeo 2D a 3D lado a lado. El código obtiene un precio, sube el archivo, espera el trabajo y descarga el resultado. Ejecuta la misma conversión que Owl3D Cloud.

Un agente de programación con IA puede leer esta página como texto plano en /llms.txt.

Actualizado el

Antes de empezar

  1. Iniciar sesión en app.owl3d.com y verificar el correo electrónico.
  2. Tener un plan o comprar créditos. Un trabajo gasta primero los créditos mensuales del plan y después los créditos comprados. Los créditos de prueba también cuentan.
  3. Abrir Claves de API y crear una clave. La página muestra la clave una sola vez.

Guardar la clave en la variable de entorno OWL3D_API_KEY. Todos los ejemplos de esta página la leen de ahí.

El uso de la API se rige por los Owl3D Cloud API Terms (en inglés).

Shell
export OWL3D_API_KEY="o3d_live_..."

Autenticación

Elemento Valor
URL base https://api.owl3d.com
Formato de la clave o3d_live_<keyId>_<secret>
Encabezado de autenticación Authorization: Bearer <key>

Escribir Bearer con B mayúscula y un solo espacio. Otras formas reciben 401.

Cada ruta necesita un scope: jobs:read o jobs:write. La tabla de rutas indica el scope de cada ruta.

La clave de API debe mantenerse en secreto. Puede gastar los créditos. Si una clave se filtra, se revoca en la página de claves de API. Una clave revocada deja de funcionar en 60 segundos.

GET /v1/jobs/{id}

curl
curl https://api.owl3d.com/v1/jobs/job_... \
  -H "Authorization: Bearer $OWL3D_API_KEY"
Respuesta200
{"id": "job_...", "status": "converting"}

Convertir un vídeo

Descargar un script y ejecutarlo con el vídeo. Cada script realiza todos los pasos siguientes.

Script Requisitos Ejecución
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
curl -O https://www.owl3d.com/docs/api/convert.sh
bash convert.sh my-video.mp4

Los scripts son opcionales. Tu código puede usar cualquier lenguaje. Necesita llamadas HTTPS, SHA-256 y los datos del archivo. Con subir primero, solo necesita llamadas HTTPS.

El flujo tiene seis pasos:

  1. Leer los datos del archivo y generar la huella.
  2. POST /v1/estimates: obtener el precio. No se cobra nada.
  3. POST /v1/jobs: aprobar el precio. Esto reserva los créditos.
  4. Enviar cada parte con PUT a su URL de subida y después POST /v1/jobs/{id}/upload-complete.
  5. Consultar GET /v1/jobs/{id} cada 15 segundos.
  6. GET /v1/jobs/{id}/result: obtener un enlace de descarga. El enlace funciona 15 minutos y solo para GET.

Subir primero omite los pasos 1 y 2. Tu código envía el nombre y el tamaño del archivo a POST /v1/jobs. Después de la subida, el servidor lee el archivo y fija el precio.

Errores

La mayoría de las respuestas de error tienen un cuerpo JSON con error, un código corto.

Estado Significado
400 Solicitud incorrecta, por ejemplo bad_fingerprint
401 Sin token, un token incorrecto o una clave revocada
402 short_of_credits: los créditos del plan, de prueba y comprados juntos no cubren el precio
403 insufficient_scope, o la ruta no es pública
404 no_job. Los trabajos creados en la app Cloud no son visibles para la API
409 Conflicto de estado, por ejemplo not_uploading o endpoint_exists
410 El resultado expiró, o insufficient_credits_expired: el trabajo esperó créditos durante 24 horas
429 Un límite está lleno. Ver Límites

Reglas de reintento:

  1. Con 429, esperar Retry-After segundos y enviar la misma solicitud.
  2. Con 5xx o un error de red, aumentar la espera de forma gradual de 1 segundo a 60 segundos.
  3. Con cualquier otro 4xx, corregir la solicitud.
curl
curl -i https://api.owl3d.com/v1/jobs/job_unknown \
  -H "Authorization: Bearer $OWL3D_API_KEY"
Respuesta404
{"error": "no_job"}

Límites

Límite Valor Respuesta
Trabajos en curso 10 para todas las claves de API de una cuenta juntas. 15 para toda la cuenta, todos los canales juntos. Límite opcional por clave 429 concurrency_limit, Retry-After: 15
Solicitudes 120 por minuto y por clave 429, Retry-After: 60
Ráfaga Cada ruta 10 por segundo, ráfaga de 20, para todos los llamadores en conjunto 429 throttled, Retry-After: 1
Tope diario de créditos Opcional por clave 429 daily_credit_cap, hasta las 00:00 UTC
Claves activas 10 por cuenta Creación rechazada
Entrada 100 GB, 3 horas, 4096 x 2160, 60 fps Estimación rechazada
Contenedores MP4, MOV, MKV, M4V Estimación rechazada

El cuerpo de un 429 concurrency_limit indica el límite que está lleno. scope es api (todas las claves de API de la cuenta), account (todos los trabajos de la cuenta) o key (esta clave). Iniciar el siguiente trabajo cuando termine uno.

Respuesta429
{
  "error": "concurrency_limit",
  "scope": "api",
  "limit": 10,
  "inProgress": 10
}

Precios

POST /v1/estimates da el precio exacto de un archivo antes de pagar.

En los dos casos, el servidor lee el archivo subido y fija el precio final a partir de él. Con subir primero, ves el precio solo después de la subida. Si el saldo no cubre el precio, el trabajo espera. Consulta Créditos insuficientes.

Rutas

Método y ruta Scope Función
POST /v1/estimates jobs:write Precio y tiempo. No se cobra nada. Válido 1 hora, para un trabajo
POST /v1/jobs jobs:write Reserva créditos y devuelve el plan de subida. Con estimateId, o con filename y sizeBytes (subir primero)
GET /v1/jobs/{id}/upload jobs:read Estado de la subida: done, missing y URL parts nuevas
POST /v1/jobs/{id}/upload-complete jobs:write Envía los ETags y pone el trabajo en cola
GET /v1/jobs/{id} jobs:read Estado, progreso, tiempo restante
POST /v1/jobs/{id}/cancel jobs:write Cancela el trabajo
POST /v1/jobs/{id}/resume jobs:write Continúa un trabajo insufficient_credits después de añadir créditos. Sin subir de nuevo
GET /v1/jobs/{id}/result jobs:read Enlace de descarga, 15 minutos

Estimaciones

POST /v1/estimates da el precio y el tiempo de un archivo. No se cobra nada. Una estimación es válida 1 hora, para un trabajo.

Campo Valor
name, size El nombre del archivo y su tamaño en bytes
durationSeconds, width, height, fps Leídos del archivo con cualquier herramienta, como ffprobe o MediaInfo
codec Uno de H.264, HEVC, AV1, VP9
bitDepth, chroma Leídos del archivo. chroma es 4:2:0
fingerprint {size, headSha256, tailSha256, edgeBytes}
  • edgeBytes es 4194304, o el tamaño del archivo si es más pequeño.
  • headSha256 y tailSha256 son los hashes de los primeros y los últimos edgeBytes del archivo.

En la respuesta, "ok": false significa que la estimación se rechaza. Leer reason. Si credits.shortBy es mayor que 0, la cuenta necesita más créditos. credits.fromPlan, credits.fromTrial y credits.fromPurchased indican qué créditos pagan el precio.

POST /v1/estimates

curl
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
    }
  }'
Respuesta200
{
  "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}
}

Trabajos

POST /v1/jobs con {"estimateId": "est_..."} aprueba el precio. Esto reserva los créditos. La respuesta contiene el id del trabajo y el plan de subida: upload.partSize y las URL de upload.parts.

Opciones de salida

POST /v1/jobs también acepta output: {layout, max_height}.

  • layout: full_sbs (valor por defecto).
  • max_height: "source" (valor por defecto), 2160 o 1080.

POST /v1/jobs

curl
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"}}'
Respuesta201
{
  "id": "job_...",
  "status": "uploading",
  "upload": {
    "partSize": 67108864,
    "parts": ["https://...", "https://..."],
    "completeUrl": "/v1/jobs/job_.../upload-complete"
  }
}

Subir primero

Envía POST /v1/jobs sin estimateId. Tu código no lee el archivo y no crea una huella.

Campo Valor
filename El nombre del archivo, 255 caracteres como máximo. Termina en .mp4, .mov, .mkv o .m4v
sizeBytes El tamaño del archivo en bytes
output Opcional. Consulta Opciones de salida
  1. El saldo debe cubrir el precio mínimo. Si no, la respuesta es 402 short_of_credits y no se crea ningún trabajo.
  2. La respuesta 201 también trae reservedCredits y "priceAfterUpload": true.
  3. Después de la subida, el servidor lee el archivo y fija el precio.
  4. Si el servidor no puede convertir el archivo, el trabajo pasa a failed. No se cobra nada y el archivo se borra.

Un vídeo HDR10 necesita una estimación. Sin ella, la respuesta es 400 hdr_needs_estimate.

POST /v1/jobs (upload first)

curl
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)}"
Respuesta201
{
  "id": "job_...",
  "status": "uploading",
  "upload": {
    "partSize": 67108864,
    "parts": ["https://...", "https://..."],
    "completeUrl": "/v1/jobs/job_.../upload-complete"
  },
  "reservedCredits": 15,
  "priceAfterUpload": true
}

Créditos insuficientes

Si el precio supera el saldo, el trabajo pasa a insufficient_credits. No se cobra nada. Esto vale para los dos casos, con o sin estimación.

  1. GET /v1/jobs/{id} muestra shortBy (los créditos que faltan), neededCredits, deleteAt y resumeUrl.
  2. Añade créditos y luego envía POST /v1/jobs/{id}/resume. No subas el archivo otra vez.
  3. Mientras el saldo siga siendo bajo, resume responde 402 short_of_credits con shortBy.
  4. Sin resume antes de deleteAt (24 horas), el trabajo pasa a failed con insufficient_credits_expired. El archivo se borra.

POST /v1/jobs/{id}/resume

curl
curl -X POST https://api.owl3d.com/v1/jobs/job_.../resume \
  -H "Authorization: Bearer $OWL3D_API_KEY"
Respuesta200
{
  "id": "job_...",
  "status": "queued"
}
Respuesta402
{
  "error": "short_of_credits",
  "shortBy": 120,
  "neededCredits": 450,
  "deleteAt": "2026-11-04T09:00:00.000Z"
}

Subidas

  1. Dividir el archivo en partes de upload.partSize bytes. La parte 1 empieza en el byte 0.
  2. Enviar cada parte con PUT a su URL en upload.parts. Conservar el encabezado ETag de cada respuesta, con sus comillas.
  3. Enviar POST /v1/jobs/{id}/upload-complete con {"etags": [...]} en el orden de las partes.

Cada URL está firmada para una longitud exacta. Una parte debe tener partSize bytes, y la última parte contiene el resto. El almacenamiento responde 403 para cualquier otra longitud y para un cuerpo chunked. Enviar Content-Length.

Si una subida se detiene, no empezar de nuevo:

  1. Llamar a GET /v1/jobs/{id}/upload.
  2. Leer done. Cada elemento tiene partNumber, etag y size.
  3. Leer missing. Lista números de parte.
  4. Subir cada parte que falta a parts[partNumber - 1]. Estas URL son válidas durante 1 hora.
  5. Completar con todos los ETags en el orden de las partes. Usar el mismo archivo.

Completa una subida en 24 horas. Después, el almacenamiento elimina las partes de una subida no completada.

PUT upload.parts[n] · POST /v1/jobs/{id}/upload-complete

curl
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>\""]}'
Respuesta200
{"id": "job_...", "status": "queued"}

GET /v1/jobs/{id}/upload

curl
curl https://api.owl3d.com/v1/jobs/job_.../upload \
  -H "Authorization: Bearer $OWL3D_API_KEY"
Respuesta200
{
  "id": "job_...",
  "partSize": 67108864,
  "parts": ["https://...", "https://..."],
  "completeUrl": "/v1/jobs/job_.../upload-complete",
  "done": [{"partNumber": 1, "etag": "\"...\"", "size": 67108864}],
  "missing": [2]
}

Estado del trabajo

GET /v1/jobs/{id} da el estado, el progreso y el tiempo restante. Consultar cada 15 segundos.

status Significado
uploading El trabajo espera el archivo
queued El trabajo espera una GPU. Un trabajo puede volver de converting a queued por poco tiempo
converting progress va de 0 a 1
delayed Retenido de nuestro lado. Los créditos siguen reservados
insufficient_credits El precio supera el saldo. Añade shortBy créditos y luego llama a resume. Consulta Créditos insuficientes
done Listo. result contiene el nombre, el tamaño y la ruta del enlace
failed error contiene un code. Los créditos se devolvieron
cancelled Cancelado por quien llama

GET /v1/jobs/{id}

curl
curl https://api.owl3d.com/v1/jobs/job_... \
  -H "Authorization: Bearer $OWL3D_API_KEY"
Respuesta200
{
  "id": "job_...",
  "status": "converting",
  "progress": 0.42,
  "etaSeconds": 310
}

Resultados

GET /v1/jobs/{id}/result da un enlace de descarga para un trabajo done. El enlace funciona 15 minutos y solo para GET. Llamar de nuevo a la ruta para obtener un enlace nuevo. Cuando el resultado expiró, la respuesta es 410.

GET /v1/jobs/{id}/result

curl
curl https://api.owl3d.com/v1/jobs/job_.../result \
  -H "Authorization: Bearer $OWL3D_API_KEY"
curl -o output.mp4 "<url>"
Respuesta200
{
  "url": "https://...",
  "filename": "...",
  "sizeBytes": 209715200,
  "expiresInS": 900
}

Cancelar

POST /v1/jobs/{id}/cancel cancela un trabajo. Un trabajo cancelado antes de que empiece la conversión no cuesta nada. Después, cuesta el precio x progress, redondeado hacia arriba, con un mínimo de 1 crédito.

POST /v1/jobs/{id}/cancel

curl
curl -X POST https://api.owl3d.com/v1/jobs/job_.../cancel \
  -H "Authorization: Bearer $OWL3D_API_KEY"
Respuesta200
{"id": "job_...", "status": "cancelled"}

¡Entérate de todas las novedades!

Owl3D
© 2026 Owl3D. Todos los derechos reservados.