Reverse-engineering an undocumented API: what I learned
A small project to practice a specific skill: reverse-engineer a website's API flow, automate it in Node, and go from "it runs" to actually understanding why it runs. This post covers the skills involved, the real difficulties, and why I only ever pointed it at my own test environment.
The problem
A modern website is rarely just static HTML: there's a session cookie, an auth token embedded in a client-side JS bundle, a CSRF token, and a bot-detection layer in front of it all (Cloudflare, in this case). The goal was to understand the full path a legitimate request has to take, then reproduce it in code: skip the browser where it isn't needed and use it only where it's required.
Skills this project exercised
- Reading network traffic and reverse-engineering the auth flow: from the session cookie, to where an OAuth token is embedded in a client-side JSON payload, to a CSRF token sitting in a meta tag.
- Knowing when a real browser is required and when it isn't: the bot-detection layer only blocks the first step (establishing a session), so only that step runs headless Playwright; everything after uses plain HTTP requests, which are far lighter and faster.
- Designing a controlled task loop: from
setInterval, to a callback wrapped in a Promise, to a sequentialforloop with sleeps. Each step fixed a real problem: execution order, error handling, the ability to stop midway. - Debugging with purposeful logging: when a header was computed correctly but attached in the wrong place, the way to find it was detailed request/response logging, not guessing.
Difficulties
- Wrong protocol. The URL was
httpsbut the first version used Node'shttpmodule. Check assumptions before debugging deeper. - Ordering dependencies between steps. The OAuth token needs a valid cookie, the CSRF token needs the HTML to have loaded, and the final request needs both. Get the order wrong and the whole chain fails.
- Where to draw the line between headless browser and plain HTTP. Running everything through Playwright is slow and resource-heavy. Running everything through plain HTTP can't get past the session-setup step. The boundary has to be drawn exactly right.
The limit I set for myself
This script defaults to localhost, a test environment I built myself, not a real site. The reason: the same flow
aimed at a real community platform, repeated many times with a fresh session each time, would be spam or engagement
manipulation, and a Terms of Service violation whether or not the technique works. Understanding how a protection
layer works doesn't mean it's okay to bypass it on someone else's system. I kept this project at the level of a
technical exercise and didn't extend it into a tool aimed at someone else's production system without clear consent.