Instagram Reels Auto Uploader in Python (2024)

← All projects

Python script · 2024

Post a random video to Instagram every day

I made this script to keep re-uploading the 50 videos I had, hoping to get pushed by the algorithm. It pulls a random video from an AWS S3 bucket and uploads it to Instagram as a Reel. Captions and hashtags are added randomly. You can use it for free and change it to fit your needs.

LanguagePython
NeedsAWS S3 bucket, Instagram account with API access
Limit2 uploads per day
PriceFree

What the script does

  1. Checks the log file to see how many videos were uploaded today. After 2, it stops.
  2. Picks a random video from your S3 bucket.
  3. Picks a random caption from a text file and three random hashtags from a list in the script.
  4. Creates the Reel through the Instagram Graph API, publishes it and writes today’s date to the log.
  5. Waits 2 minutes before the next upload.

This script only handles Instagram. I did not include a YouTube uploader here.

Setup

Put these files in one folder:

  • instagram_uploader.py: the script below
  • aws_config.txt: one key=value per line with aws_access_key_id, aws_secret_access_key and region
  • instagram_config.txt: one key=value per line with INSTAGRAM_ACCOUNT_ID and ACCESS_TOKEN
  • descriptions.txt: one caption per line
  • upload_log.txt: leave it empty, the script fills it

Install the libraries with pip install requests boto3, replace the bucket name and hashtags in the script, and run it once a day, for example with a scheduler.

Good to know

  • Instagram has to download the video from a public URL. The script builds a public S3 link, so the videos in your bucket must be publicly readable.
  • Never publish your AWS or Instagram keys, and keep the config files out of any public repository.
  • The script calls version 18 of the Meta Graph API, which I used in 2024. Meta changes its API, so check the current documentation if uploads fail.
  • Some messages in the script are in German, because I wrote it for my own use.
  • Posting the same videos over and over may go against the platform’s rules or hurt your reach, so use it carefully.

I plan to make a YouTube video that walks through each step, and I have started a guidebook that explains how to get the credentials and what the code does in detail. I just haven’t had the time to finish it yet.

The script

import requests
import time
import os
import random
import boto3
import urllib.parse
from datetime import datetime
 
# Skriptverzeichnis ermitteln und dorthin wechseln
current_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(current_dir)
 
# S3-Bucket-Name
S3_BUCKET_NAME = "testbucket"

# Log-Datei
LOG_FILE = "upload_log.txt"
 
# Hashtags
HASHTAGS = [
    "example", "car", "beauty", "shopping", "fashion", "trend", "nature"
 
]
 
def load_instagram_config():
    config_file = "instagram_config.txt"
    try:
        with open(config_file, "r") as f:
            config_data = {}
            for line in f:
                key, value = line.strip().split("=")
                config_data[key.strip()] = value.strip()
            return config_data
    except (FileNotFoundError, ValueError):
        return None
 
def load_aws_config():
    config_file = "aws_config.txt"
    try:
        with open(config_file, "r") as f:
            config_data = {}
            for line in f:
                key, value = line.strip().split("=")
                config_data[key.strip()] = value.strip()
            return config_data
    except (FileNotFoundError, ValueError):
        return None
 
def get_random_video_from_s3():
    aws_config = load_aws_config()
    if not aws_config:
        return None
 
    aws_access_key_id = aws_config.get("aws_access_key_id")
    aws_secret_access_key = aws_config.get("aws_secret_access_key")
    region_name = aws_config.get("region")
 
    s3 = boto3.client(
        's3',
        aws_access_key_id=aws_access_key_id,
        aws_secret_access_key=aws_secret_access_key,
        region_name=region_name
    )
 
    try:
        objects = s3.list_objects_v2(Bucket=S3_BUCKET_NAME).get('Contents', [])
        if not objects:
            return None
 
        random_object = random.choice(objects)
        video_key = random_object['Key']
        video_key = urllib.parse.quote(video_key)
        video_url = f"https://{S3_BUCKET_NAME}.s3.amazonaws.com/{video_key}"
        return video_url
    except Exception:
        return None
 
def get_random_caption():
    captions_file = "descriptions.txt"
    try:
        with open(captions_file, "r") as f:
            captions = f.readlines()
            if not captions:
                return ""
            return random.choice(captions).strip()
    except (FileNotFoundError, ValueError):
        return ""
 
def get_random_hashtags():
    return " ".join(random.sample(HASHTAGS, 3))
 
def upload_video_to_instagram(video_url):
    instagram_config = load_instagram_config()
    if not instagram_config:
        return False
 
    INSTAGRAM_ACCOUNT_ID = instagram_config.get("INSTAGRAM_ACCOUNT_ID")
    ACCESS_TOKEN = instagram_config.get("ACCESS_TOKEN")
 
    container_url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_ACCOUNT_ID}/media"
    caption = get_random_caption()
    hashtags = get_random_hashtags()
    caption_with_hashtags = f"{caption}\n\n{hashtags}"
 
    container_data = {
        "media_type": "REELS",
        "video_url": video_url,
        "caption": caption_with_hashtags,
        "access_token": ACCESS_TOKEN,
        "share_to_feed": "true"
    }
 
    try:
        container_response = requests.post(container_url, data=container_data)
        if container_response.status_code != 200:
            return False
 
        creation_id = container_response.json().get("id")
        if not creation_id:
            return False
 
        publish_url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_ACCOUNT_ID}/media_publish"
        publish_data = {
            "creation_id": creation_id,
            "access_token": ACCESS_TOKEN
        }
 
        publish_response = requests.post(publish_url, data=publish_data)
        return publish_response.status_code == 200
    except Exception:
        return False
 
def count_uploads_today():
    """Zählt die Anzahl der heutigen Uploads anhand der Log-Datei."""
    if not os.path.exists(LOG_FILE):
        return 0
     
    today_date = datetime.now().date()
    try:
        with open(LOG_FILE, "r") as f:
            uploads = [line.strip() for line in f.readlines()]
            # Nur Einträge zählen, die vom heutigen Tag sind
            return sum(1 for entry in uploads if entry == str(today_date))
    except Exception:
        return 0
 
def log_upload():
    """Loggt den Upload mit dem aktuellen Datum."""
    with open(LOG_FILE, "a") as f:
        f.write(f"{datetime.now().date()}\n")
         
         
def main():
    # Prüfen, wie viele Videos heute schon hochgeladen wurden
    uploads_today = count_uploads_today()
     
    if uploads_today >= 2:
        print("Tageslimit erreicht. Skript wird beendet.")
        return  # Skript direkt beenden
     
    while True:
        video_url = get_random_video_from_s3()
        if video_url:
            success = upload_video_to_instagram(video_url)
            if success:
                log_upload()  # Upload loggen
                uploads_today += 1
                print(f"Video erfolgreich hochgeladen! ({uploads_today}/2 heute)")
                 
                # Nach dem Hochladen nochmal prüfen
                if uploads_today >= 2:
                    print("Tageslimit erreicht. Skript wird beendet.")
                    return
                 
                time.sleep(120)  # 2 Minuten warten
        else:
            time.sleep(120)  # 2 Minuten warten, wenn kein Video gefunden wurde
             
if __name__ == "__main__":
    main()

This post may contain affiliate links. These links are marked with an * and I may earn a commission from qualifying purchases.