Guard Labs Стенд · Instagram Graph API
Instagram · Graph API · автопубликация

Как устроена публикация в Instagram — изнутри, без скриншотов

Ниже разобран путь поста от текста и картинки до опубликованной записи, а конвейер на нашей стороне — очередь, планировщик, предохранители и журнал — работает по-настоящему: можно нажать и посмотреть, что ответит сервер.

Сразу о главном, чтобы не тратить ваше время. Сданных проектов на Meta Graph API у нас пока нет — вместо выдуманного кейса мы собрали этот стенд. Реальную публикацию в чужой Instagram показать невозможно: она требует Advanced Access на разрешение instagram_content_publish, который Meta выдаёт после App Review, а это недели и решение не наше.

Поэтому живое здесь — всё, что на нашей стороне: очередь, планировщик, шесть предохранителей и журнал. Последний шаг конвейера работает в режиме dry-run: вместо запроса к graph.facebook.com он пишет в журнал точный запрос, который ушёл бы. Ни одного «успешно опубликовано» там, где ничего не публиковалось.

01

Конвейер, который можно потрогать

Соберите пост и поставьте его в очередь. Предохранители считаются на сервере, а не в браузере — их нельзя обойти правкой этой страницы. Планировщик заберёт задачу, когда наступит время, и напишет в журнал каждый шаг с настоящей меткой времени.

Бэкенд стенда

проверяем связь…

Предохранители

1
Пост утверждён человеком
Проверяется на сервере при постановке в очередь.
2
Целевой аккаунт в белом списке
Список аккаунтов задаётся заранее и вручную.
3
Суточный лимит не исчерпан
Свой счётчик, ниже лимита Meta.
4
Подпись проходит правила Instagram
2200 символов, 30 хэштегов, не пусто.
5
Это не повтор
Защита от двойной отправки после сбоя.
6
Картинка доступна по публичному https
Meta скачивает файл сама.

Очередь и журнал

сервер хранит записи 24 часа, потом чистит
Пока пусто. Поставьте пост в очередь — здесь появится задача и её журнал, строка за строкой.
Что здесь настоящее, а что нарисовано. Настоящие: очередь и планировщик (отдельный сервис на нашем сервере, SQLite, фоновый поток), все шесть предохранителей, повторы с нарастающей паузой, метки времени и журнал — они считаются на сервере и переживают перезагрузку страницы. Нарисованных элементов на этом экране нет вообще: единственное, чего не происходит, — сетевой вызов к graph.facebook.com на последнем шаге. Он помечен в журнале как dry-run, и рядом написан ровно тот запрос, который ушёл бы.
Второй независимый свидетель. Каждая постановка в очередь дополнительно уходит в наш давно работающий трекер событий guardlabs.online/api/track — тот пишет строку в свою SQLite и возвращает её номер и время обратно. Номер видно в карточке задачи. Код ответа 200 сам по себе не доказывает ничего, а перечитанная из базы строка — доказывает.
02

Путь поста: от текста до записи в ленте

Instagram не принимает пост одним запросом. Публикация всегда двухшаговая: сначала создаётся медиа-контейнер, потом отдельной командой он публикуется. Между ними — ожидание, и именно там ломается большинство самописных интеграций.

подготовка
Текст и картинка
Картинка должна лежать по публичному https-адресу: Meta скачивает её сама, загрузки файлом в этом API нет.
POST /media
Контейнер
Создаём медиа-контейнер, в ответ приходит creation_id. Пост ещё не виден никому.
GET status_code
Ожидание
Спрашиваем состояние контейнера, пока не станет FINISHED. Поспешить нельзя — публикация упадёт.
POST /media_publish
Публикация
Отдаём creation_id, получаем идентификатор поста и сохраняем его у себя — по нему потом снимается статистика.
Facebook — отдельная история. У страницы Facebook публикация одношаговая (запись или фото уходят прямо в ленту страницы) и требует своего разрешения — pages_manage_posts, которое проходит App Review отдельно от инстаграмного. У Meta Business Suite, через который многие ведут обе площадки руками, публичного API нет вообще: автоматизировать «через Business Suite» нельзя, только через Graph API.

Что ломается и что мы с этим делаем

Что вернул MetaКлассПоведение конвейера
status_code = IN_PROGRESS повторяем Контейнер ещё обрабатывается. Ждём с нарастающей паузой (1 → 2 → 4 с) и спрашиваем снова. Публиковать раньше времени — гарантированная ошибка. Это поведение можно увидеть в стенде выше, выбрав «контейнер обрабатывается дольше».
status_code = ERROR / EXPIRED останавливаемся Контейнер испорчен или протух — повторять его бессмысленно. Задача уходит в «нужен человек», очередь идёт дальше, пост не теряется.
Лимит запросов исчерпан повторяем Meta считает запросы по скользящему окну. Отступаем, ждём и продолжаем — вместо того чтобы долбить API и получить блокировку приложения.
Исчерпан лимит публикаций (50 за 24 ч) откладываем Документированный потолок Instagram на аккаунт. Свой счётчик держим заведомо ниже, чтобы упереться в него у себя, а не у Meta.
OAuthException, код 190 зовём человека Токен недействителен или истёк. Повтор только сожжёт лимит и ничего не починит: нужен перевыпуск долгоживущего токена руками. Очередь встаёт, приходит уведомление. Тоже можно проверить в стенде выше.
Сеть отвалилась на середине безопасно Худший случай в любой автопубликации — отправить пост дважды. Поэтому creation_id сохраняется до публикации, а повтор с тем же текстом в коротком окне блокируется предохранителем.
03

Своё хранилище статистики — иначе сравнивать будет не с чем

Это неочевидная часть, из-за которой отчёты «было — стало» через полгода обычно оказываются невозможными.

!
Подписчики — только 30 дней

Метрика follower_count отдаётся Instagram лишь за последние 30 дней. Спросить «сколько было в марте» в июле уже не у кого.

!
Охваты живут коротким окном

Показы, охват и сохранения доступны в узком окне после публикации. Не забрали вовремя — цифры исчезли навсегда.

Решение — снимки по расписанию

Раз в сутки складываем срез в свою базу. Через год у вас есть история, которой у самого Instagram уже нет.

схема хранилища снимков
-- срез аккаунта: снимается ежесуточно, строка в день
CREATE TABLE account_snapshot (
  captured_at   TIMESTAMP,   -- когда сняли, UTC
  account       TEXT,        -- какой аккаунт
  followers     INTEGER,     -- 🔴 у Meta живёт 30 дней, у нас — всегда
  reach_1d      INTEGER,
  profile_views INTEGER
);

-- срез поста: чаще в первые дни, потом реже
CREATE TABLE post_snapshot (
  captured_at TIMESTAMP,
  media_id    TEXT,          -- тот самый id из шага media_publish
  published   TIMESTAMP,
  reach       INTEGER,
  saves       INTEGER,       -- сохранения: главный сигнал полезности
  comments    INTEGER,
  likes       INTEGER
);

-- расписание: +1 ч, +24 ч, +7 дн после публикации, дальше раз в месяц
-- и ежедневный срез аккаунта в 04:00 — вне зависимости от публикаций
Что это даёт на практике. Отчёт «за квартал подписчиков стало больше на столько, лучший пост дал столько сохранений» строится по своей базе за секунду и не зависит от того, что Meta решит отдать сегодня. Плюс сравнение с конкурентами: их публичные счётчики тоже можно снимать по расписанию и хранить рядом. Схема выше — план, а не работающая база: снимать статистику реального аккаунта можно только после подключения к нему, которого у нас на этом стенде нет.
04

Почему оно не напишет ерунду вашим клиентам

Самый честный страх при автоматизации соцсетей — что система однажды опубликует не то, не туда или сто раз подряд. Разбираем по пунктам, что этому мешает. Все шесть проверок — те же самые, что срабатывают в стенде выше.

1
Ничего без подтверждения

Пост уходит в очередь только с явной отметкой «утверждено». Черновик, сгенерированный текст, правка задним числом — всё это ждёт человека. Автопубликация без подтверждения не включается вообще, даже по вашей просьбе «пусть само».

2
Белый список аккаунтов

Публиковать можно только туда, что внесено в конфиг заранее. Опечатка в имени аккаунта или подмена в данных не приведут к посту в чужой ленте — задача просто не пройдёт.

3
Свой лимит ниже лимита Meta

У Instagram потолок 50 публикаций в сутки на аккаунт. Наш счётчик стоит заметно ниже: упереться лучше в свой предохранитель, чем в чужую блокировку.

4
Проверка подписи

Длина до 2200 символов, не больше 30 хэштегов, пустой текст не пропускается. Это правила самого Instagram — нарушение означает отказ уже после отправки.

5
Защита от двойной отправки

Один и тот же текст в коротком окне блокируется. Классическая авария автопостинга — сеть моргнула, скрипт повторил, в ленте два одинаковых поста — здесь не проходит.

6
Проверка картинки до отправки

Ссылка должна быть публичной и на https, иначе Meta не заберёт файл. Проверяем до постановки в очередь, а не после падения на середине.

Где живут предохранители. На сервере, до отправки, а не в интерфейсе. Правка страницы в браузере их не отключает — в этом легко убедиться в стенде выше: снимите галку подтверждения, и задача не уйдёт, сколько бы раз вы ни нажали кнопку. Заодно каждое срабатывание попадает в журнал: видно не только что опубликовано, но и что было остановлено и почему.
05

Чего мы не обещаем

Короткий список, который обычно узнают уже после оплаты. Лучше прочитать сейчас.

Живых сданных проектов на Meta API у нас нет
Мы написали это в ставке и повторяем здесь. Опыт с внешними API, очередями, вебхуками и расписаниями — есть, и он на этом стенде виден. Опыт именно с Meta Graph API — пока нет. Кейс с чужим логотипом мы не поставим.
Сроки App Review нам не подчиняются
Разрешения instagram_content_publish и pages_manage_posts Meta выдаёт после проверки приложения: нужен бизнес-аккаунт, привязанная страница, видео работы приложения и описание сценария. Это недели ожидания, и ускорить их не может никто. Код к этому моменту может быть давно готов — но обещать дату запуска, зависящую от Meta, мы не будем.
Обход правил Meta — не за какие деньги
Рассылки в личные сообщения в обход правила 24 часов, накрутка, работа через неофициальные библиотеки и чужие сессии, массовые подписки — мы этого не делаем. Не из осторожности: такие схемы заканчиваются блокировкой аккаунта, который вы годами набирали, и чинить это потом придётся вам.
Роста охватов мы не гарантируем
Автоматизация снимает ручную работу и даёт историю цифр, которой иначе не будет. Сколько людей увидит пост — решает алгоритм Instagram, а не наш код. Любой, кто называет вам цифру прироста заранее, её выдумал.
06

Что дальше

Если механика подходит — вот соседние стенды и способ проверить нас на своей задаче.

О данных на этой странице. Ни одного скриншота чужого аккаунта, ни одной цифры охватов, ни одного отзыва здесь нет — у нас их нет, а выдумывать мы не станем. Аккаунты @guardlabs.demo и @client.main в форме — условные имена для проверки предохранителей, ни один реальный профиль этим стендом не затрагивается. Идентификаторы контейнера и поста в журнале сгенерированы на нашем сервере и помечены как dry-run: запросы к Meta не отправляются, потому что App Review не пройден. Живое на странице — очередь, планировщик, предохранители, повторы и журнал: отдельный сервис на сервере Guard Labs. Страница сделана как ответ на конкретный вопрос заказчика «а вы это уже делали» — и как честная замена кейсу, которого у нас пока нет.