TL;DR, Respuesta Rápida
9 min de lecturaLa subida de vídeo por fragmentos en X v2 son cuatro llamadas: POST /2/media/upload/initialize devuelve un media id, POST /2/media/upload/{id}/append recibe cada fragmento con un segment_index, POST /2/media/upload/{id}/finalize cierra la sesión, y GET /2/media/upload?command=STATUS sondea processing_info hasta que state sea succeeded o failed. Los antiguos parámetros command=INIT y command=APPEND han desaparecido de todo salvo de ese GET de estado. segment_index tiene un tope de 999 y X recomienda fragmentos de 5 MB, lo que no llega a los 16 GB de total_bytes que acepta el esquema de initialize.
¿Qué es el flujo de subida de medios INIT APPEND FINALIZE de la API de X?
Cuatro llamadas componen el flujo de subida de medios INIT APPEND FINALIZE de la API de X: tres POST que mueven el archivo y un GET que informa de lo que X hizo después con él. X las lista en ese orden en la guía de subida por fragmentos.
- INIT,
POST /2/media/upload/initialize, que X describe como "start the session and get amedia_id" - APPEND,
POST /2/media/upload/{id}/append, "upload each chunk" - FINALIZE,
POST /2/media/upload/{id}/finalize, "complete the upload" - STATUS,
GET /2/media/upload, "wait for processing whenprocessing_infois returned"
Los nombres de los comandos sobrevivieron al salto a v2, el transporte no. X pone el aviso justo debajo de la lista: "Do not send command=INIT, command=APPEND, or command=FINALIZE to POST /2/media/upload. Those command-style parameters were the previous upload protocol. The v2 flow uses the dedicated paths above. command=STATUS is still used only on the status GET."
Así que uno de los cuatro comandos sigue existiendo como parámetro de consulta y tres no. El código escrito contra v1.1 falla en los tres POST y sigue funcionando en el sondeo, que es una forma confusa de romperse.
Las cuatro llamadas están detrás del mismo scope de OAuth 2.0, media.write, incluido el GET de estado. Leer el estado de procesamiento de tu propia subida necesita un scope de escritura, y X no publica ninguna alternativa de solo lectura.
¿Qué envía y qué devuelve cada paso?
Cada paso lleva parámetros distintos, y solo dos de los cuatro devuelven algo que te quedes.
| Paso | Endpoint | Qué envías | Qué recibes |
|---|---|---|---|
| INIT | POST /2/media/upload/initialize | media_type, total_bytes, media_category, additional_owners y shared opcionales | id, media_key, expires_after_secs |
| APPEND | POST /2/media/upload/{id}/append | media y segment_index | expires_at |
| FINALIZE | POST /2/media/upload/{id}/finalize | nada salvo el id de la ruta | id, media_key, size, expires_after_secs, processing_info |
| STATUS | GET /2/media/upload?command=STATUS&media_id= | media_id, command opcional | id, media_key, size, processing_info, video o image |
INIT recibe un cuerpo JSON, no campos de formulario, y X lo dice en la guía: "Send a JSON body, not multipart form fields." El enum de media_type va desde video/mp4 y video/quicktime, pasando por text/srt y text/vtt, hasta dos formatos 3D en los que casi nadie repara, model/gltf-binary y model/vnd.usdz+zip. El campo total_bytes tiene un techo duro de esquema de 17179869184, que son 16 GiB.
{
"data": {
"id": "1880028106020515840",
"media_key": "13_1880028106020515840",
"expires_after_secs": 86400
}
}Ese expires_after_secs de 86400 del propio ejemplo de X es el reloj de la sesión: un día desde INIT para adjuntar el medio terminado a algo. El id encaja con el patrón ^[0-9]{1,19}$, y el media_key le antepone un número de tipo y un guion bajo, la misma forma 13_ que documenta la Ads API cuando llama al valor id de vídeo.
APPEND es el único paso cuya respuesta no te dice nada útil sobre el medio. Su esquema tiene un solo campo, expires_at, descrito como "Epoch seconds when the upload session expires." Todos los demás pasos informan de la vida restante de la sesión como expires_after_secs, una cuenta relativa. APPEND la informa como marca de tiempo absoluta. El mismo dato, dos unidades, tres grafías de campo repartidas en cuatro endpoints.
FINALIZE es la solicitud más vacía del flujo. Recibe el id de la ruta y nada más, sin cuerpo y sin parámetros de consulta, y por eso el curl entero cabe en dos líneas:
curl -X POST "https://api.x.com/2/media/upload/1880028106020515840/finalize" \
-H "Authorization: Bearer $USER_ACCESS_TOKEN"En su respuesta está la bifurcación de verdad. X pone la regla en una frase: "If processing_info is returned, proceed to Step 4 to wait for processing. If not, the media is ready to use." Una imagen suele saltarse el paso cuatro. Un vídeo nunca.

¿Cómo funciona segment_index?
segment_index es un contador simple que llevas tú, empieza en cero, y el esquema fija sus dos extremos: "minimum: 0, maximum: 999."
La indicación de X sobre el tamaño de fragmento está en la guía y no en el esquema: "Keep each segment at or below 5 MB (the server maximum is 8 MB). Segments are indexed from 0." El ejemplo en Python de esa misma guía lee el archivo en bloques de 4 MB e incrementa el índice tras cada llamada con éxito.
curl -X POST "https://api.x.com/2/media/upload/1880028106020515840/append" \
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
-F "segment_index=0" \
-F "media=@/path/to/chunk0.mp4"El cuerpo puede ser multipart form data como arriba o JSON, porque el esquema de la solicitud está registrado para los dos tipos de contenido. En JSON el campo media recibe base64. En multipart recibe bytes en bruto.
Ahora multiplica los dos techos publicados. Mil segmentos al máximo de servidor de 8 MB son 8 GB. Mil segmentos a los 5 MB recomendados son 5 GB. Mientras tanto, POST /2/media/upload/initialize acepta total_bytes de hasta 16 GiB, y X afirma sin rodeos que una cuenta con X Premium o verificada puede subir un vídeo de 16 GB a un Post. Esos dos hechos no pueden cumplirse a la vez con APPEND tal como está documentado, y X no publica ningún tamaño de segmento mayor, ninguna ruta alternativa ni ninguna nota que diga que el tope de 999 se mueve para archivos grandes. Todo lo que pase de 8 GB no tiene ruta documentada hacia el flujo.
AdaptlyPost
Empieza tu prueba gratis de 7 días
Analíticas multiplataforma
Bandeja Social
Asistente con IA
La subida tampoco tiene que ser secuencial en ningún sentido publicado. X lista "Failed chunks can be retried individually" y "Uploads can be paused and resumed" entre las ventajas de fragmentar, pero nunca dice si un segmento reintentado sobrescribe al anterior, si los índices pueden llegar desordenados o qué ocurre si te saltas un número. La misma pregunta aparece en otras redes: la sesión de subida reanudable de Instagram tiene una cabecera de offset que todos los ejemplos publicados ponen a cero, y LinkedIn la responde entregándote una lista de URLs de subida, una por parte.

¿Qué devuelve el comando STATUS?
STATUS devuelve un objeto processing_info con tres campos, y aquel sobre el que actúas es state.
| Campo | Qué dice el esquema |
|---|---|
state | "Processing state (pending, in_progress, failed, succeeded)" |
check_after_secs | "Seconds to wait before polling status again" |
progress_percent | "Processing completion percentage" |
La guía imprime el camino feliz como un diagrama de flechas: "pending to in_progress to succeeded or failed." Dos de esos cuatro son terminales. El propio ejemplo de sondeo de X rompe el bucle en succeeded y en failed, y duerme check_after_secs en cualquier otro caso, que es la forma correcta porque el intervalo lo pone el servidor y no tu política de reintentos. El ejemplo de FINALIZE devuelve "check_after_secs": 1, así que el primer sondeo llega rápido.
curl "https://api.x.com/2/media/upload?command=STATUS&media_id=1880028106020515840" \
-H "Authorization: Bearer $USER_ACCESS_TOKEN"Vale la pena conocer dos huecos antes de construir el bucle.
Los cuatro valores de estado viven en una cadena de descripción, no en un enum. El esquema tipa state como cadena simple sin lista de valores permitidos, así que los cuatro nombres que imprime X son documentación y no un conjunto cerrado. Trata un estado desconocido como "sigue sondeando" en lugar de como un error.
El mayor es que processing_info no tiene dónde poner un motivo del fallo. Su esquema declara exactamente tres propiedades y luego additionalProperties: false, así que una respuesta que dice "state": "failed" no lleva ningún código, ningún mensaje ni ningún subcódigo al lado. El protocolo v1.1 devolvía un objeto de error dentro de processing_info con un nombre y un mensaje. El esquema v2 tal como está publicado no tiene campo para eso, lo que deja failed como diagnóstico completo.
progress_percent está documentado en todos los esquemas de processing_info de la referencia de medios y no aparece en ninguna respuesta de ejemplo de X, así que cuenta con que falte en lugar de con que valga cero.
¿Por qué una subida terminada sigue fallando en POST /2/tweets?
Porque X comprueba el archivo dos veces contra dos personas distintas, y lo dice dos veces. La introducción de medios abre con ello: "Upload and posting are separately enforced. A file that uploads successfully can still be rejected when you attach it to a Post."
La primera comprobación corre contra el usuario que sube, en INIT y FINALIZE. La segunda corre contra el usuario que publica, cuando el media_id llega a POST /2/tweets. X nombra el fallo con exactitud:
{
"title": "Forbidden",
"detail": "This user is not allowed to post a video longer than 20 minutes.",
"type": "about:blank",
"status": 403
}Veinte minutos es la cifra por defecto en esa cadena. X señala que el número se mueve: "N is the posting user's duration cap (20 minutes by default, 125 minutes for Premium / verified)." Lo que significa que el mismo media_id, finalizado una vez y procesado hasta succeeded, se adjunta en una cuenta y devuelve 403 en otra. El derecho sigue a la cuenta, y los niveles que deciden qué duración de vídeo recibe una cuenta son los mismos niveles de X Premium, no tu plan de desarrollador.
Los límites de composición del Post se aplican en ese mismo momento. X permite "up to 4 photos, 1 animated GIF, or 1 video" en un Post, y un media_id que rompa esa mezcla falla al adjuntarse aunque la subida fuera limpia. La secuencia práctica para cualquier cosa larga es el recorrido completo para llevar vídeo de alta calidad a X, con el 403 tratado como resultado normal y no como excepción.
Preguntas frecuentes
¿Qué endpoints sustituyen a command=INIT y command=APPEND en la API de X v2?
POST /2/media/upload/initialize y POST /2/media/upload/{id}/append. X indica que los parámetros con forma de comando "were the previous upload protocol" y que ya no deben enviarse a POST /2/media/upload.
¿Sigue siendo válido command=STATUS en la API de X v2?
Sí, y es el único que lo es. X escribe que "command=STATUS is still used only on the status GET", es decir, GET /2/media/upload?command=STATUS&media_id=. El esquema marca el parámetro command como opcional aunque todos los ejemplos publicados lo pasan.
¿Cuál es el segment_index máximo en el endpoint append de la API de X?
- El esquema de la solicitud append fija
segment_indexcon un mínimo de 0 y un máximo de 999, así que un archivo tiene un tope de 1000 fragmentos.
¿Cómo de grande puede ser cada fragmento de APPEND?
X recomienda 5 MB y nombra 8 MB como máximo del servidor. Su propio ejemplo en Python lee el archivo en bloques de 4 MB.
AdaptlyPost
Empieza tu prueba gratis de 7 días
Analíticas multiplataforma
Bandeja Social
Asistente con IA
¿Cuáles son los estados de procesamiento de la subida de medios de X?
pending, in_progress, succeeded y failed. X imprime la progresión como pending a in_progress a succeeded o failed, aunque el esquema tipa state como cadena abierta en lugar de como enum.
¿Por qué un media_id que finalizó con éxito falla en POST /2/tweets?
La subida y la publicación se controlan por separado contra derechos distintos. Si el usuario que publica no puede adjuntar un vídeo tan largo, la solicitud del Post devuelve 403 Forbidden con el detalle "This user is not allowed to post a video longer than N minutes."
¿Cuánto tiempo permanece válida una sesión de subida de medios de X antes de caducar?
El propio ejemplo de INIT de X devuelve un expires_after_secs de 86400, es decir, un día entre INIT y adjuntar el medio terminado a algo. FINALIZE informa el tiempo restante del mismo modo, como expires_after_secs. APPEND rompe ese patrón y devuelve expires_at, una marca de tiempo epoch absoluta en lugar de una cuenta regresiva, así que el mismo reloj de sesión aparece en dos unidades distintas.
¿Subir una foto a X exige consultar el endpoint STATUS?
Casi nunca. La regla de X es que si FINALIZE no devuelve ningún bloque processing_info, el medio está listo para usarse, y una subida de imagen suele volver sin él. Un vídeo nunca vuelve sin él, así que el paso cuatro es en la práctica cosa de vídeos.
¿El endpoint STATUS usa un scope de OAuth distinto al de los pasos de subida?
Las cuatro llamadas, INIT, APPEND, FINALIZE y STATUS, funcionan bajo el mismo scope media.write. Consultar el estado de procesamiento de tu propia subida exige un scope de escritura, y X no publica ninguna alternativa de solo lectura.
¿Qué tipos de medios se pueden subir por el endpoint initialize de la API de X?
El enum media_type de INIT va desde video/mp4 y video/quicktime hasta text/srt y text/vtt, y llega a dos formatos 3D, model/gltf-binary y model/vnd.usdz+zip. Un solo endpoint de subida cubre así vídeo, subtítulos y archivos 3D.
¿Te resultó útil este artículo?
¡Cuéntanos qué te parece!
Vernos más en Google
Un clic marca AdaptlyPost como fuente preferida y nuestros artículos aparecen más arriba en tus Noticias destacadas, el modo IA y los resúmenes con IA.
Antes de irte...
AdaptlyPost
Programa tu contenido en todas las plataformas
Gestiona todas tus cuentas de redes sociales en un solo lugar con AdaptlyPost.
Analíticas multiplataforma
Bandeja Social
Asistente con IA
Términos relacionados del glosario


Todos los valores de media_category de la API de Twitter y qué permiten
El parámetro media_category de la API de Twitter tiene ocho valores, decide los topes de tamaño y duración que aplica X, y falla al publicar si eliges mal.


La sesión de subida reanudable de Instagram y el host rupload
Una subida reanudable de Instagram empieza con upload_type=resumable en /media y sigue con un POST a rupload.facebook.com con offset y file_size.


Meta documenta cuatro límites distintos para scheduled_publish_time de Facebook
El campo scheduled_publish_time de Facebook exige una hora al menos 10 minutos por delante, y Meta le da cuatro máximos: 29 días, 30 días, 75 días y 6 meses.
Artículos Relacionados


Cómo el límite de tasa business use case de Meta mide las Páginas de Facebook
Para Páginas, el límite de tasa business use case de Meta da 4800 llamadas por usuario implicado cada 24 horas con tokens de Página y falla con el código 80001.


Los requisitos de imagen de la API de Instagram que Meta aplica al subir
Meta fija los requisitos de imagen de la API de Instagram en JPEG, 8 MB como máximo y una relación de aspecto de 4:5 a 1.91:1, cada uno con su código.


El límite de tasa de la API de Instagram es 4800 por impresión
Meta fija el límite de tasa de la API de Instagram en 4800 llamadas por impresión en 24 horas móviles y reporta el uso en X-Business-Use-Case-Usage.

