An open API service indexing awesome lists of open source software.

https://github.com/subzeroid/insto

Interactive Instagram OSINT CLI with pluggable HikerAPI and aiograpi backends
https://github.com/subzeroid/insto

aiograpi analysis cli hikerapi information-gathering instagram instagram-account instagram-api instagrapi intelligence maltego open-source-intelligence osint osint-python prompt-toolkit python repl social-media

Last synced: 7 days ago
JSON representation

Interactive Instagram OSINT CLI with pluggable HikerAPI and aiograpi backends

Awesome Lists containing this project

README

          

# insto

Interactive Instagram OSINT CLI on the [HikerAPI](https://hikerapi.com/p/6k1q1388) backend and [aiograpi](https://github.com/subzeroid/aiograpi) lib.

![demo](docs/demo.gif)

## Design choices

- **HikerAPI is the default backend, not a side mode.** Quota balance is read on REPL startup and surfaced in the bottom toolbar; `with_retry` honours `RateLimited.retry_after`; every mapper raises typed `SchemaDrift(endpoint, missing_field)` when HikerAPI's documented fields move. Logged-in `aiograpi` is one extra (`pipx install 'insto[aiograpi]'`) when you actually need data behind the login wall — but kept off the default path so your account isn't in scope.
- **Async, typed, tested.** Python ≥ 3.11, strict mypy, ~93% coverage, ruff-clean. Backends, facade, commands all `async def`.
- **Two surfaces, one grammar.** A prompt-toolkit REPL with slash-popup completion and live `/watch` notifications, *and* a Unix-friendly one-shot mode (`insto @user -c info`). `--json -` and `--csv -` write to stdout; `/batch -` reads targets from stdin.
- **Snapshot / watch / diff.** Persisted in `~/.insto/store.db`. Poll a target on an interval; diff against the last snapshot.
- **Maltego CSV export** out of the box (`--maltego` on any flat-row command, plus full `/dossier`).

Two surfaces over the same command grammar:

- **REPL** — `insto` drops you into a prompt-toolkit session with tab-completion,
a bottom toolbar (active target, backend, quota), and live `/watch`
notifications. Visually similar to the Claude Code welcome screen.
- **One-shot** — `insto @user -c [args]` runs a single slash-command
and exits. Pipe-friendly: `--json -` writes to stdout, `--csv -` does the
same for flat commands, `/batch -` reads targets from stdin.

## Install

Requires Python ≥ 3.11. Pick the install path that matches how you keep
other CLIs:

```sh
uv tool install insto # uv users — fastest, no venv to manage
pipx install insto # pip users — same effect, classic tool
brew install pipx && pipx install insto # macOS, no Python yet
```

For the optional logged-in `aiograpi` backend (private accounts, posts
behind Instagram's login wall) install with the extra:

```sh
uv tool install 'insto[aiograpi]'
pipx install 'insto[aiograpi]'
```

`insto setup` then offers a `hiker | aiograpi` choice and prompts for
the right credentials. See [`docs/backends.md`](docs/backends.md) for
the trade-offs and the account-ban risk on aiograpi.

> **Got `insto: command not found` after install?** Both `pipx` and
> `uv tool` install into `~/.local/bin`, which is not on `$PATH` by
> default on a fresh Linux box. Fix it once:
>
> ```sh
> pipx ensurepath # or: uv tool update-shell
> exec "$SHELL" # reload PATH in the current session
> insto --version
> ```

Or from a checkout (development):

```sh
git clone git@github.com:subzeroid/insto.git
cd insto
uv sync && uv run insto --help # editable inside .venv
# or: uv tool install --editable . to put `insto` on $PATH
```

> ℹ️ **Bare `pip install insto` does not work on modern systems by default**
> (PEP 668 — Homebrew Python, Debian 12+, Ubuntu 23.04+ all reject system-wide
> pip writes). Use `pipx` or `uv tool install` — both create an isolated
> venv per CLI, no manual sourcing.

## Setup

```sh
insto setup
```

Interactive wizard. Writes `~/.insto/config.toml` (mode `0600`) with your
HikerAPI token, output directory, sqlite store path, and optional proxy.
The token is read with `getpass` so it does not echo to the terminal; pass
`-` for the proxy to clear a previously-saved value.

> 💸 **HikerAPI gives you 100 free requests** to try things out — no card,
> no email-verification dance, just sign up and a token is waiting:
> . That's enough for a couple of full
> `/dossier` runs or many `/info` / `/where` / `/timeline` calls — pick a
> target you already know to test the surface before deciding whether to
> top up.

Token precedence is **flag > env (`HIKERAPI_TOKEN`) > config.toml**; the same
precedence applies to the proxy (`--proxy`, `HIKERAPI_PROXY`,
`[hiker].proxy`). `socks5h://` (Tor) and `http://` proxies are both
supported.

### Environment variables

| Variable | Purpose |
|-------------------|---------------------------------------------------------------------|
| `HIKERAPI_TOKEN` | API token (overrides `[hiker].token` in config.toml) |
| `HIKERAPI_PROXY` | Proxy URL (overrides `[hiker].proxy`) |
| `INSTO_HOME` | Override the default `~/.insto/` config root |
| `INSTO_BACKEND` | `hiker` (default) / `aiograpi` / `fake` (e2e suite). Same as `--backend` and `[backend]` in `config.toml` |

## How insto compares to other Instagram OSINT tools

| | insto | [Osintgram](https://github.com/Datalux/Osintgram) | [Toutatis](https://github.com/megadose/toutatis) | [Sherlock](https://github.com/sherlock-project/sherlock) |
|---|---|---|---|---|
| Backends supported | [HikerAPI](https://hikerapi.com/p/6k1q1388) (default) + aiograpi | Username/password (default) + HikerAPI ([#2586](https://github.com/Datalux/Osintgram/pull/2586)) | Logged-in Instagram session | HTTP probes — multi-site |
| Default backend | HikerAPI — no IG session in scope | Logged-in Instagram session | Logged-in Instagram session | N/A — username probing |
| Scope | Instagram deep dive | Instagram only | Instagram (email/phone focus) | 400+ sites, username search |
| Geo OSINT (`/where`, `/place`, `/placeposts`) | ✅ unique | ❌ | ❌ | ❌ |
| Network ops (`/intersect`, `/mutuals`) | ✅ unique | ❌ | ❌ | ❌ |
| Posting cadence (`/timeline`) | ✅ unique | ❌ | ❌ | ❌ |
| Superfan ranking (`/fans`) | ✅ unique | ❌ partial | ❌ | ❌ |
| Snapshot / watch / diff | ✅ sqlite-backed | ❌ | ❌ | ❌ |
| Maltego CSV export | ✅ | ❌ | ❌ | ❌ |
| Interactive REPL | ✅ prompt-toolkit, slash-popup | ✅ basic shell | ❌ one-shot | ❌ one-shot |
| One-shot / scriptable | ✅ stdin/stdout pipes | ⚠️ shell only | ✅ | ✅ |
| Type-safe / strict mypy | ✅ ~93% coverage | ❌ | ❌ | ❌ |

**When to pick insto** — Instagram-specific OSINT where you want HikerAPI on the default path (no IG session is ever in scope), deep network/geo analytics, snapshots over time, and Maltego-ready export. Modern stack — asyncio, strict mypy, prompt-toolkit REPL with slash-popup completion.

**When to pick Osintgram** — historic OSS standard, large existing community. As of PR [#2586](https://github.com/Datalux/Osintgram/pull/2586) (merged Aug 2025) it can also run through HikerAPI via `HIKERAPI_TOKEN` so you don't have to use a logged-in Instagram account. The default path is still username/password and the command surface is the original Osintgram one — no `/where`, `/intersect`, `/timeline`, or `/fans` analytics.

**When to pick Toutatis** — narrow goal: extract email + phone hints from a target's `user_info`. It's smaller and single-purpose. insto's `/email` and `/phone` cover the same data through HikerAPI without an Instagram login.

**When to pick Sherlock / Maigret** — you're doing username reconnaissance across many platforms (forums, image hosts, code hosts). Different tool category — pair with insto for Instagram depth after Sherlock points you at an account.

Related projects: [aiograpi](https://github.com/subzeroid/aiograpi) (the async Instagram private API library that powers insto's optional `[aiograpi]` backend), [Social-Media-OSINT-Tools-Collection](https://github.com/subzeroid/Social-Media-OSINT-Tools-Collection) (curated tools list), [awesome-osint](https://github.com/subzeroid/awesome-osint).

## Examples

REPL:

```text
$ insto
Tips for getting started
___ _ _ ____ _____ ___ /target set OSINT target
|_ _| \ | / ___|_ _/ _ \ /info full profile dump
| || \| \___ \ | || | | | /help list all commands
| || |\ |___) || || |_| |
|___|_| \_|____/ |_| \___/ Recent activity
@nasa
i n s t o ⇋ o s i n t @instagram
instagram tool · open-source intel
hiker · 14.7M requests left · $4,417 · 15 rps cap

insto @→ /
```

Type `/` and the popup opens with every command (Slack / Claude Code style):

```text
insto @→ /info
> /target ferrari
> /info
> /posts 10 # last 10 feed posts, media saved under output/ferrari/posts/
> /posts 10 --no-download # URLs only, no CDN write
> /followers 500 --csv followers.csv
> /diff
> /watch ferrari 600 # poll every 10 minutes (5 min floor)
> /dossier # collect a full target package
> /quit
```

`/info ` is also valid as inline form — runs the lookup without
mutating the active session target. Same for every single-target
command (`/posts nasa 5`, `/dossier nasa`, ...).

One-shot:

```sh
insto @ferrari -c info
insto -c info instagram # inline target, no REPL state
insto @ferrari -c posts 10 --json - # 10 posts, JSON to stdout
insto @ferrari -c followers 500 --csv followers.csv
insto @ferrari -c followers 200 --maltego # Maltego CSV under output/ferrari/
insto -c search ferrari 20 --maltego # full SERP, no active target needed
insto @nasa -c fans --limit 10 # top fans = ❤️ + 3*💬 across 10 posts
insto @ferrari -c recommended --maltego # IG's "same category" recommendations
cat targets.txt | insto -c batch - info --yes # stdin pipe + non-interactive
insto -c dossier instagram --maltego # full target package, Maltego CSVs per section
```

`-c ` consumes the rest of `argv` as the slash-command's arguments,
so `-c batch targets.txt info` runs `batch targets.txt info` (one `-c`
per invocation). `--yes` is required when `/batch` reads from stdin or
when the target list exceeds the confirmation threshold.

### Global flags

| Flag | Purpose |
|---------------------------------|----------------------------------------------------------|
| `-c / --cmd [args...]` | One-shot mode: run a single slash-command and exit |
| `-i / --interactive` | Force the REPL even when a target is provided |
| `--proxy ` | Override `HIKERAPI_PROXY` for this invocation |
| `--json [PATH or -]` | Write the JSON envelope (default path, file, or stdout) |
| `--csv [PATH or -]` | Same for flat-row commands |
| `--maltego [PATH or -]` | Maltego entity-import CSV (alias for `--output-format maltego`) |
| `--output-format {json,csv,maltego}` | Explicit format selector |
| `--limit N` / `--no-download` | Per-command paging cap and media opt-out |
| `--backend {hiker,aiograpi}` | Backend selector for this invocation (overrides `$INSTO_BACKEND` and `config.toml`) |
| `--no-progress` | Suppress tqdm bars + spinner on long commands (`/fans`, `/wliked`, `/wcommented`, `/dossier`) |
| `--yes / -y` | Skip confirmation prompts (required for `/batch -`) |
| `--verbose` / `--debug` | Logging level for `~/.insto/logs/insto.log` |
| `--version` | Print the version and exit |
| `--print-completion {bash,zsh}` | Emit a shell-completion script |

Pipe to `jq`:

```sh
insto @ferrari -c info --json - | jq '.username, .followers_count'
```

Shell completion (uses `argparse` via `shtab`):

```sh
insto --print-completion zsh > ~/.insto/_insto
echo 'fpath+=~/.insto && autoload -Uz compinit && compinit' >> ~/.zshrc
```

## Command surface

🔥 marks the killer-feature commands — the ones that are uniquely OSINT-positioned and don't have obvious equivalents in other tools.

| Group | Commands | What it does |
|---|---|---|
| **Profile** | `info` `about` `propic` `email` `phone` `export` `pinned` | profile dump, user_about slice, avatar download, contact extraction, JSON export, pinned posts |
| **Media** | `posts` `reels` `reposts` `stories` `highlights` `tagged` `audio` `postinfo` 🔥 | feed media, reposts, stories, highlights, tagged-in, audio-asset → clips, **`postinfo` resolves any URL/code/pk to full Post DTO** |
| **Network** | `followers` `followings` `mutuals` `intersect` 🔥 `similar` `search` 🔥 `recommended` | follower lists, self-intersection, **`intersect` = followers(@a) ∩ followers(@b)**, suggested similar, **`search` = free-text account discovery**, category recommendations |
| **Geo** | `locations` `where` 🔥 `place` 🔥 `placeposts` 🔥 | location frequency, **`where` = geo fingerprint (anchor + centroid + radius)**, **`place` = text → IG locations**, **`placeposts` = top media at a location** |
| **Content** | `hashtags` `mentions` `captions` `likes` `timeline` 🔥 | top hashtags / @mentions / captions, like-count stats, **`timeline` = posting-cadence histogram (hour-of-day + day-of-week)** |
| **Interactions** | `comments` `wcommented` `wliked` `wtagged` `fans` 🔥 | per-post + aggregated comments, top commenters / likers / taggers, **`fans` = weighted superfan ranking (likes + 3×comments)** |
| **Discovery** | `resolve` | expand `instagram.com/share/...` short-links to canonical URLs (aiograpi only) |
| **Direct** | `direct` `direct-thread` | read-only Direct threads and messages (aiograpi only) |
| **Saved** | `collections` `saved` | read-only saved collections and saved posts for the logged-in aiograpi account |
| **Watch / diff** | `watch` `unwatch` `watching` `diff` `history` | poll-based snapshot diffing; cli-history |
| **Operational** | `quota` `health` `config` `purge` | balance + p50/p95 latency + error breakdown, effective config with origins, sqlite/cache cleanup |
| **Session** | `target` `current` `clear` | active-target plumbing for the REPL |
| **Batch / dossier** | `batch` `dossier` 🔥 | run one command across a target list, **`dossier` = full target package (profile + media + network + analytics) with `--maltego` CSV per section** |

Inside the REPL each command may be invoked with or without a leading `/`.

Pretty much every command takes `--limit N` (paging cap) and supports `--json` / `--csv` / `--maltego` export to file or `-` for stdout. Long-running aggregations (`/fans`, `/wliked`, `/wcommented`, `/dossier`) show a tqdm progress bar; everything else gets a `⢿ ...` spinner during the silent setup wait.

## Where things go

- `~/.insto/config.toml` — settings (mode `0600`).
- `~/.insto/store.db` — sqlite store: snapshots, watches, cli history.
- `~/.insto/logs/insto.log` — rotating log file (mode `0600`, secrets redacted).
- `~/.insto/aiograpi.session.json` — persisted Instagram session for the
aiograpi backend (mode `0600`; only created when you pick that backend).
- `./output///…` — downloaded media. Override with
`[output_dir]` in config or `--out` on commands that accept it.

## Documentation

Full docs at :

- [**🔥 Killer features**](https://subzeroid.github.io/insto/killer-features/) — `/dossier`, `/where`, `/intersect`, `/fans`, `/place`, `/postinfo`, `/timeline`, `/search` with real output examples.
- [**📓 OSINT recipes**](https://subzeroid.github.io/insto/recipes/) — concrete investigative scenarios: find someone's likely city, detect sockpuppet rings, evidence-archive a post chain, audit your own footprint, etc.
- [Installation](https://subzeroid.github.io/insto/installation/)
- [Basic usage](https://subzeroid.github.io/insto/basic-usage/)
- [CLI reference](https://subzeroid.github.io/insto/cli-reference/)
- [Backends](https://subzeroid.github.io/insto/backends/)
- [Architecture](https://subzeroid.github.io/insto/architecture/)
- [Troubleshooting](https://subzeroid.github.io/insto/troubleshooting/)

Contributing: see [CONTRIBUTING.md](CONTRIBUTING.md). Security policy: [SECURITY.md](SECURITY.md).

## License

MIT — see [LICENSE](LICENSE).