Trace Website API Calls with REA: Browser, HAR and Bundles
On this page
You click a button on a web page and something loads. Which request was it, what came back, and which line of JavaScript sent it? DevTools can answer that for one session, but the answer disappears when you close the tab, and an AI agent cannot see your DevTools panel.
REA, the open-source project by morluto, gives your agent a set of browser and capture tools that answer these questions as Evidence. It attaches to a browser you started, records what a page does during a time window you choose, reads saved HAR and Fiddler SAZ files offline, and maps captured scripts and source maps back to original code.
Who this is for: front-end and full-stack developers, QA engineers and security learners who want to document how their own site, or a public demo site, talks to its backend.
What you will learn:
- Attach REA to a dedicated Chrome profile and pick a page
- Take a screenshot and record one user action
- Read the request, its initiator and the response shape
- Export the captured scripts and find the code that calls the API
- Inspect HAR and Fiddler SAZ captures offline, including an API-surface view
- Map a minified position back to source with a source map
Use only sites you are allowed to analyze. The examples use REA’s public Notes demo at https://rea.tools/examples/notes-web/ and placeholders for your own site. Before observing any other website, read its terms of service and get permission where needed. Do not use these tools to get around authentication, bot protection or rate limits. See REA’s DISCLAIMER.md.
Prerequisites
- REA installed.
npx rea-agents setupregisters the MCP server with your agent;npm install --global rea-agentsadds thereacommand. Every command below also works asnpx -y rea-agents@latest <command>. See Install REA and connect it to Claude Code, Cursor or Codex. - A Chrome-family browser. REA attaches through the Chrome DevTools Protocol (CDP). It does not launch, drive or close the browser for passive inspection.
- No native analysis engine is needed. Browser capture and JavaScript analysis do not use Ghidra, Hopper or IDA.
- For HAR or SAZ: a HAR 1.2 export from your browser or proxy, or a
.sazfile saved by Fiddler Everywhere.
Step 1: Start a dedicated browser with remote debugging
# Linux
google-chrome \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/rea-notes-browser \
https://rea.tools/examples/notes-web/
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/rea-notes-browser \
https://rea.tools/examples/notes-web/
Why a separate profile: CDP gives deep access to the whole profile, including cookies and storage. A throwaway --user-data-dir keeps your everyday logins out of the investigation. Never expose the debugging port on a non-loopback interface, and close the browser yourself when you finish.
On Linux, a locked desktop keyring can stall Chrome before the first navigation. For a dedicated profile, adding --password-store=basic avoids that.
Replace the URL with a page on your own site (for example, a local dev server at http://127.0.0.1:3000) when you move past the demo.
Step 2: List page targets
rea list-browser-targets http://127.0.0.1:9222 --json
Why: target IDs are specific to this browser instance, so every capture starts here. Pick the target whose URL matches your page and copy its ID. Use 127.0.0.1, not localhost. REA accepts only a literal loopback CDP endpoint.
When you work through an agent, give it the endpoint and the page URL. It calls list_browser_targets with {"cdp_endpoint": "http://127.0.0.1:9222"} and chooses the target itself.
Step 3: Take a screenshot
rea capture-web-screenshot http://127.0.0.1:9222 TARGET_ID --json > before.json
The result is a content-addressed PNG of the visible viewport, returned inline. Screenshots are useful as a “before” and “after” record around an action, and as a sanity check that the agent attached to the page you meant. To compare two screenshots locally, without OCR or external services:
# The comparison takes the screenshot artifact, not the whole Evidence record.
jq '.normalized_result.artifact' before.json > before-artifact.json
jq '.normalized_result.artifact' after.json > after-artifact.json
rea compare-web-screenshots before-artifact.json after-artifact.json --json
Pass file paths rather than inline JSON, because the embedded PNG bytes can exceed the shell’s argument limit.
Step 4: Record one action
Start an observation window, then click Export notes in the browser while it runs:
rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
--observation-ms 10000 \
--include-accessibility-text \
--include-json-body-shapes --json > export-click.json
Why each option:
--observation-ms 10000gives you ten seconds to act. The default window is 500 ms. Network and console coverage starts only after REA attaches, so requests made before the window are not in the capture. Their absence proves nothing.--include-accessibility-textkeeps button names and labels, which helps you and the agent connect the request to the control you clicked.--include-json-body-shapesrecords the structure of JSON responses (property paths, types and counts) without their values.
REA’s browser guide records this exact action on the Notes demo. With REA 4.1.0 it observed:
| Captured fact | Value |
|---|---|
| Request | GET /examples/notes-web/notes.json |
| Response | 200 · application/json |
| Request initiator | export.js · script |
| Response structure | notes[] with id and title fields |
Network observations keep the method, status, MIME type, size, initiator stack location and the complete URL. Cookies and authorization headers are never retained, and the DOM snapshot keeps node names and attribute names but not text or attribute values. Read data minimization before you capture anything that touches real accounts.
To record navigations, redirects and SPA route changes instead of resources, use rea observe-web-session http://127.0.0.1:9222 TARGET_ID --observation-ms 10000 --json.
Step 5: Find the code that sent the request
The initiator tells you which script started the request. Now read that script.
5.1 Capture and export the scripts
rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
--include-script-sources --json > scripts-capture.json
rea export-web-scripts /absolute/path/scripts-capture.json \
/absolute/path/exported-scripts --json
rea analyze-javascript-application /absolute/path/exported-scripts/files --json > app-evidence.json
Why: export-web-scripts writes the captured sources and a verified manifest.json into a new directory. The destination must not already exist. The local JavaScript analyzer then inventories modules and imports without running any code. Export never fetches URLs, so what you analyze is exactly what the browser had.
For the Notes demo, the captured export.js contains the call that the network capture saw:
const response = await fetch("./notes.json");
const { notes } = await response.json();
The network Evidence shows that the click requested notes.json, and the script source shows how the response becomes a CSV. Keep those two facts separate. Neither one alone proves the other.
5.2 Get exact call sites for a larger app
On a real app the initiator may point into a large bundle. observe-web-execution records request initiators with derived call sites and script IDs, plus V8 precise coverage for the same window:
rea observe-web-execution http://127.0.0.1:9222 TARGET_ID \
--observation-ms 10000 --json > execution.json
Wait for the armed progress event on stderr before you click. Note that precise coverage changes timing and JIT behavior while it runs. To check listeners on the button itself:
rea inspect-web-event-listeners http://127.0.0.1:9222 TARGET_ID '#export' --json
This returns directly registered listeners and their callback locations. Delegated listeners on ancestors and framework-internal registries are not enumerated, so an empty result on a React or Vue button is normal.
5.3 Static bundle analysis
rea analyze-web-bundle http://127.0.0.1:9222 TARGET_ID --json
analyze-web-bundle parses the page’s scripts without executing them and returns chunk edges, route and endpoint candidates, and vendor fingerprints. Endpoint candidates are useful for finding API calls that your recorded action did not trigger. They are candidates, not observations: a string that looks like /api/v1/notes is not proof that anything calls it.
On your own site, you can also let it fetch source maps from the same origin by adding --fetch-source-maps.
5.4 Map a minified position to source
If you have the source map for your own build, map a generated position to the original file:
rea trace-web-source-location \
/absolute/path/exported-scripts/manifest.json 0 \
/absolute/path/app.js.map 'https://your-site.example/assets/app.js.map' \
1 25 --json
The six positional arguments are the manifest, the zero-based script index, the local map path, the map’s URL context, a one-based generated line and a zero-based UTF-16 column. The URL only gives the context for resolving sources; nothing is fetched. CDP initiator positions are zero-based lines, so add one to the line number when you copy it from an external script. The web runtime guide covers the inline-script conversion.
Step 6: Read HAR captures offline
Live capture is not always possible: a teammate may send you a HAR, or a proxy may have recorded the session. REA reads HAR 1.2 offline. It never fetches recorded URLs and starts no browser or proxy.
To export one from Chrome, open DevTools, record the action in the Network panel, and use its HAR export. Prefer a sanitized export when your browser offers one, because HAR files can contain cookies and tokens.
rea inspect-web-network-capture /absolute/path/session.har har --json > har-evidence.json
rea inspect-web-network-capture /absolute/path/session.har har --record 2 --record 0 --json
--record selects zero-based entries in the order you give. REA validates every entry before selecting, so a malformed entry elsewhere in the file is still reported. Header and cookie authentication values are excluded structurally. To also exclude a specific secret, such as a test API key that appears in a body, repeat --sensitive-value:
rea inspect-web-network-capture /absolute/path/session.har har \
--sensitive-value 'test-key-123' --json
Group requests into an API surface
For a quick list of the endpoints a HAR contains, project the saved Evidence through the api-surface view:
jq -c '{source: {kind: "inline", evidence: .}, view: {kind: "api-surface", offset: 0, limit: 50}}' \
har-evidence.json > har-api-view.json
rea inspect-analysis-view har-api-view.json
The view groups requests by method, origin and path template. Numeric segments, UUIDs, long hex strings and ISO dates become {numeric}, {uuid}, {hex} and {iso-date}. So /notes/41 and /notes/42 show up as one group, GET /notes/{numeric}, with both observed values listed. Query strings, headers and bodies are not copied into the groups. The view does not claim which handler serves a route. It also refuses parents inspected with --sensitive-value, because it cannot check its own output against declarations that were not retained.
Over MCP, an agent can call inspect_web_network_capture with detail: "summary" and then page through records with inspect_analysis_view, which keeps large captures out of a single response.
Step 7: Read Fiddler Everywhere SAZ files
Fiddler Everywhere saves sessions as Session Archive Zip files. REA reads them directly. You do not need Fiddler installed or a conversion to HAR:
rea inspect-web-network-capture /absolute/path/session.saz saz --json
rea inspect-web-network-capture /absolute/path/session.saz saz --record 0 --json
REA pairs each session’s request, response and metadata members inside the archive. Ordinals follow ZIP directory order, not Fiddler’s session numbers. Bodies are kept as stored, without dechunking or decompression. The API-surface view supports HAR only, so for SAZ either page through records or export HAR from Fiddler (Export > HTTPArchive, then confirm log.version is "1.2") and keep the original SAZ.
Fiddler Everywhere also has its own MCP server for live traffic. REA does not wrap it; connect it separately if you want both. See Fiddler Everywhere captures and MCP.
Step 8: Let the agent put it together
Once you know the pieces, one prompt covers the workflow:
Use REA to inspect the page at http://127.0.0.1:9222 while I click Export notes.
Tell me which request it makes, which script initiates it, and show the code
that builds the CSV. Cite the Evidence ID for each fact.
Check the answer the same way as any other REA result: the request should come from inspect_web_page network Evidence, the initiator from the same record, and the code from captured or exported script sources. If the agent names an endpoint that only appears in analyze_web_bundle candidates, ask whether it was observed or only found in code.
Troubleshooting
list-browser-targets fails or returns nothing. Confirm Chrome started with --remote-debugging-port=9222, that no other Chrome instance already owns the profile, and that you used http://127.0.0.1:9222. localhost, HTTPS endpoints and paths are rejected.
My request is missing. It happened before the window opened, or it went to an origin outside the default scope. A capture defaults to the page’s current origin; requests to a separate API host are filtered unless you add it to allowed_origins (--allowed-origins on the CLI).
target_changed error. The page navigated during final capture, and REA discarded the mixed result. Start the window again after the navigation settles.
export-web-scripts reports no exportable sources. The capture did not include script content. Capture again with --include-script-sources.
HAR inspection fails validation. Only HAR 1.2 is supported, and duplicate JSON keys are rejected. Check log.version, and export again from the producer if the file was edited by hand.
Large captures hit a resource limit. HAR and SAZ inspection has a 32 MiB input budget. Export a smaller capture that covers only the action you care about, and keep the full file for provenance.
Next steps
- Analyze a desktop app built on the same web stack: Reverse engineer an Electron app with REA.
- Keep and compare captures across releases: Evidence, snapshots and version comparison.
- Apply the same Evidence discipline to a binary: Solve CTF reversing challenges with REA and an AI agent.
- Read the full browser observation and network capture references.