El trabajo cabe en una frase: publicar en Instagram según un calendario, desde tu propio código, sin que nadie abra la aplicación.
Casi todo lo que se escribe sobre cómo automatizar publicaciones de Instagram responde a otra pregunta. Responde a “qué calendario debería pagar”. Esta es la otra respuesta, la de quien ya decidió construirlo, sobre la Graph API de Meta directamente o sobre una API de publicación unificada. La construcción tiene tres partes, y solo una es la que los productos enseñan en la demo. Todos los datos de Meta que aparecen abajo se leyeron de su documentación el 14 de septiembre de 2026, y las páginas están enlazadas al final.
Una API en lugar de la fontanería de la Graph API. InvisibleAPI es una sola API basada en jobs para Instagram y X. Conecta la cuenta, crea un job de publicación y consúltalo hasta que llegue a published.
Obtener clave APIQué significa realmente automatizar publicaciones de Instagram
Quita las herramientas y un publicador automático son tres cosas.
| Parte | Qué guarda | Cuánto cuesta |
|---|---|---|
| La cola | Qué publicar, dónde y cuándo | Fácil. Es una tabla. |
| El despachador | El bucle que convierte una fila vencida en una publicación | Fácil de empezar, y la parte que decide si esto sobrevive un mes |
| La política de fallos | Qué pasa cuando la publicación no llega | El trabajo entero |
La automatización de publicaciones de Instagram se vende por la primera parte porque la primera parte es la que luce en una demo. Un calendario con arrastrar y soltar es una cola con una persona dentro. Quita a la persona y la cola no cambia en nada, pero las otras dos partes empiezan a importar, porque ya no hay nadie mirando que note que la publicación del martes nunca salió.
Así que la construcción de abajo dedica cuatro líneas a la cola y el resto a lo que pasa después del despacho.
La cola es un archivo, no una base de datos
Empieza por lo más pequeño que funcione. Un CSV, versionado en el repositorio o guardado en almacenamiento de objetos, con cinco columnas:
row_id |
account |
media_url |
caption |
publish_at |
|---|---|---|---|---|
2026-09-15-launch |
@yourbrand |
https://cdn.example.com/launch.jpg |
Nuevo lanzamiento disponible. | 2026-09-15T09:00:00Z |
2026-09-16-behind |
@yourbrand |
https://cdn.example.com/studio.jpg |
Detrás de la sesión. | 2026-09-16T09:00:00Z |
Dos cosas de esta tabla sostienen todo lo demás.
El media es una URL, no un archivo. Aquí no hay paso de subida, porque el media se entrega como una URL públicamente accesible. Un enlace a un bucket privado es la razón más frecuente por la que falla la primera publicación automática, y ningún reintento convierte un objeto privado en público.
row_id no es decoración. Se convierte en la clave de idempotencia de la llamada de creación, que es lo que evita que un cron caído publique dos veces en la siguiente ejecución. Elige ahora algo estable y legible y la sección de reintentos más abajo no te costará nada.
Si prefieres partir de una receta en lugar de un folio en blanco, un calendario de publicación de Instagram con aprobación es la misma forma con un paso de revisión delante.
Una fila, un job de publicación
La preparación son dos pasos: conecta una cuenta profesional de Instagram y crea una API key de organización con los scopes publishing:read y publishing:publish. La guía de inicio rápido recorre ambos.
Después, una fila se convierte en un job de publicación:
curl -X POST "https://api.invisibleapi.ai/api/v1/organizations/$ORGANIZATION/publishing/jobs" \
-H "Authorization: Bearer $INVISIBLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"targets": ["acct_ig_01"],
"caption": "New drop, live now.",
"mediaItems": [{ "mediaType": "image", "sourceUrl": "https://cdn.example.com/launch.jpg" }],
"requestedPublishAt": "2026-09-15T09:00:00Z",
"clientRequestId": "2026-09-15-launch"
}'
La respuesta es un job, no una publicación:
{
"id": "job_01K2M4P7",
"status": "scheduled",
"clientRequestId": "2026-09-15-launch",
"requestedPublishAt": "2026-09-15T09:00:00Z"
}
Esta es la parte que la mayoría de los tutoriales de automatización se salta. La publicación es asíncrona. Creas un job, la secuencia de publicación de varios pasos de la plataforma corre por detrás, y consultas hasta que el estado llega a published. Un tutorial que enseña una llamada de creación devolviendo la URL de una publicación en vivo describe algo que no ocurre, y un despachador escrito sobre esa suposición reporta éxito para publicaciones que luego fallaron.
Sobre los nombres de campo. La forma de la petición está confirmada en la referencia de la API: targets, mediaItems con su mediaType y sourceUrl, caption, requestedPublishAt y clientRequestId, y la llamada de creación devolviendo 202. Lo que sigue sin confirmar es la respuesta del trabajo: el nombre del array deliveries y el campo failureCategory. Compruébalos en la referencia de la API antes de publicar código que los lea.
La API de Instagram no programa, así que lo programas tú
Este es el dato sobre el que están construidos los productos de calendario, dicho por Meta: la API de publicación de contenido de Instagram “does not natively support scheduling”, no admite programación de forma nativa. Esa sola frase explica por qué existe un mercado de programadores de Instagram.
La programación es, por tanto, algo que asume una capa de publicación, y será tu cron guardando filas hasta que toquen o un campo requestedPublishAt cargando con esa responsabilidad por ti. En la construcción de arriba es lo segundo, lo que significa que el despachador puede empujar la semana entera el lunes y dejar de pensar en ello. Un job que aún no se ha despachado todavía se puede cancelar.
En cualquier caso, conoce los estados que lee tu bucle. Un job pasa por pending, scheduled, processing, preparing, submitted y polling, y después llega a uno de estos: published, partial_failure, failed, action_required o canceled. Cinco finales, no uno, y la diferencia entre tres de ellos es todo el tema de la política de fallos.
Consulta los límites antes de despachar
Un despachador que se entera de una cuota chocándose con ella ya perdió las publicaciones que tenía en la mano. Hay dos techos, y no son el mismo techo.
El de Meta. La documentación de publicación de contenido indica 100 publicaciones por API dentro de un periodo móvil de 24 horas, y un carrusel cuenta como una sola. La cifra antigua de 25 publicaciones al día sigue circulando mucho en textos de terceros y no es lo que dice la documentación hoy, así que presupuesta contra 100 y verifica el número tú mismo antes de construir sobre él.
El tuyo. Publicando con InvisibleAPI, cada cuenta conectada puede publicar 100 posts por periodo de facturación, y un endpoint de límites reporta el uso en vivo para que el despachador lea el margen restante antes de empezar un lote.
El comportamiento útil no es detenerse ante una cola llena. Es publicar lo que cabe y reprogramar el resto después del reinicio, lo que convierte un lote de fallos en un lote que llega un día más tarde. Un control de límites de publicación es esa lógica como receta, y cuánto cuesta publicar en Instagram cubre la pregunta más amplia de contra qué se cuenta el volumen.
Qué sale mal, y qué hace el código al respecto
Cada fallo que ve tu despachador se resuelve en una de cuatro decisiones, y cada una tiene un dueño distinto.
invalid_publish_data es tu error. El caption era demasiado largo, la relación de aspecto estaba fuera de rango, el carrusel tenía demasiados elementos. La misma carga fallará igual para siempre, así que un reintento es cuota tirada. Corrige la fila y crea un job nuevo.
retryable_provider_failure es la plataforma teniendo un mal momento. Media todavía procesándose, un contenedor caducado, un error de servidor. Se reintenta automáticamente, y el trabajo de tu bucle es seguir esperando en lugar de escalar.
provider_action_required es el problema de una persona, normalmente una concesión de credenciales caducada. Ningún bucle de reintentos puede volver a iniciar sesión por alguien. Muéstralo y detente.
platform_software_failure es nuestro. Se enruta a soporte técnico, y tu despachador no hace nada salvo no reintentarlo.
Por eso el switch de abajo se apoya en la categoría y no en un código de error del proveedor. Los códigos se añaden y se reclasifican; una clasificación de cuatro valores no.
const TERMINAL = ["published", "partial_failure", "failed", "action_required", "canceled"];
const NOTIFY = {
invalid_publish_data: "developer",
retryable_provider_failure: null,
provider_action_required: "account_owner",
platform_software_failure: "support",
} as const;
export async function dispatch(row: QueueRow) {
const job = await createJob(row); // clientRequestId = row.row_id
const final = await pollUntil(job.id, (j) => TERMINAL.includes(j.status));
if (final.status === "published") return { row: row.row_id, ok: true };
return final.deliveries.map((d) => ({
target: d.target,
retry: d.failureCategory === "retryable_provider_failure",
notify: NOTIFY[d.failureCategory],
}));
}
Fíjate en que el valor devuelto es por delivery, no por job. Un job puede apuntar a varias cuentas conectadas a la vez, y el mismo contenido puede funcionar en una y fallar en otra. Un caption de 500 caracteres se publica en Instagram y falla la regla de 280 caracteres de X dentro del mismo job, que es exactamente lo que describe partial_failure. (Si X está en tu lista, cuánto cuesta publicar en X cubre su propia tarifa.) Lee las deliveries y el informe dice qué cuenta publicó y cuál no. Lee solo el estado del job y te queda un punto rojo.
Esa distribución es la versión de agencia de esta construcción, cubierta por publicar una vez en varias cuentas conectadas, y la mitad de barrer los fallos es la receta de recuperación de publicaciones fallidas.
Lo que un reintento nunca debe hacer
Este es el escenario que rompe en silencio a los publicadores desatendidos. Tu despachador envía la llamada de creación. La conexión agota el tiempo de espera. No tienes respuesta, y no hay forma de distinguir una petición que falló de una que funcionó y perdió su respuesta por el camino.
Si reintentas puedes duplicar la publicación. Si la saltas puedes perderla en silencio. Un cron que corre cada cinco minutos se encontrará este caso, y se lo encontrará a las 3 de la madrugada.
Una clave de idempotencia elimina la elección. El clientRequestId de la llamada de creación es esa clave, y una petición repetida con el mismo valor no publica dos veces. Por eso la regla es estrecha y absoluta: un reintento reutiliza la clave original. Nunca genera una nueva. Generar un id nuevo dentro de la rama de reintento es la forma más común de que un script con buena pinta produzca publicaciones duplicadas, porque apaga el mecanismo de seguridad justo cuando hacía falta.
Por eso también row_id venía del archivo de la cola y no del despachador. La clave pertenece a la fila, no al intento.
La automatización por la que se restringen cuentas de Instagram
Busca automatización de Instagram y buena parte de lo que vuelve es otra categoría de producto: herramientas que envían mensajes directos automáticos a nuevos seguidores, dan me gusta por hashtag, siguen y dejan de seguir, y librerías que llegan a Instagram iniciando sesión con usuario y contraseña en vez de por la API documentada. Conviene ser preciso sobre por qué esa vía es un riesgo distinto, porque la distinción está publicada y no es cuestión de opinión.
Las Condiciones de la Plataforma de Meta, sección 6.a.iii, dicen que una aplicación “must not separately request or collect a Meta user’s login credentials for any Meta Products”, no debe solicitar ni recopilar por separado las credenciales de acceso de un usuario de Meta. Una librería que inicia sesión como el usuario hace exactamente eso por diseño: es el mecanismo, no un caso extremo. La sección 2.a es más amplia, y dice que salvo licencia expresa “you will not use, access, integrate with, modify, translate, create derivative works of, reverse engineer, or otherwise exploit Platform or any aspect thereof”. Un endpoint no documentado al que se llega haciendo ingeniería inversa de la aplicación móvil no está licenciado expresamente.
La automatización de interacciones cae bajo las Políticas para Desarrolladores. La sección 2 prohíbe participar en “any program that promotes or facilitates the purchase, sale, or exchange of ‘Likes’, ‘Shares’, ‘Followers’, ‘Comments’”, y la sección 5 describe “creating bots either manually or automatically, at very high frequencies” como spam.
La aplicación de las normas tampoco es teórica. La propia referencia de errores de Instagram de Meta incluye códigos para una cuenta restringida y para actividad restringida por sospecha de spam en la publicación. Existen porque hay cuentas que llegan a ese estado.
Nada de esto aplica a la construcción de este artículo. La API de publicación de contenido es la vía documentada, la cuenta la autoriza con el propio flujo OAuth de Instagram, ninguna contraseña llega nunca a tu código, y el límite de uso es un número publicado que puedes consultar antes de despachar. Esa es la razón honesta para tomar la vía oficial, y aguanta mejor que cualquier comparativa de funciones.
Publica el bucle, no otro calendario
Tu cola, tu calendario, tu código, y un job de publicación por fila que te dice exactamente qué pasó con cada una.
Conecta una cuenta profesional de Instagram, crea una API key con scopes y mete la primera fila. Cada cuenta empieza con una prueba gratuita de 7 días, y cómo funciona el precio por volumen explica contra qué se cuenta el uso.
Publica en X y en Instagram desde una sola API.
Conecta las cuentas una vez, crea un job de publicación y consúltalo hasta que quede publicado. La validación previa detecta una publicación inválida antes de que llegue a la plataforma, y un clientRequestId hace que los reintentos sean seguros.
Fuentes
Las tres páginas se consultaron el 14 de septiembre de 2026.
- Guía de publicación de contenido de Instagram, para el límite de 100 publicaciones por periodo móvil de 24 horas, el carrusel contando como una y la ausencia de programación nativa
- Condiciones de la Plataforma de Meta, para las secciones 2.a y 6.a.iii
- Políticas para Desarrolladores de Meta, para las secciones 2 y 5