DEV Community

Cover image for Why your `fetch()` request fails on Instagram (and how to fix TLS Fingerprinting)
Olamide Olaniyan
Olamide Olaniyan

Posted on

Why your `fetch()` request fails on Instagram (and how to fix TLS Fingerprinting)

It's not your IP. It's your handshake.*

You write a Python script to scrape a public Instagram profile.
It works locally.
You deploy it to AWS Lambda.
It fails immediately. 403 Forbidden or Login Required.

You buy expensive residential proxies.
It still fails.

Why?

The answer is TLS Fingerprinting.

What is TLS Fingerprinting?

When your code connects to an HTTPS server (like instagram.com), it performs a "TLS Handshake".

During this handshake, your client sends a ClientHello packet. This packet contains:

  • Cipher suites supported
  • TLS versions supported
  • Elliptic curves supported
  • Header order

Here is the problem:

  • Chrome's ClientHello looks like A.
  • Python's requests ClientHello looks like B.
  • Node's axios ClientHello looks like C.

Instagram's firewall (WAF) looks at this packet. If it sees a request that claims to be "User-Agent: Chrome" but has the TLS fingerprint of "Python Requests", it blocks it instantly.

This is called JA3 Fingerprinting.

How to verify this

Go to tls.peet.ws in your browser. You'll see a hash.
Now run this in Python:

import requests
print(requests.get("https://tls.peet.ws/api/all").json()['ja3_hash'])
Enter fullscreen mode Exit fullscreen mode

The hashes are different. That's how they catch you.

How to fix it

Option 1: Use a specialized library (The Hard Way)

You need a library that can spoof the TLS handshake.

Python: Use curl_cffi. It wraps curl-impersonate to mimic Chrome's TLS signature.

from curl_cffi import requests

# Impersonate Chrome 110
response = requests.get(
    "https://www.instagram.com/zuck/",
    impersonate="chrome110"
)
Enter fullscreen mode Exit fullscreen mode

Node.js: Use got-scraping.

import { gotScraping } from 'got-scraping';

const response = await gotScraping({
    url: 'https://www.instagram.com/zuck/'
});
Enter fullscreen mode Exit fullscreen mode

Option 2: Use an API (The Easy Way)

Even with TLS spoofing, you still have to deal with:

  • IP Rotation (Datacenter IPs are blocked)
  • Canvas Fingerprinting (if using Puppeteer)
  • Behavioral Analysis

If you don't want to maintain this cat-and-mouse game, use a dedicated scraping API like SociaVault.

We handle the TLS spoofing, proxy rotation, and header management for you.

curl "https://api.sociavault.com/v1/scrape/instagram/profile?handle=zuck" \
  -H "x-api-key: YOUR_KEY"
Enter fullscreen mode Exit fullscreen mode

Conclusion

If your scraper is failing, stop buying more proxies. Check your TLS fingerprint first.

In 2025, looking like a browser is more important than acting like one.

webscraping #python #security #javascript #api

Top comments (0)