# Как скачать сторис Instagram на Python без входа

> Скачивание сторис Instagram на Python без логина: короткий скрипт на requests и API StoriesFly, повторный запуск не качает дубли, плюс строка для cron.

- URL: https://storiesfly.com/ru/blog/download-instagram-stories-python
- Language: Русский (ru)
- English version: https://storiesfly.com/blog/download-instagram-stories-python
- Published: 2026-10-21
- Topic: Downloading
- Publisher: StoriesFly (https://storiesfly.com)

## Как скачать сторис 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

Установка:

```bash
pip install requests
```

Ключ берётся на [/developers/dashboard](/developers/dashboard): войдите через Telegram. Новому аккаунту дают 100 бесплатных запросов, один раз на Telegram-аккаунт, этого хватит, чтобы попробовать всё из статьи. Ключ выглядит как `sf_live_...`. Документация на английском: [/developers/docs](/developers/docs), метод сторис описан на [/developers/docs/user-stories](/developers/docs/user-stories).

Не храните ключ в коде. Скрипт читает его из переменной окружения `STORIESFLY_KEY`.

## Полный скрипт

Сохраните как `download_stories.py`:

```python
"""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()
```

## Как запустить

Задайте ключ и передайте имя пользователя, с `@` или без:

```bash
export STORIESFLY_KEY=sf_live_your_key_here
python download_stories.py natgeo
```

Вывод выглядит так (значения примерные):

```text
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` | Длина в секундах, для видео |

Пример элемента с выдуманными значениями:

```json
{
  "id": "3412345678901234567",
  "media_type": "video",
  "media_url": "https://...",
  "taken_at": 1792570492,
  "video_duration": 12.4
}
```

Полная схема есть на [странице метода](/developers/docs/user-stories) и в 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` и добавьте строку:

```bash
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](/developers/docs/user-highlights) и [highlight](/developers/docs/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](/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`.
