Перейти к содержимому
Socialift
Техническое руководство

Как опубликовать историю Instagram через API

История публикуется через Instagram API так же, как обычный пост: POST /<IG_ID>/media создаёт контейнер с параметром media_type=STORIES, а POST /<IG_ID>/media_publish его публикует. Отдельного endpoint для историй нет, параметра caption тоже нет, а контейнер истекает через 24 часа. Изображение принимается только в JPEG и до 8 МБ, видео — от 3 до 60 секунд.

Обновлено:

Как опубликовать историю через Instagram API?

Два вызова. Первый — POST /<IG_ID>/media с image_url или video_url и media_type=STORIES. Второй — POST /<IG_ID>/media_publish с creation_id. Для видео перед вторым вызовом дождитесь статуса FINISHED. Meta скачивает файл сама, поэтому ссылка должна быть публичной HTTPS.

Другого значения media_type для историй не нужно: фото это или видео, определяет то, какой параметр вы передали — image_url или video_url. В одном контейнере они вместе не работают.

История живёт 24 часа. Контейнер — тоже: неопубликованный контейнер переходит в EXPIRED, и повторно его использовать нельзя.

Требования к медиа для истории

Эти требования не проверит ни SDK, ни кабинет — файл загружает Meta на своей стороне. При несоответствии контейнер уходит в ERROR.

Требования к медиа для истории — Таблица возможностей по платформам
ЧтоФотоВидео
ФорматJPEG (только)MOV или MP4
Размердо 8 МБдо 100 МБ
Длительностьнет3–60 секунд
Соотношениерекомендуется 9:16от 0.1:1 до 10:1, рекомендуется 9:16
КодекsRGBH264 или HEVC, звук AAC 48 кГц
Кадрынет23–60 FPS

естьнет

PNG и WebP не принимаются — конвертируйте в JPEG заранее. Если ссылка требует авторизации или уже истекла, Meta не сможет забрать файл.

Сырые запросы: контейнер и публикация

Без SDK поток выглядит ровно так же. В первом ответе приходит id контейнера, он же становится creation_id во втором запросе.

story.sh
# 1) Контейнер истории
curl -X POST "https://graph.facebook.com/v26.0/$IG_ID/media" \
  -d "image_url=https://cdn.example.com/story.jpg" \
  -d "media_type=STORIES" \
  -d "access_token=$TOKEN"
# -> {"id":"17998...."}

# 2) Для видео — дождитесь готовности контейнера
curl -G "https://graph.facebook.com/v26.0/17998...." \
  -d "fields=status_code" -d "access_token=$TOKEN"
# -> {"status_code":"FINISHED"}

# 3) Публикация
curl -X POST "https://graph.facebook.com/v26.0/$IG_ID/media_publish" \
  -d "creation_id=17998...." -d "access_token=$TOKEN"

В схеме Instagram Login базовый адрес — graph.instagram.com, в схеме Facebook Login — graph.facebook.com; имена endpoint одинаковые.

Для фото ждать не нужно: контейнер обычно готов сразу. Для видео не вызывайте media_publish, пока status_code не станет FINISHED.

Почему у истории нет параметра caption

В Content Publishing API caption — это текст поста в ленте. История в ленту не попадает, поля описания у неё нет, поэтому caption, отправленный вместе с media_type=STORIES, нигде не отображается.

Стикеров в API тоже нет: ссылку, опрос, вопрос, геометку, музыку, @упоминание и хештег Graph API не добавляет. Если в истории нужен текст или ссылка, их придётся вшить в само изображение (JPEG) или в кадры видео.

По этой причине в Socialift SDK метод publish_story() намеренно не принимает caption — места под несуществующий параметр не оставлено.

Разрешения для двух схем входа

У Instagram две схемы авторизации, и наборы scope у них разные. Страница Facebook нужна только в одной из них.

Разрешения для двух схем входа — Таблица возможностей по платформам
ДействиеInstagram LoginFacebook Login
Публикация историиinstagram_business_basic + instagram_business_content_publishinstagram_basic + instagram_content_publish + pages_read_engagement
Список активных историйнет — edge отсутствуетinstagram_basic + pages_read_engagement
Токентокен пользователя (graph.instagram.com)токен страницы (graph.facebook.com)
Страница Facebookне нужнанужна связанная страница

В схеме Facebook Login, если роль на странице выдана через Business Manager, дополнительно потребуется ads_management или ads_read.

Кто может читать список активных историй

Истории не попадают в edge media — для них Meta отдаёт отдельный GET /<IG_ID>/stories. В ответе обычные объекты IG Media, а признак истории — media_product_type == "STORY": поле media_type и для истории возвращает IMAGE или VIDEO, то есть по нему отличить нельзя.

Главное: в документации Meta этот edge помечен как «Instagram API with Facebook Login only». С токеном Instagram Login его просто нет, и официального способа получить список активных историй в этой схеме тоже нет. Поэтому Socialift SDK в таком случае поднимает ConfigurationError, не отправляя запрос — это честнее, чем ждать от Meta путающую ошибку «nonexisting field».

В списке только последние 24 часа: ни архива, ни «актуального» (highlights) в API нет. Истории прямых эфиров и истории, созданные при репосте пользователем, тоже не возвращаются.

Дневной лимит: в самой документации две разные цифры

История выходит через тот же media_publish, то есть попадает в лимит публикаций — в документации нет ни одной фразы, которая выводила бы истории из-под него.

Но само число на разных страницах Meta отличается: на общей странице Content Publishing указано 100 постов за скользящие 24 часа, а на страницах media_publish и content_publishing_limit — 50. Поэтому не зашивайте цифру в код.

Правильный путь — спросить значение: GET /<IG_ID>/content_publishing_limit возвращает quota_usage (сколько постов вышло за окно) и объект config с quota_total и quota_duration. Необязательный параметр since принимает Unix-время не старше 24 часов. Читайте лимит из ответа и притормаживайте очередь по нему.

У контейнеров есть свой отдельный предел: до 400 контейнеров за скользящие 24 часа. Неопубликованный контейнер истекает через сутки, так что создавать их заранее пачками бессмысленно.

Публикация истории через Socialift SDK

SDK собирает создание контейнера, ожидание и публикацию в один вызов и возвращает container_id и media_id.

ig_story.py
import asyncio, os
from socialift import ContainerNotReadyError, Instagram

CDN = "https://cdn.example.com"

async def main():
    token = os.environ["IG_USER_TOKEN"]
    async with Instagram.from_env() as ig:
        photo = await ig.publish_story(token, image_url=f"{CDN}/story.jpg")
        try:
            clip = await ig.publish_story(token, video_url=f"{CDN}/story.mp4", wait_timeout=120.0)
        except ContainerNotReadyError as exc:
            print("контейнер ещё не готов:", exc.container_id)
            return
        active = await ig.stories(token, limit=25)
        print(photo["media_id"], clip["media_id"], len(active))

asyncio.run(main())

publish_story принимает ровно один из image_url и video_url — если передать оба, будет ConfigurationError.

Для видео контейнер ожидается до wait_timeout секунд (по умолчанию 300). По истечении времени приходит ContainerNotReadyError, и по его container_id можно продолжить без повторной загрузки видео: контейнер живёт 24 часа.

Для схемы Facebook Login создайте клиент через Instagram.via_facebook(app_secret=...), передавайте токен страницы и ig_user_id=.

stories() работает только в схеме Facebook Login; с токеном Instagram Login SDK вернёт ConfigurationError, не отправляя запрос.

История страницы Facebook: фото и видео

Истории страницы вообще не используют контейнерный поток Instagram — здесь два собственных протокола.

  1. Фото: сначала неопубликованное фото

    Edge photo_stories принимает только photo_id и не принимает ссылку. Поэтому сначала вызывается POST /<PAGE_ID>/photos?published=false&url=<ссылка> — он возвращает только id, и фото не появляется в ленте.

  2. Фото: публикация как истории

    С полученным id вызывается POST /<PAGE_ID>/photo_stories?photo_id=<id>. Неопубликованное фото хранится на серверах Facebook около 24 часов, после чего photo_id перестаёт работать.

  3. Видео: открыть сессию

    POST /<PAGE_ID>/video_stories?upload_phase=start — в ответе приходят video_id и upload_url. Адрес upload_url ведёт на rupload.facebook.com и берётся из ответа, а не собирается вручную.

  4. Видео: загрузка файла

    POST на тот самый upload_url. Токен здесь передаётся не в query, а в заголовке Authorization: OAuth <token>. При ссылке — заголовок file_url и пустое тело; при файле — заголовки offset и file_size, а в теле байты.

  5. Видео: завершение

    POST /<PAGE_ID>/video_stories?upload_phase=finish&video_id=<id>. Перед этим дождитесь uploading_phase.status == "complete" через GET /<VIDEO_ID>?fields=status.

История страницы: пример SDK и обрыв загрузки

SDK выполняет оба потока внутри: два шага для фото, три для видео и докачку при обрыве.

page_story.py
import asyncio, os
from socialift import Facebook

async def main():
    page_id = os.environ["FB_PAGE_ID"]
    page_token = os.environ["FB_PAGE_TOKEN"]
    async with Facebook.from_env() as fb:
        photo = await fb.publish_photo_story(
            page_id, page_token, photo_url="https://cdn.example.com/story.jpg"
        )
        clip = await fb.publish_video_story(
            page_id, page_token, video="/srv/media/story.mp4", upload_attempts=3
        )
        state = await fb.video_story_status(clip["video_id"], page_token)
        print(photo["post_id"], state.get("publishing_phase"))

        for item in await fb.stories(page_id, page_token, limit=25):
            print(item["post_id"], item["status"])

asyncio.run(main())

publish_photo_story принимает ровно один из photo_url и photo_id. Второй нужен, если первая попытка оборвалась на шаге публикации: перезаливать фото не придётся.

publish_video_story при обрыве запрашивает uploading_phase.bytes_transfered и продолжает с этого байта (до upload_attempts попыток). Файл не отправляется заново с начала.

Разрешения: pages_manage_posts, pages_read_engagement, pages_show_list и задача CREATE_CONTENT на странице.

Требования к видео: .mp4, H.264 или H.265, звук AAC 48 кГц; соотношение 9:16; разрешение от 540x960 до 1080x1920; 24–60 FPS. По длительности документация Meta противоречит себе: в одном месте 3–90 секунд, в другом — не более 60; безопасный диапазон — 3–60 секунд.

Частые ошибки и как их избежать

  • Фото в PNG или WebP — для истории Instagram принимается только JPEG.
  • Подписанная ссылка истекла или требует входа — Meta забирает файл именно в момент вызова.
  • media_publish вызван до того, как контейнер видео стал FINISHED.
  • В один контейнер переданы и image_url, и video_url.
  • Ожидается caption или стикер — API их не добавляет.
  • GET /<IG_ID>/stories вызван с токеном Instagram Login — там этого edge нет.
  • Дневной лимит зашит числом в коде — читайте его из content_publishing_limit.
  • Видео истории страницы ниже 540p — ошибка приходит не на третьем шаге, а в статусе после загрузки.
  • Фото страницы пролежало больше суток: неопубликованный photo_id уже истёк.

← Назад в блог

Частые вопросы

Какие разрешения нужны для публикации истории через Instagram API?

В схеме Instagram Login — instagram_business_basic и instagram_business_content_publish. В схеме Facebook Login — instagram_basic, instagram_content_publish и pages_read_engagement, с токеном страницы. Если роль на странице выдана через Business Manager, дополнительно нужны ads_management или ads_read.

Почему нельзя добавить подпись к истории?

История не пост в ленте, поля описания у неё нет. В Content Publishing API caption относится только к постам ленты; переданный вместе с media_type=STORIES он нигде не появится. Если нужен текст, вшивайте его в изображение или видео.

Можно ли добавить стикер или ссылку через API?

Нет. Graph API не поддерживает стикеры историй — ссылку, опрос, вопрос, геометку, музыку, @упоминание и хештег. Они добавляются только из приложения Instagram.

Как получить список активных историй?

Через GET /<IG_ID>/stories, но этот edge существует только в схеме Facebook Login. С токеном Instagram Login его нет. В списке только последние 24 часа: архива и «актуального» в API нет.

Сколько историй в сутки можно опубликовать?

История попадает в тот же лимит media_publish, что и обычный пост, но сама документация Meta даёт две цифры: 100 на общей странице и 50 на страницах media_publish и content_publishing_limit. Не зашивайте число в код — читайте quota_total и quota_usage из GET /<IG_ID>/content_publishing_limit.

История страницы Facebook отличается от истории Instagram?

Да, полностью. У страницы нет контейнера: фото сначала загружается с published=false и публикуется по photo_id через photo_stories, а видео проходит через upload_phase=start, загрузку на rupload.facebook.com и upload_phase=finish.

Источники

  1. Meta: Publish content using the Instagram Platform
  2. Meta: IG User Stories reference
  3. Meta: Page Stories API
  4. Meta: Instagram API with Facebook Login

Связанные страницы