July 21, 2026
Reverse Engineering APIs with Chrome DevTools: A Bug Bounty Practical Guide
Every modern web application is powered by APIs. Whether you’re hunting for your first bug bounty or you’re a seasoned pentester, the…

By MD Mehedi Hasan
10 min read
Every modern web application is powered by APIs. Whether you're hunting for your first bug bounty or you're a seasoned pentester, the ability to reverse engineer an undocumented API is one of the most critical skills in your toolkit. The frontend has to talk to the backend somehow — and Chrome DevTools is the window through which you can observe, manipulate, and weaponize that conversation.
This guide is a deep, practical walkthrough. We'll go far beyond just "open the Network tab and look at XHR requests." We'll cover the full pipeline: passive observation, endpoint discovery, parameter extraction, request manipulation, automation, and how to chain these techniques into finding real vulnerabilities.
1. The Mindset: Think Like the Application
Before you open a single tool, understand this: the frontend is a client, just like Postman or curl. The browser doesn't have any special privileges — it sends HTTP requests, receives responses, and renders them. Your job is to observe those requests, understand their structure, and then replicate them with modifications.
Every API endpoint the frontend uses is an endpoint you can test. Every parameter the frontend sends is a parameter you can fuzz. Every cookie, token, and header is something you can manipulate.
2. The Network Tab: Your Primary Battlefield
2.1 Setting Up for Success
Open DevTools — Ctrl+Shift+I (Windows/Linux) or Cmd+Option+I (Mac) — and navigate to the Network tab.
Before you do anything else, check these boxes:
☑ Preserve Log — Keeps requests across page navigations
☑ Disable Cache — Fresh requests every time, no stale data☑ Preserve Log — Keeps requests across page navigations
☑ Disable Cache — Fresh requests every time, no stale dataSet the recording dot to red (it should be by default). Now filter intelligently:
Pro tip: Use negative filters too. -domain:google-analytics.com -domain:sentry.io strips out analytics noise when hunting.
2.2 Reading the Initiator Stack
One of the most underutilized features in the Network tab is the Initiator column. Hover over it and you'll see the exact line of JavaScript that triggered the request. Shift-click to lock the stack trace.
This is gold for two reasons:
- It tells you which function called the API — you can set a breakpoint there and inspect the arguments before they're sent.
- You can trace the data flow: where does the user input get processed before being stuffed into the API payload?
2.3 The Request/Response Lifecycle
Click any API request and you'll see five tabs. Here's what to look for in each:
Headers: Examine every custom header. X-API-Key, Authorization: Bearer, X-CSRF-Token, X-Requested-With, custom rate-limit or version headers. These are authentication clues and potential bypass vectors.
Payload (or Request): The body of POST/PUT/PATCH requests. This is your parameter map. Every key-value pair is a potential injection point.
Preview: A parsed, readable view of the response. Great for quickly understanding the data structure.
Response: The raw response body. Always check this — sometimes Preview hides encoding errors or extra data.
Initiator: As discussed above — jump directly to the source code.
3. Hidden Endpoint Discovery
3.1 JavaScript Source Map Analysis
Modern web apps bundle and minify JavaScript. But many leave .map files exposed in production. These source maps contain the original, readable source code — including every API endpoint the developers hardcoded.
Automate the discovery:
# Find all JS source maps
gau target.com | grep '\.js\.map$' | sort -u | tee js_maps.txt
# Download them
cat js_maps.txt | xargs -I {} wget -q {}
# Extract API endpoints from all maps
cat *.map | grep -oP '["'\'']/api/[a-zA-Z0-9/_-]+["'\'']' | sort -u# Find all JS source maps
gau target.com | grep '\.js\.map$' | sort -u | tee js_maps.txt
# Download them
cat js_maps.txt | xargs -I {} wget -q {}
# Extract API endpoints from all maps
cat *.map | grep -oP '["'\'']/api/[a-zA-Z0-9/_-]+["'\'']' | sort -uBut even without .map files, you can extract endpoints directly from minified JS:
# Use LinkFinder
python3 linkfinder.py -i https://target.com/app.js -o cli
# Or grep for common patterns
curl -s https://target.com/static/js/main.abc123.js | \
grep -oP '["'\'']/[a-zA-Z0-9_/]{3,}["'\'']' | \
sort -u | grep -E '(api|v1|v2|graphql|rest|admin)'# Use LinkFinder
python3 linkfinder.py -i https://target.com/app.js -o cli
# Or grep for common patterns
curl -s https://target.com/static/js/main.abc123.js | \
grep -oP '["'\'']/[a-zA-Z0-9_/]{3,}["'\'']' | \
sort -u | grep -E '(api|v1|v2|graphql|rest|admin)'3.2 Heap Snapshots: The Nuclear Option
When you can't find endpoints in JS files or network traffic, take a heap snapshot. Some applications construct API URLs dynamically at runtime — these strings exist only in memory.
Here's the technique:
- Open DevTools → Memory tab
- Select Heap snapshot and click Take snapshot
- Interact with the application (navigate, trigger features)
- Take a second snapshot
- Switch to Comparison view (Snapshot 2 vs Snapshot 1)
In the filter box, start searching for strings like /api, fetch(, axios, graphql, or endpoint. Strings allocated between the two snapshots will surface dynamically constructed API paths that never appeared in static files.
You can also search across all snapshots for patterns:
# In the Memory tab, look for string values containing:
/api/
/graphql
/v2/
/internal# In the Memory tab, look for string values containing:
/api/
/graphql
/v2/
/internal3.3 Global Search Across All Sources
Press Ctrl+Shift+F (or Cmd+Option+F on Mac) to open the Search tab across all loaded resources.
Search for:
fetch(andaxios.and$.ajax— API call patternsbaseURL,baseUrl,apiUrl,apiEndpoint— configuration objects/api/,/v1/,/v2/,/graphql— endpoint prefixesAuthorization,Bearer,apiKey,x-api-key— auth header constructionendpoint,route,path:— route definitions
This one search reaches into HTML, CSS, JS, source maps, and even web workers.
4. Advanced Interception and Manipulation
4.1 Local Overrides: Modify Responses in Real-Time
Local overrides let you modify API responses as if you controlled the server. This is incredibly powerful for testing client-side authorization logic.
Setup:
- Go to Sources → Overrides
- Click Select folder for overrides and pick a local directory
- Grant permission when prompted
- Back in the Network tab, right-click any XHR/Fetch request
- Select Override content
Now you can edit the JSON response directly. Save the file, reload, and the page renders your modified data.
Bug bounty use cases:
- Change
"role": "user"to"role": "admin"in a user profile API response — does the frontend expose admin features? - Modify
"is_premium": falseto"is_premium": true— can you access premium content? - Alter
"allowed_actions": ["view"]to"allowed_actions": ["view","edit","delete"] - Change
"account_verified": falseto"account_verified": true— does this unlock privileged operations?
You can also override response headers. Right-click a request → Override headers. Modify Access-Control-Allow-Origin, Content-Security-Policy, or even Set-Cookie values. This is useful for testing CORS misconfigurations and cookie-tampering scenarios.
4.2 Request Blocking: Understand the App's Dependencies
Press Ctrl+Shift+P → type "block" → Show Request Blocking. Add URL patterns to block. This tells you what breaks when certain APIs or resources are unavailable.
Is the app still functional when the analytics endpoint fails? Does an error-handling bypass kick in? Sometimes blocking a slow API call reveals fallback endpoints that are less secure.
4.3 Network Conditions: Simulate Restricted Environments
DevTools → Network → Network conditions (or find it via the Command Menu). Change the User-Agent to a mobile device — some applications serve entirely different API responses to mobile clients, often with less authentication rigor.
You can also throttle the connection to simulate slow networks. This can expose race conditions where the client-side protection hasn't loaded yet but the API is already accepting requests.
5. Console APIs That Hackers Should Know
5.1 debug(functionName)
This is the most powerful console trick for API reverse engineering. When you find a function in the Sources tab that constructs API calls, type this in the console:
debug(functionName)debug(functionName)Now every time that function is called, execution pauses at its first line. You can inspect the arguments, step through the execution, and see exactly how the API request is built — including parameters injected from other parts of the application.
// Common targets to debug
debug(fetch)
debug(axios.post)
debug($.ajax)
debug(XMLHttpRequest.prototype.send)// Common targets to debug
debug(fetch)
debug(axios.post)
debug($.ajax)
debug(XMLHttpRequest.prototype.send)5.2 monitor(functionName)
If you don't want to break execution but just want to log every call:
monitor(fetch)monitor(fetch)Now every fetch() call logs to the console with its arguments. You'll see every URL, every options object — including ones that happen on timers, scroll events, or other triggers you might miss in the Network tab.
5.3 monitorEvents(element)
Track all events on a specific DOM element:
// Monitor all click/change/submit events on the entire document
monitorEvents(document.body)
// Monitor specific event types
monitorEvents(document.querySelector('form'), 'submit')
monitorEvents(window, 'message') // postMessage interception// Monitor all click/change/submit events on the entire document
monitorEvents(document.body)
// Monitor specific event types
monitorEvents(document.querySelector('form'), 'submit')
monitorEvents(window, 'message') // postMessage interceptionThis is particularly useful for finding cross-window messages (postMessage) that shuttle API tokens or sensitive data between origins.
5.4 getEventListeners(element)
Return all event listeners attached to an element:
getEventListeners(document.querySelector('button'))getEventListeners(document.querySelector('button'))This reveals handler functions you can then debug() to trace API calls.
6. Exporting and Processing Captured Traffic
6.1 HAR File Export
Once you've performed a series of actions, export everything:
- Right-click any request in the Network tab → Save all as HAR with content
- This single
.harfile contains every request, response, header, cookie, and timing detail.
6.2 HAR → OpenAPI with mitmproxy2swagger
This is the single most useful automation step:
# Install
pip install mitmproxy2swagger
# Feed it your HAR file
mitmproxy2swagger -i captured_traffic.har -o api_spec.yml -f har
# The output is an OpenAPI 3.0 spec with all discovered endpoints,
# methods, parameters, headers, and response schemas# Install
pip install mitmproxy2swagger
# Feed it your HAR file
mitmproxy2swagger -i captured_traffic.har -o api_spec.yml -f har
# The output is an OpenAPI 3.0 spec with all discovered endpoints,
# methods, parameters, headers, and response schemasNow you have a machine-readable API specification. Import it into Postman, Burp Suite, or your fuzzer of choice. Every endpoint is documented with the exact parameters the frontend sends.
6.3 curl Export for Immediate Testing
In the Network tab, right-click any request → Copy → Copy as cURL. This gives you a ready-to-paste curl command with all headers and the request body intact.
# Modify a single parameter and resend
curl 'https://api.target.com/v2/users/profile' \
-H 'Authorization: Bearer eyJ...' \
-H 'Content-Type: application/json' \
-d '{"user_id":123,"role":"admin"}'# Modify a single parameter and resend
curl 'https://api.target.com/v2/users/profile' \
-H 'Authorization: Bearer eyJ...' \
-H 'Content-Type: application/json' \
-d '{"user_id":123,"role":"admin"}'6.4 Node.js fetch Export
For more complex workflows, copy as fetch and paste into a Node.js script:
// Copy as fetch gives you a complete fetch() call
fetch("https://api.target.com/v2/users/profile", {
"headers": {
"authorization": "Bearer eyJ...",
"content-type": "application/json"
},
"body": JSON.stringify({user_id: 123, role: "admin"}),
"method": "POST"
});// Copy as fetch gives you a complete fetch() call
fetch("https://api.target.com/v2/users/profile", {
"headers": {
"authorization": "Bearer eyJ...",
"content-type": "application/json"
},
"body": JSON.stringify({user_id: 123, role: "admin"}),
"method": "POST"
});7. Replaying and Fuzzing from the Browser
7.1 Using the Console as an API Client
DevTools' console has full access to the page's fetch function, cookies, and storage. You don't need to export anything — just craft requests right there:
// Test an endpoint directly
fetch('/api/v2/users/123/profile', {
headers: {
'Authorization': localStorage.getItem('auth_token'),
'Content-Type': 'application/json'
}
}).then(r => r.json()).then(console.log)// Test an endpoint directly
fetch('/api/v2/users/123/profile', {
headers: {
'Authorization': localStorage.getItem('auth_token'),
'Content-Type': 'application/json'
}
}).then(r => r.json()).then(console.log)The session cookie is automatically included. This is often faster than exporting to an external tool.
7.2 Parameter Tampering in the Payload Tab
In the DevTools Network tab, find a POST request. Click the Payload tab. You'll see form data or JSON parameters. Right-click any parameter and you can:
- Edit and resend (in newer Chrome versions)
- Copy the full payload
- Copy as fetch/curl for external fuzzing
7.3 Replay XHR with Console Snippets
Save common replay patterns as DevTools Snippets:
// Save this as a Snippet in Sources → Snippets
(async () => {
const baseUrl = 'https://api.target.com/v2';
const token = localStorage.getItem('access_token');
const endpoints = [
`${baseUrl}/users/me`,
`${baseUrl}/users/me/roles`,
`${baseUrl}/admin/users`,
];
for (const url of endpoints) {
const res = await fetch(url, {
headers: { 'Authorization': `Bearer ${token}` }
});
console.log(url, res.status, await res.json());
}
})();// Save this as a Snippet in Sources → Snippets
(async () => {
const baseUrl = 'https://api.target.com/v2';
const token = localStorage.getItem('access_token');
const endpoints = [
`${baseUrl}/users/me`,
`${baseUrl}/users/me/roles`,
`${baseUrl}/admin/users`,
];
for (const url of endpoints) {
const res = await fetch(url, {
headers: { 'Authorization': `Bearer ${token}` }
});
console.log(url, res.status, await res.json());
}
})();8. Real-World Bug Bounty Patterns
8.1 GraphQL Introspection via DevTools
Many SPAs embed GraphQL endpoints. From the Network tab, filter by graphql or gql. Look at the request body — if you see structured queries like:
{"query":"query { user(id: 123) { email role } }"}{"query":"query { user(id: 123) { email role } }"}Copy the endpoint URL and run an introspection query:
curl -X POST https://target.com/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"query { __schema { types { name fields { name } } } }"}'curl -X POST https://target.com/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"query { __schema { types { name fields { name } } } }"}'If introspection is enabled, you've just mapped their entire data model.
8.2 IDOR via Parameter Discovery
While reverse engineering a user profile API:
// Original request observed in DevTools
fetch('/api/v2/profile', {
method: 'POST',
body: JSON.stringify({ user_id: 123, action: 'update_email' })
})// Original request observed in DevTools
fetch('/api/v2/profile', {
method: 'POST',
body: JSON.stringify({ user_id: 123, action: 'update_email' })
})But the Network tab showed a response structure containing { "admin": false }. Try:
fetch('/api/v2/profile', {
method: 'POST',
body: JSON.stringify({ user_id: 456, action: 'update_email' })
})fetch('/api/v2/profile', {
method: 'POST',
body: JSON.stringify({ user_id: 456, action: 'update_email' })
})If the API accepts user_id from the request body instead of the session, you've found an IDOR — one of the most common and lucrative bug bounty vulnerabilities.
8.3 Mass Assignment via Response Inspection
An API returns this structure:
{
"user": {
"id": 123,
"name": "John",
"email": "john@example.com",
"role": "user",
"is_premium": false,
"credit_balance": 0
}
}{
"user": {
"id": 123,
"name": "John",
"email": "john@example.com",
"role": "user",
"is_premium": false,
"credit_balance": 0
}
}Try adding these fields to a PUT/PATCH request:
{
"role": "admin",
"is_premium": true,
"credit_balance": 999999
}{
"role": "admin",
"is_premium": true,
"credit_balance": 999999
}Mass assignment vulnerabilities often reveal themselves through the response structure.
8.4 WebSocket Manipulation
Filter by WebSocket in the Network tab. Right-click a WebSocket message → Copy message. Open the console and reconnect:
// Create a WebSocket connection
const ws = new WebSocket('wss://api.target.com/ws');
ws.onmessage = (event) => {
console.log('Received:', JSON.parse(event.data));
};
ws.onopen = () => {
// Send a crafted message
ws.send(JSON.stringify({
action: 'join_room',
room_id: 'admin_chat_001'
}));
};// Create a WebSocket connection
const ws = new WebSocket('wss://api.target.com/ws');
ws.onmessage = (event) => {
console.log('Received:', JSON.parse(event.data));
};
ws.onopen = () => {
// Send a crafted message
ws.send(JSON.stringify({
action: 'join_room',
room_id: 'admin_chat_001'
}));
};WebSocket endpoints often have weaker authentication than HTTP endpoints. A room enumeration or message injection vulnerability here can be critical.
9. The Complete Workflow
Here's the battle-tested flow I use on every target:
1. Open DevTools → Network tab → Preserve Log ✓, Disable Cache ✓
2. Filter: XHR/Fetch
3. Browse the application thoroughly — every page, every feature
4. Watch the Initiator column — trace back to JS functions
5. Right-click interesting requests → Copy as cURL / fetch
6. Export HAR → mitmproxy2swagger → OpenAPI spec
7. Search across all sources for hidden endpoints
8. Take heap snapshots on key interactions
9. Use local overrides to test client-side auth bypasses
10. Fuzz discovered parameters in external tools (ffuf, Burp, custom scripts)
11. Log everything — `monitor(fetch)` in the console
12. Test GraphQL introspection and WebSocket endpoints1. Open DevTools → Network tab → Preserve Log ✓, Disable Cache ✓
2. Filter: XHR/Fetch
3. Browse the application thoroughly — every page, every feature
4. Watch the Initiator column — trace back to JS functions
5. Right-click interesting requests → Copy as cURL / fetch
6. Export HAR → mitmproxy2swagger → OpenAPI spec
7. Search across all sources for hidden endpoints
8. Take heap snapshots on key interactions
9. Use local overrides to test client-side auth bypasses
10. Fuzz discovered parameters in external tools (ffuf, Burp, custom scripts)
11. Log everything — `monitor(fetch)` in the console
12. Test GraphQL introspection and WebSocket endpoints10. Tooling Ecosystem
11. Advanced: DevTools MCP (2026)
As of mid-2026, Chrome's DevTools MCP (Model Context Protocol) is in public beta. This allows AI agents to programmatically hook into DevTools and observe the same network traffic, console logs, and source content that a human would see.
For reverse engineering, this means you can script an agent to:
- Navigate through an entire application
- Record all API interactions
- Deduplicate and classify endpoints
- Test parameter variations automatically
The workflow becomes: browse normally → export HAR → feed to agent → receive structured API documentation with tested attack surfaces. This is still emerging, but it's the direction the industry is heading.
Conclusion
Chrome DevTools is not just for debugging layout bugs. It is the single most accessible, powerful reverse engineering tool available to every bug bounty hunter and pentester. The same Network tab your frontend colleagues use to fix a broken API call is the same Network tab you'll use to find an IDOR worth thousands of dollars.
Master the basics: filtering, initiator tracing, HAR export. Then graduate to the advanced techniques: heap snapshots for hidden endpoints, local overrides for client-side auth bypass, and console API interception for real-time monitoring. Chain these techniques together and you'll see APIs the way the application sees them — and find vulnerabilities the developers never anticipated.
The frontend has no secrets from those who know how to look. Open DevTools. Start watching the network. The bugs are there.
GitHub: SecurityTalent | Medium: Security Talent | Twitter: Securi3yTalent | Facebook: Securi3ytalent | Telegram: Securi3yTalent
#BugBounty #CyberSecurity #WebSecurity #APISecurity #ChromeDevTools #ReverseEngineering #OWASP #GraphQL #RESTAPI #PenetrationTesting #EthicalHacking #SecurityResearch #AppSec #InfoSec #SecurityTalent