Usage¶
Docker Usage (Recommended)¶
Running via Docker is the easiest setup for most users and avoids local Python dependency management.
Docker Compose (Easiest)¶
The repo ships a docker-compose.yml that wraps the volume mounts, port and session persistence for you, so you do not have to remember long docker run commands. If you cloned the repo, you already have this file.
If you are starting from an empty directory, download it first:
curl -fsSLO https://raw.githubusercontent.com/misiektoja/instagram_monitor/refs/heads/main/docker-compose.yml
- Generate a config (the wizard sets
WEB_DASHBOARD_HOST = "0.0.0.0"for Docker automatically, so the dashboard is reachable from your host):
- Optionally copy the secrets template and fill it in. If you did not clone the repo, download the template first:
curl -fsSLO https://raw.githubusercontent.com/misiektoja/instagram_monitor/refs/heads/main/.env.example
cp .env.example .env
- Start it:
By default this launches the Web Dashboard as a control panel. Open http://127.0.0.1:8000/ and add targets in the browser. It auto-loads instagram_monitor.conf and .env from the current directory.
To monitor specific targets from the command line instead, override the command:
Docker Hub Image¶
A prebuilt multi-architecture image is available on Docker Hub: misiektoja/instagram-monitor
Run and show help:
Build Image Locally¶
If you want to build from source:
If you prefer the local image, replace misiektoja/instagram-monitor with instagram_monitor:local in the Docker commands below.
Common Run Scenarios¶
Shell note: The examples below use Bash/Zsh variables ($PWD, $HOME). In PowerShell use ${PWD} and ${HOME} or replace them with absolute paths in -v mounts.
- Basic monitoring with persistent data and session storage:
docker run --rm -it --init -v "$PWD:/data" -v instagram_monitor_session:/home/instagram/.config/instaloader misiektoja/instagram-monitor <target_insta_user>
This keeps generated files in your current directory and keeps Instaloader sessions in the Docker volume instagram_monitor_session.
- Use config file and dotenv from your current directory:
docker run --rm -it --init -v "$PWD:/data" -v instagram_monitor_session:/home/instagram/.config/instaloader misiektoja/instagram-monitor <target_insta_user> --config-file /data/instagram_monitor.conf --env-file /data/.env
- Run Web Dashboard and access it from host browser:
Set WEB_DASHBOARD_HOST = "0.0.0.0" in instagram_monitor.conf, then run:
docker run --rm -it --init -v "$PWD:/data" -v instagram_monitor_session:/home/instagram/.config/instaloader -p 8000:8000 misiektoja/instagram-monitor <target_insta_user> --web-dashboard
Open http://127.0.0.1:8000/ on your host.
- Import Firefox session cookies on Linux host:
docker run --rm -it --init -v "$PWD:/data" -v instagram_monitor_session:/home/instagram/.config/instaloader -v "$HOME/.mozilla/firefox:/home/instagram/.mozilla/firefox:ro" misiektoja/instagram-monitor --import-browser-session --browser firefox
- Import Firefox session cookies on macOS host from explicit cookie file:
docker run --rm -it --init -v "$PWD:/data" -v instagram_monitor_session:/home/instagram/.config/instaloader -v "$HOME/Library/Application Support/Firefox/Profiles/<profile>/cookies.sqlite:/cookies/cookies.sqlite:ro" misiektoja/instagram-monitor --import-browser-session --browser firefox --cookie-file /cookies/cookies.sqlite
Firefox is the practical choice inside Docker because its cookies are plain files that can be mounted read-only. Importing from Chrome, Brave or Chromium (--browser chrome|brave|chromium) relies on the host's keyring for decryption, which is not available in the container, so run those imports directly on the host instead.
Once imported, run with -u <your_insta_user> as usual and the session file from the persistent volume will be reused.
Monitoring Mode¶
To monitor specific user activity in No-login mode (no session login), just type Instagram username as a command-line argument (target_insta_user in the example below):
To monitor specific user activity in Logged-in mode (with session login), you also need to specify your Instagram account name (your_insta_user in the example below) via SESSION_USERNAME configuration option or -u flag:
Since v3.0 you can also launch the Web Dashboard along with tracking:
By default, the tool looks for a configuration file named instagram_monitor.conf in:
- current directory
- home directory (~)
- script directory
If you generated a configuration file as described in Configuration, but saved it under a different name or in a different directory, you can specify its location using the --config-file flag:
The tool runs until interrupted (Ctrl+C). Use tmux or screen for persistence.
You can monitor multiple Instagram users in one process by passing multiple target usernames:
Note: You can also add and remove monitoring targets directly via the Web Dashboard without restarting the tool.
To reduce the chance of triggering Instagram anti-bot mechanisms, the tool will stagger the start of each target's monitoring loop (auto-spread across your INSTA_CHECK_INTERVAL by default). You can override it with:
The tool automatically saves its output to an instagram_monitor_<suffix>.log file. It can be changed in the settings via INSTA_LOGFILE configuration option or disabled completely via DISABLE_LOGGING / -d flag.
- In single-target mode,
<suffix>is the username. - In multi-target mode,
<suffix>is the sorted list of target usernames joined with underscores.
The tool in Logged-in mode (session login) also saves the list of followings & followers to these files:
- instagram_<username>_followings.json
- instagram_<username>_followers.json
Thanks to this we do not need to re-fetch it every time the tool is restarted and we can also detect changes since the last usage of the tool.
When downloading lists of followers or followings, a progress bar is displayed showing real-time download progress, including statistics such as names per request, total requests, elapsed time and estimated remaining time. Progress updates are shown in the terminal only (to avoid cluttering log files), with the final completion state written to the log file for reference.
The tool also saves the user profile picture to instagram_<username>_profile_pic*.jpg files.
It also saves downloaded posts/reels images & videos to:
- instagram_<username>_post/reel_YYYYmmdd_HHMMSS.jpg
- instagram_<username>_post/reel_YYYYmmdd_HHMMSS.mp4
And downloaded stories images & videos to:
- instagram_<username>_story_YYYYmmdd_HHMMSS.jpg
- instagram_<username>_story_YYYYmmdd_HHMMSS.mp4
Email Notifications¶
To enable email notifications for various events (such as new posts, reels and stories, changes in followings, bio updates, changes in profile picture and visibility):
- set STATUS_NOTIFICATION to True
- or use the -s flag
- or toggle it via the Settings menu in the Web Dashboard
To also get email notifications about changed followers:
- set FOLLOWERS_NOTIFICATION to True
- or use the -m flag
- or toggle it via the Settings menu in the Web Dashboard
To disable sending an email on errors (enabled by default):
- set ERROR_NOTIFICATION to False
- or use the -e flag
- or toggle it via the Settings menu in the Web Dashboard
Make sure you defined your SMTP settings earlier (see SMTP settings).
Example email:
Webhook Notifications¶
The tool supports webhook notifications (compatible with Discord and other webhook services) for all monitored events (posts, reels, stories, followings, followers, bio, profile visibility, profile picture changes and errors).
1. Configure Discord Webhook¶
If you are new to Discord, follow these steps to get your Webhook URL:
- Create a Server: Click the + (Plus) icon on the left sidebar ("Add a Server") -> Create My Own -> For me and my friends.
- Create/Edit a Channel: In your new server, find the #general channel (or create a new one). Click the Edit Channel icon (⚙️ gear) next to the channel name.
- Create Webhook: Go to Integrations in the left menu -> Webhooks -> New Webhook.
- Copy URL: Click on the new webhook (often named "Spidey Bot", you can rename it) and click Copy Webhook URL.
2. Enable in the Tool¶
- set
WEBHOOK_ENABLEDtoTrueandWEBHOOK_URLto your copied URL ininstagram_monitor.conf - or use an environment variable or a dotenv file for
WEBHOOK_URL - or use the
--webhook-urlflag (alternatively use the--webhookflag if URL is already in config) - or toggle it via the Settings menu in the Web Dashboard
# Enable with URL
instagram_monitor <target_insta_user> --webhook-url "https://discord.com/api/webhooks/..."
# Explicitly enable/disable if URL is in config
instagram_monitor <target_insta_user> --webhook
instagram_monitor <target_insta_user> --no-webhook
3. Test your settings¶
You can verify your configuration by sending a test notification:
# Verify settings from configuration file
instagram_monitor --send-test-webhook
# Verify a specific URL from command line
instagram_monitor --webhook-url "https://discord.com/api/webhooks/..." --send-test-webhook
4. Advanced Configuration¶
By default, all webhook notification types (status, followers, errors) are disabled. You must explicitly enable what you want the tool to send:
- Use
--webhook-statusto toggle status notifications (new posts, reels, stories, bio, visibility, profile pic) - Use
--webhook-followersto toggle follower/following change notifications - Use
--webhook-errorsto toggle error notifications
Example with explicit control:
# Enable webhooks and specifically choose what to send
instagram_monitor <target_insta_user> --webhook-url "..." --webhook-status --webhook-followers --webhook-errors
Configuration file options (all disabled by default):
WEBHOOK_ENABLED = False
WEBHOOK_URL = "https://discord.com/api/webhooks/..."
WEBHOOK_USERNAME = "Instagram Monitor"
WEBHOOK_AVATAR_URL = ""
WEBHOOK_STATUS_NOTIFICATION = False
WEBHOOK_FOLLOWERS_NOTIFICATION = False
WEBHOOK_ERROR_NOTIFICATION = False
Follower Churn Detection¶
When enabled, the tool fetches the full list of followers and followings on every check (not just when counts change) and compares usernames to detect changes. This is useful for scenarios where:
- Someone unfollows and someone else follows at the same time (count stays the same)
- You want to track exactly who followed/unfollowed even without count changes
- You need comprehensive monitoring of all follower/following activity
To enable follower churn detection:
- set FOLLOWERS_CHURN_DETECTION to True
- or use the --followers-churn flag
- or toggle it via the Settings menu in the Web Dashboard
Note: This feature is automatically disabled if SKIP_FOLLOW_CHANGES is active, as detailed tracking is not possible when follow-related reporting is suppressed. It also requires Logged-in mode.
Note: This feature requires Logged-in mode (session login) to access the Instagram API and it will increase API calls since it fetches the full follower/following lists every check interval, so the risk of account suspension is higher.
Skipping Follow Changes¶
If you want to track followers/followings counts in the dashboards, but don't want to get any notifications or logs when they change, you can enable the "Skip Follow Changes" mode.
When enabled: - Notifications: Email and Webhook alerts for follower/following changes are suppressed. - Reporting: Console prints and activity logs for these changes are disabled. - CSV Export: No "Followers Count" or "Followings Count" entries are written to the CSV file. - Performance: High-overhead downloading of full lists is skipped, saving bandwidth and reducing API call volume.
To enable skipping follow changes:
- set SKIP_FOLLOW_CHANGES to True in your config
- or use the --skip-follow-changes flag
- or toggle it via the Settings menu in the Web Dashboard
Advanced Follower/Following Fetching¶
By default the tool fetches the full follower and following lists in one go. On large accounts this can be a strong signal to Instagram's automated detection. To reduce that risk you can fetch them gradually, in batches with a delay in between and up to an optional total cap.
Configure it with these options in instagram_monitor.conf:
# Number of accounts to fetch before pausing (0 = no batching)
FOLLOWERS_PER_BATCH = 0
FOLLOWEES_PER_BATCH = 0
# Delay in seconds between batches (0 = no delay)
FOLLOWER_DELAY_PER_BATCH = 0
FOLLOWEE_DELAY_PER_BATCH = 0
# Total number of accounts to fetch across all batches (0 = no limit)
FOLLOWER_LIMIT_TO_FETCH = 0
FOLLOWEE_LIMIT_TO_FETCH = 0
Depending on which values you set, the tool runs in one of these modes (it prints the active mode on startup and warns if the combination is invalid):
- Disabled: fetch everything at once (default)
- Maximum of N accounts: only
*_LIMIT_TO_FETCHis set - Batches of Y accounts with Z second delay:
*_PER_BATCHand*_DELAY_PER_BATCHare set - Maximum of N accounts in batches of Y with Z second delay: all three are set
Note: This feature requires Logged-in mode (session login).
Routing Traffic Through a Proxy¶
You can route the tool's Instagram traffic (and optionally webhook traffic) through an HTTP or HTTPS proxy. This is useful for pinning the monitor to a stable egress IP or for keeping it on the same network identity over time.
To enable a proxy:
- set PROXY_ENABLED to True and PROXY_URL to your proxy URL
- or use the --enable-proxy and --proxy-url flags
Additional options:
- PROXY_CERT_PATH (or --proxy-cert): path to a local SSL certificate to use for the proxied connection
- PROXY_WEBHOOKS (or --enable-proxy-webhooks): also send webhook POST requests through the proxy (some proxies do not allow POST, so this is off by default)
PROXY_URL may contain credentials, so it is treated as a secret: the tool masks it in all output and you can store it via an environment variable or dotenv file.
PROXY_ENABLED = True
PROXY_URL = "http://user:pass@host:port"
PROXY_CERT_PATH = ""
PROXY_WEBHOOKS = False
Note: Even when PROXY_ENABLED is False, the underlying requests library still honors the HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables. If those are set in your shell or service unit they are applied silently, so unset them if you want a guaranteed direct connection.
HTTP Transport Backend¶
All Instagram traffic flows through a configurable HTTP transport backend:
curl_cffi(default): sends requests via curl_cffi, impersonating a real browser's TLS (JA3/JA4) and HTTP/2 fingerprint. This avoids fingerprint-based blocks where Instagram returns a spuriousHTTP 429on the very first request even from a clean IP, a pattern most often seen on Linux builds (including Raspberry Pi OS) whose system TLS stack presents a fingerprint Instagram treats as automation.requests: the stockrequests/urllib3transport using the system TLS stack (the historical behavior).
Both the no-login and logged-in paths use the selected backend. If curl_cffi is selected but not installed, the tool prints a warning and transparently falls back to requests.
Select the backend with HTTP_BACKEND (or --http-backend) and choose which browser curl_cffi impersonates with CURL_CFFI_IMPERSONATE (or --impersonate):
CURL_CFFI_IMPERSONATE defaults to auto, which picks the impersonation target that matches your USER_AGENT so the TLS, HTTP/2 and client-hint headers stay consistent with the browser identity. This matters when you import a Firefox session and set a matching Firefox USER_AGENT: auto then presents a Firefox TLS fingerprint instead of pairing a Firefox user agent with Chrome client-hint headers. You can also pin a specific target such as chrome, safari, safari_ios, edge or firefox:
See the curl_cffi documentation for the full list of impersonation targets available in your installed version.
Privacy Substitutions¶
If you want to hide or rename identities in everything the tool produces (console output, logs, CSV, emails, webhooks and both dashboards), use privacy substitutions. For example you can replace a real Instagram username with a friendlier label or mask it entirely.
Provide a list of (search, replace) tuples via the PRIVACY_SUBSTITUTIONS config option:
Every occurrence of a search term is replaced with its replacement before anything is displayed, logged or sent. Internal keys and file paths are kept intact, so the substitution affects only what you see, not how the tool locates data. Invalid entries are ignored with a warning.
Shadowban and Flagged Account Detection¶
Instagram sometimes flags a session or IP (challenge, checkpoint or shadowban) instead of a monitored target actually disappearing. When that happens, a profile lookup can fail in a way that looks identical to the target being deleted or renamed, which previously could trigger misleading alerts.
To tell the two apart, the tool probes a canonical, always-present public account (by default instagram) whenever a target lookup fails ambiguously. If that probe also fails, the tool concludes the session/IP is flagged rather than the target being gone and it idles and recovers instead of reporting a false change. When the session can recover it keeps waiting, otherwise it exits cleanly.
This runs automatically. The behavior can be tuned via these config options:
# Canonical public account used to probe whether the session/IP is flagged
FLAGGED_PROBE_USERNAME = "instagram"
# Seconds to reuse a flag-probe result so simultaneous target failures do not each hit the network
FLAGGED_PROBE_TTL = 300
Reducing Jitter Log Noise¶
When Jitter Mode (or debug/verbose output) is enabled, the HTTP back-off wrapper prints a WRAP-REQ / WRAP-SEND line for every request, which can be overwhelming. Set SKIP_WRAP_MESSAGES to True to suppress those per-request lines while keeping the rest of the jitter behavior:
CSV Export¶
If you want to save all Instagram user's activities and profile changes to a CSV file, set CSV_FILE or use -b flag:
The file will be automatically created if it does not exist.
The tool uses the following logic for CSV path resolution:
- Absolute Path:
- Single-target mode: The file is saved exactly where specified.
- Multi-target mode: The absolute path is used as a base; separate files are created for each user (e.g.,
/path/file_user1.csv). Isolation is preserved.
- Relative Path +
OUTPUT_DIR: If you provide a relative path and haveOUTPUT_DIRconfigured, the file is saved in thecsvs/subdirectory:- Single-target mode:
OUTPUT_DIR/csvs/<filename>(uses basename of your input) - Multi-target mode:
OUTPUT_DIR/<username>/csvs/<filename>(uses basename of your input)
- Single-target mode:
- Relative Path + no
OUTPUT_DIR:- Single-target mode: Saved as specified in the current working directory.
- Multi-target mode: One file per user is created in the current working directory using a suffix:
<CSV_FILE_basename>_<username>.csv.
Output Directory¶
By default, the tool saves all generated files (JSON, images, videos, logs) in the current working directory.
You can specify a custom root directory for all output files using the -o / --output-dir flag or OUTPUT_DIR configuration option:
The tool will organize files into subdirectories:
-
Output structure: The layout depends on whether you monitor one or multiple users:
-
Single-target mode: All files are organized into subdirectories directly under
OUTPUT_DIR:OUTPUT_DIR/images/OUTPUT_DIR/videos/OUTPUT_DIR/json/OUTPUT_DIR/logs/OUTPUT_DIR/csvs/
-
Multi-target mode: Each user gets their own isolated subdirectory:
OUTPUT_DIR/<username>/images/OUTPUT_DIR/<username>/videos/OUTPUT_DIR/<username>/json/OUTPUT_DIR/<username>/logs/OUTPUT_DIR/<username>/csvs/
Common messages (like the summary screen or global errors) are automatically broadcasted to all active log files.
This helps keep your files organized, especially when monitoring multiple users.
Detection of Changed Profile Pictures¶
The tool can detect when a monitored user changes their profile picture. Notifications appear in the console and (if the -s flag is enabled) via email.
This feature is enabled by default. To disable it, either:
- set the
DETECT_CHANGED_PROFILE_PICtoFalse - or use the
-kflag - or toggle it via the Settings menu in the Web Dashboard
How It Works¶
Since Instagram periodically changes the profile picture URL even when the image is the same, the tool performs a binary comparison of JPEG files to detect actual changes.
On the first run, it saves the current profile picture to instagram_<username>_profile_pic.jpg
On each subsequent check a new image is fetched and it is compared byte-for-byte with the saved image.
If a change is detected, the old picture is moved to instagram_<username>_profile_pic_old.jpg and the new one is saved to:
- instagram_<username>_profile_pic.jpg (current)
- instagram_<username>_profile_pic_YYmmdd_HHMM.jpg (for history)
Empty Profile Picture Detection¶
The tool also has built-in detection of empty profile pictures. Instagram does not indicate an empty user's profile image in their API; that's why the tool detects it by using an empty profile image template (which appears to be identical on a binary level for all users).
To enable this:
- download the instagram_profile_pic_empty.jpg file
- place it in the directory where you run the tool. Note: If installed via pip, this file is already bundled; however, any local file in your working directory will take priority over the bundled default.
Without this file, the tool will treat an empty profile picture as a regular image. For example, if a user removes their profile picture, it would be treated as a change rather than a removal.
Detecting Collab Posts on Private Accounts¶
Instagram's collaboration feature lets two accounts co-author a single post. When a private account co-authors a post with a public account, that post stays visible on the private account's profile through the public web_profile_info endpoint even though the rest of the account is hidden. The tool surfaces these otherwise hidden posts.
This feature is enabled by default. To disable it, either:
- set the
DETECT_COLLAB_POSTStoFalse - or use the
--no-detect-collab-postsflag
Collab Posts - How It Works¶
The probe runs only for accounts whose posts are not otherwise viewable, meaning private profiles you do not follow.
On the first run the tool displays the newest collab post currently visible, the same way it shows a regular account's latest post and records a baseline so it does not re-alert on the ones already there. On later checks, when the account's post or reel count changes, it looks for newly leaked collab posts and reports each one with its date, owner, collaborators, likes, comments, caption and media through the console, email and webhook notifications. Media is saved like any other post.
This was inspired by InstagramPrivSniffer. Meta has confirmed that this visibility is intended behavior of the collaboration feature rather than a vulnerability. Use it only for legitimate research and investigation.
Displaying Images in Your Terminal¶
If you have imgcat installed, you can use the feature of displaying profile pictures and stories/reels/posts images right in your terminal.
To do this, set the path to your imgcat binary in the IMGCAT_PATH configuration option.
If you specify only the binary name, it will be auto-searched in your PATH.
Set it to empty to disable this feature.
Check Intervals¶
If you want to customize polling interval, use -c flag (or INSTA_CHECK_INTERVAL configuration option):
Note: You can also adjust check intervals and randomization timers live via the Settings menu in the Web Dashboard.
It is generally not recommended to use values lower than 1 hour as it will be quickly picked up by Instagram automated tool detection mechanisms.
In order to make the tool's behavior less suspicious for Instagram, by default the polling interval is randomly picked from the range:
[ INSTA_CHECK_INTERVAL (-c) - RANDOM_SLEEP_DIFF_LOW (-i) ]
⇄
[ INSTA_CHECK_INTERVAL (-c) + RANDOM_SLEEP_DIFF_HIGH (-j) ]
This means each check will happen after a random delay centered around INSTA_CHECK_INTERVAL with some variation defined by RANDOM_SLEEP_DIFF_LOW and RANDOM_SLEEP_DIFF_HIGH.
So having the check interval set to 1 hour (-c 3600), RANDOM_SLEEP_DIFF_LOW set to default 15 mins (-i 900) and RANDOM_SLEEP_DIFF_HIGH set to default 3 mins (-j 180) means that the check interval will be with every iteration picked from the range of 45 mins to 1 hour and 3 mins.
That's why the check interval information is printed in the console and email notifications as it is essentially a random number.
On top of that you can also define that fetching updates should be done only in specific hour ranges by setting CHECK_POSTS_IN_HOURS_RANGE to True and then defining proper values for MIN/MAX_H1/H2 configuration options (see Use Hour-Range Checking for more information).
Signal Controls (macOS/Linux/Unix)¶
The tool has several signal handlers implemented which allow to change behavior of the tool without a need to restart it with new configuration options / flags.
List of supported signals:
| Signal | Description |
|---|---|
| USR1 | Toggle email notifications for new posts, reels & stories, changed followings, bio, profile picture, visibility (-s) |
| USR2 | Toggle email notifications for new followers (-m) |
| TRAP | Increase the user activity check interval (by 5 mins) |
| ABRT | Decrease the user activity check interval (by 5 mins) |
| HUP | Reload secrets from .env file |
Send signals with kill or pkill, e.g.:
As Windows supports limited number of signals, this functionality is available only on Linux/Unix/macOS.
Coloring Log Output with GRC¶
The tool has native color output support for terminal since v3.0 (see COLORED_OUTPUT and COLOR_THEME config options), but you can also use GRC to color logs.
Add to your GRC config (~/.grc/grc.conf):
Now copy the conf.monitor_logs to your ~/.grc/ and log files should be nicely colored when using grc tool.
Example: