DEV Community

irtiza
irtiza

Posted on

I built a small Instagram OSINT checker for public profiles

What can I learn from an Instagram profile without logging in, scraping pages manually, or turning a quick check into a whole research project?

That was the question behind a small tool I built for OSINT research.

The goal wasn't to build a giant intelligence platform. I wanted something much smaller: give the tool a public Instagram username, fetch the available profile data, and make it easier to inspect things like public stories during an investigation.

For the API layer, I used HikerAPI
, a REST API for Instagram with pricing starting at $0.001 per request and 100 free requests.

The basic experiment

I started with the smallest possible Python test.

import requests

headers = {"x-access-key": "YOUR_KEY"}

user = requests.get(
"https://api.hikerapi.com/v2/user/by/username?username=apple",
headers=headers
).json()

resp = requests.get(
"https://api.hikerapi.com/v2/user/stories",
params={"user_id": user["pk"]},
headers=headers
)

print(resp.json())

There are two requests here.

First, I resolve the username to a user object and get its pk. Then I use that ID to request the user's stories.

That distinction matters because the second endpoint expects the internal user ID rather than the username.

Turning it into a tiny research tool

Once the request worked, I wrapped the same idea in a small CLI.

The basic flow is:

username

resolve public profile

get user ID

request available stories

print structured response

I kept the output deliberately boring. For OSINT work, I generally prefer raw, inspectable data over a UI that tries to decide what is important for me.

A minimal version looks like this:

import argparse
import json
import requests

API_URL = "https://api.hikerapi.com/v2"
API_KEY = "YOUR_KEY"

headers = {
"x-access-key": API_KEY
}

def get_user(username):
response = requests.get(
f"{API_URL}/user/by/username",
params={"username": username},
headers=headers,
timeout=20,
)
response.raise_for_status()
return response.json()

def get_stories(user_id):
response = requests.get(
f"{API_URL}/user/stories",
params={"user_id": user_id},
headers=headers,
timeout=20,
)
response.raise_for_status()
return response.json()

def main():
parser = argparse.ArgumentParser(
description="Inspect publicly available Instagram data."
)
parser.add_argument("username")
args = parser.parse_args()

user = get_user(args.username)
stories = get_stories(user["pk"])

print(json.dumps({
    "user": user,
    "stories": stories,
}, indent=2))
Enter fullscreen mode Exit fullscreen mode

if name == "main":
main()

Now the workflow is simply:

python osint_check.py public_username

I can pipe the resulting JSON into other tools later if I need to, rather than making the CLI responsible for the entire investigation.

What I actually wanted from it

The interesting part for me wasn't "getting Instagram data."

It was reducing the friction between a research question and a repeatable check.

For example, when investigating a public account, I might want to answer questions such as:

Does this username resolve to a public profile?

What profile information is currently exposed?

Are there currently available public stories?

Can I save the API response for later analysis?

Can I repeat the same check consistently across several public accounts?

The tool doesn't try to infer someone's identity or make conclusions from the data. It just gives me a structured starting point from publicly accessible information.

The part that was harder than expected

The harder part wasn't making the HTTP requests.

It was keeping the tool's assumptions straight.

A username and a user ID are not interchangeable, and API responses can contain considerably more information than the small field I initially cared about. Once I started treating the response as structured research data rather than something to immediately print to the terminal, error handling and output structure became much more important.

I also had to resist the temptation to turn a small script into a giant framework.

For this use case, a predictable CLI that does a couple of things well is more useful to me than a dashboard full of features I may never use.

Where I want to take it

The next useful step would be adding optional JSON output to a file, timestamps for each collection, and a simple way to compare two observations of the same public profile.

That would make the tool more useful for longitudinal research without changing its basic purpose.

For now, though, I'm happy with the experiment.

It's a small Python wrapper around a REST API, but it answers the question I started with: can I make routine public-profile checks quicker and more reproducible without building a whole OSINT platform?

Yes.

And sometimes that's enough for a useful tool.

Top comments (0)