Скачивание6 мин чтения
Как скачать сторис Instagram на Python без входа
Скачивание сторис Instagram на Python без логина: короткий скрипт на requests и API StoriesFly, повторный запуск не качает дубли, плюс строка для cron.
Содержание
Как скачать сторис Instagram на Python без входа в аккаунт?
Отправьте запрос к API сторис через requests, возьмите список stories из JSON-ответа и сохраните каждый media_url в файл. В API StoriesFly это один GET на /v1/user/stories с ключом в заголовке. Аккаунт Instagram, пароль и cookies не нужны. Скрипт ниже занимает около 60 строк и использует только requests и стандартную библиотеку.
StoriesFly — это наш API, поэтому примеры на нём. Тот же подход подойдёт для любого сервиса, который отдаёт ссылки на сторис. Статья про публичные аккаунты.
Что понадобится
- Python 3.9 или новее
- Пакет
requests - API-ключ StoriesFly
Установка:
pip install requestsКлюч берётся на /developers/dashboard: войдите через Telegram. Новому аккаунту дают 100 бесплатных запросов, один раз на Telegram-аккаунт, этого хватит, чтобы попробовать всё из статьи. Ключ выглядит как sf_live_.... Документация на английском: /developers/docs, метод сторис описан на /developers/docs/user-stories.
Не храните ключ в коде. Скрипт читает его из переменной окружения STORIESFLY_KEY.
Полный скрипт
Сохраните как download_stories.py:
"""Download every story an Instagram account has up right now.
Usage: STORIESFLY_KEY=sf_live_... python download_stories.py natgeo
"""
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
import requests
API = "https://storiesfly.com/api/v1"
KEY = os.environ["STORIESFLY_KEY"]
def get_stories(username: str) -> list[dict]:
r = requests.get(
f"{API}/user/stories",
params={"username": username},
headers={"X-Api-Key": KEY},
timeout=60,
)
if r.status_code != 200:
# Every error is JSON: {"error": "...", "code": "..."}
err = r.json()
raise SystemExit(f"{r.status_code} {err['code']}: {err['error']}")
print("Requests charged:", r.headers.get("X-Units-Charged"))
return r.json()["stories"]
def download(url: str, path: Path) -> None:
with requests.get(url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(chunk_size=1 << 16):
f.write(chunk)
def main() -> None:
username = sys.argv[1].lstrip("@")
stories = get_stories(username)
if not stories:
print(f"@{username} has no stories right now.")
return
folder = Path("stories") / username
folder.mkdir(parents=True, exist_ok=True)
for story in stories:
ext = "mp4" if story["media_type"] == "video" else "jpg"
when = datetime.fromtimestamp(story["taken_at"], tz=timezone.utc)
path = folder / f"{when:%Y-%m-%d_%H-%M-%S}_{story['id']}.{ext}"
if path.exists():
continue # already saved on an earlier run
download(story["media_url"], path)
print("saved", path)
if __name__ == "__main__":
main()Как запустить
Задайте ключ и передайте имя пользователя, с @ или без:
export STORIESFLY_KEY=sf_live_your_key_here
python download_stories.py natgeoВывод выглядит так (значения примерные):
Requests charged: 1
saved stories/natgeo/2026-10-21_08-14-52_3412345678901234567.mp4
saved stories/natgeo/2026-10-21_09-02-10_3412345678901234999.jpgФайлы попадают в stories/<username>/. Если активных сторис нет, скрипт напишет об этом и завершится.
Что возвращает API
GET /v1/user/stories?username=natgeo отвечает JSON с массивом stories. У каждого элемента такие поля:
| Поле | Значение |
|---|---|
id | Идентификатор сторис |
media_type | video или photo |
media_url | Ссылка на файл |
taken_at | Unix-время публикации |
video_duration | Длина в секундах, для видео |
Пример элемента с выдуманными значениями:
{
"id": "3412345678901234567",
"media_type": "video",
"media_url": "https://...",
"taken_at": 1792570492,
"video_duration": 12.4
}Полная схема есть на странице метода и в OpenAPI-спецификации https://storiesfly.com/openapi.json.
Как скрипт работает с файлами
Видео или фото. Расширение зависит от media_type: mp4 для video, jpg для остального.
Имена файлов. Файл называется <дата>_<время>_<id>.<расширение>, например 2026-10-21_08-14-52_3412345678901234567.mp4. Время берётся из taken_at в UTC, поэтому файлы сортируются в порядке публикации. Id разводит две сторис, выложенные в одну секунду.
Повторный запуск пропускает сохранённое. Имя строится из данных, которые у сторис не меняются, поэтому одна и та же сторис всегда получает один и тот же путь. Если файл уже есть, скрипт идёт дальше без скачивания. Запускать его можно сколько угодно, скачиваются только новые сторис.
Качайте сразу. media_url ведёт на CDN Instagram, и такие ссылки со временем перестают работать. Скрипт скачивает файлы в том же запуске, где получил список. Не храните ссылки на потом.
Ошибки. Любая ошибка приходит как JSON {"error": "...", "code": "..."}. Скрипт печатает статус и code и останавливается. Частые коды: not_found (нет такого пользователя), private_account, unauthorized (неверный ключ), credits_exhausted и rate_limited (подождите, сколько указано в заголовке Retry-After, и повторите).
Запуск каждые несколько часов
Сторис исчезают через 24 часа после публикации. Чтобы вести архив, запускайте скрипт по расписанию. В Linux или macOS выполните crontab -e и добавьте строку:
0 */4 * * * cd /home/you/story-archive && STORIESFLY_KEY=sf_live_your_key_here /usr/bin/python3 download_stories.py natgeo >> run.log 2>&1Она запускает скрипт в нулевую минуту каждого четвёртого часа. Один запуск стоит один запрос на аккаунт, скачивание файлов с CDN не считается, так что для одного аккаунта расписание раз в четыре часа даёт шесть запросов в сутки. Если аккаунтов несколько, пройдите по списку имён в shell-скрипте или оберните main() в цикл. Тогда добавьте паузу между аккаунтами, чтобы не упереться в лимит: 1 запрос в секунду до первого пополнения и 3 в секунду на уровне Start.
Хайлайты
Хайлайты остаются в профиле и не пропадают через сутки. Чтобы их сохранить, вызовите /v1/user/highlights?username=..., получите список хайлайтов, затем /v1/highlight с id нужного хайлайта, чтобы получить его элементы. Скачиваются они так же, как сторис. Подробности в документации: user highlights и highlight.
Чем это лучше Instaloader и скриптов с логином
Инструменты с входом в Instagram, например Instaloader, работают через сессию настоящего аккаунта. У этого есть цена. Аккаунт, с которого идут автоматические запросы, могут отправить на проверку (checkpoint) или заблокировать, а сессию и cookies приходится поддерживать самому. Если аккаунт вам дорог, это риск. С API сторис это не ваша забота: ваш скрипт превращается в обычный HTTP-запрос с ключом. Взамен вы платите за сервис и зависите от провайдера. Выбирайте то, что подходит вашему проекту.
Закрытые аккаунты
Сторис закрытых аккаунтов так скачать нельзя. API отвечает private_account (HTTP 403) и не отдаёт закрытый контент. Такие сторис видят только одобренные подписчики, и инструмент без входа обойти это не может.
Цена
Большинство вызовов считаются как один запрос. Оплата по факту с баланса в долларах, пополнить его можно криптой, включая TON и USDT. Ответы not_found и private_account тарифицируются как обычные данные. Сбои на стороне сервера (upstream_unavailable, internal_error) возвращаются. Актуальные цены на /developers/pricing.
Правила использования
Используйте скрипт для публичных данных и соблюдайте условия Instagram и законы вашей страны. Уважайте авторов: не публикуйте чужие сторис без разрешения.
Частые вопросы
Можно ли скачать сторис Instagram на Python без аккаунта?
Да. Скрипт обращается к API StoriesFly по ключу. Аккаунт Instagram или Facebook, логин и cookies не нужны.
Скачает ли скрипт одну сторис дважды при повторном запуске?
Нет. Файлы называются по времени публикации и id, а существующий путь скрипт пропускает. При повторе скачиваются только новые сторис.
Сколько запросов тратит один запуск?
Один запрос к /v1/user/stories на аккаунт. Скачивание файлов идёт с CDN Instagram и не считается. Стоимость вызова видна в заголовке X-Units-Charged.
Можно ли скачать сторис закрытого аккаунта?
Нет. API вернёт private_account и не отдаст контент. Работают только открытые аккаунты.
Зачем запускать скрипт по расписанию?
Сторис удаляются через 24 часа. Запуск раз в несколько часов сохраняет каждую сторис до того, как она исчезнет.
Работает ли скрипт с фото-сторис, а не только с видео?
Да. media_type бывает video или photo: видео сохраняется как .mp4, фото как .jpg.
