homlity / sdk-instagram
SDK PHP de Homlity para la Instagram Graph API con Instagram Login: OAuth embebido, tokens de larga duracion renovables y publicacion de imagenes, carruseles y reels. Sin dependencias de runtime.
Requires
- php: ^8.1
- ext-curl: *
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-09-11 10:19:28 UTC
README
Homlity · Portal de desarrolladores
SDK de Instagram para PHP
Cliente de la Instagram API with Instagram Login: conexión de cuentas por OAuth, tokens de larga duración que se renuevan solos y publicación de imágenes, carruseles y reels.
Sin dependencias de runtime: PHP 8.1, ext-curl y ext-json.
Por qué «con Instagram Login» y no «con Facebook Login»
Meta ofrece dos caminos para publicar en Instagram. Este SDK habla el primero:
| Instagram Login | Facebook Login | |
|---|---|---|
| Qué hace el usuario | Entra con su cuenta de Instagram | Entra con Facebook y elige página |
| Requiere página de Facebook | No | Sí |
| Requiere Business Manager | No | Sí |
| Tipo de cuenta | Business o Creator | Business |
Para una inmobiliaria que sólo quiere publicar sus inmuebles, el segundo camino significa crear una página de Facebook, vincularla a Instagram y pasar por Business Manager antes de poder empezar. El primero es entrar con su usuario y contraseña de Instagram de siempre.
Instalación
composer require homlity/sdk-instagram
Uso
1. Conectar una cuenta
use Instagram\Sdk\Auth\Scope; use Instagram\Sdk\Auth\State; use Instagram\Sdk\Config; use Instagram\Sdk\InstagramClient; $config = new Config( appId: getenv('INSTAGRAM_APP_ID'), appSecret: getenv('INSTAGRAM_APP_SECRET'), redirectUri: 'https://tu-dominio.com/instagram/callback', ); $client = new InstagramClient($config); // El `state` firmado lleva de qué cuenta es la conexión y vuelve intacto. $state = State::create(['tenant_id' => 77], getenv('APP_KEY')); header('Location: ' . $client->oauth()->authorizationUrl(Scope::publishing(), $state));
En el callback:
$payload = State::verify($_GET['state'], getenv('APP_KEY')); // lanza si no cuadra $token = $client->oauth()->connect($_GET['code']); // token de 60 días $account = InstagramClient::withToken($config, $token)->account()->me(); if (! $account->isPublishable()) { // Cuenta personal: la API de publicación no funciona sobre ella. } guardar($payload['tenant_id'], $token->toArray(), $account->toArray());
2. Publicar
$client = InstagramClient::withToken($config, $token); $media = $client->publisher()->publishCarousel( $account->id(), ['https://cdn.tu-dominio.com/1.jpg', 'https://cdn.tu-dominio.com/2.jpg'], "Apartamento en El Poblado\n3 hab · 2 baños · 92 m²\n#medellin #arriendo" ); echo $media->permalink();
publishCarousel() crea un contenedor por imagen, espera a que Instagram
termine de descargarlas y sólo entonces publica. Publicar sin esa espera
falla de forma intermitente: funciona con imágenes pequeñas y falla con las
grandes.
Con una sola URL publica una foto normal, porque Instagram no admite carruseles de un elemento.
3. Mantener vivo el token
use Instagram\Sdk\Auth\AccessToken; use Instagram\Sdk\Auth\RefreshingTokenProvider; $provider = new RefreshingTokenProvider( $client->oauth(), AccessToken::fromStorage($guardado), fn (AccessToken $nuevo) => guardar($tenantId, $nuevo->toArray()), // ← imprescindible ); $client = new InstagramClient($config, $provider);
El token vive 60 días y se puede estirar otros 60 tantas veces como se quiera,
pero sólo mientras no haya caducado: si expira, la única salida es que el
usuario vuelva a autorizar. Por eso el proveedor renueva con siete días de
margen y por eso el callback $onRefresh no es opcional en la práctica: sin él
el token renovado se pierde al terminar la petición.
Lo que hay que hacer una sola vez en Meta
Nada de esto lo puede automatizar el SDK. Está detallado en
docs/configuracion-meta.md:
- Crear una app en developers.facebook.com y añadirle el producto Instagram.
- Registrar la URI de retorno (HTTPS obligatorio).
- Registrar los avisos de desautorización y de borrado de datos, que Meta
exige y este SDK verifica con
SignedRequest. - Pedir en App Review los permisos
instagram_business_basiceinstagram_business_content_publish.
Hasta que Meta apruebe la revisión, la app sólo funciona con las cuentas añadidas como testers en el panel.
Requisitos de las imágenes
Instagram se descarga las imágenes desde sus servidores, así que las URLs
tienen que ser públicas: una URL firmada que caduque, detrás de VPN o que exija
Authorization no le sirve.
| Formato | JPEG |
| Tamaño | hasta 8 MB |
| Proporción | entre 4:5 y 1.91:1 |
| Carrusel | de 2 a 10 imágenes |
| Pie de foto | 2200 caracteres, 30 hashtags |
| Cuota | 100 publicaciones cada 24 h |
$client->media()->publishingLimit() dice cuántas van consumidas.
Errores
Cada situación tiene su excepción, para poder decidir sin leer mensajes:
| Excepción | Cuándo | Qué hacer |
|---|---|---|
OAuthException |
El code caducó, el state no cuadra, el token no se puede refrescar |
Rehacer la conexión |
AuthException |
Token inválido o revocado | Reconectar la cuenta |
PermissionException |
Falta un permiso, o la cuenta no es Business | Revisar App Review o el tipo de cuenta |
MediaException |
Imagen inaccesible, formato o proporción no admitidos | Corregir la imagen |
RateLimitException |
Cuota de la app o las 100 publicaciones diarias | Reintentar más tarde |
ValidationException |
Parámetro inválido | Corregir la llamada |
TransportException |
Red, DNS, TLS, timeout | Reintentar |
ApiException expone errorCode(), errorSubcode(), providerMessage(),
userMessage() y traceId() —este último es lo único que el soporte de Meta
pide para investigar una incidencia.
Ni el token ni el app secret aparecen nunca en mensajes de excepción,
var_dump() ni logs.
Tests
composer install && composer test
Mantenido por Homlity · homlity.com/desarrolladores