ScreenshotNeo

BlogHow-to

How to Discover and Test APIs Used by a Website

Use browser developer tools to find the API requests behind a website, then replay and test them safely with cURL, Python, or Node.js.

By the ScreenshotNeo team4 October 20269 min read

To discover the APIs a website uses, open your browser’s Developer Tools, select the Network panel, start recording, reload the page, and repeat the interaction you want to understand. Inspect the resulting request’s method, URL, parameters, headers, body, response, status, initiator, and timing. Then replay a request you are authorized to test with an API client or a short script, and compare the result with the API’s documentation and access rules.

Browser traffic is evidence of what happened during one page journey and session, not a complete API specification. Test only websites and accounts you own or are explicitly authorized to assess. Requests can contain session cookies, bearer tokens, personal data, and other secrets; redact them before sharing exports.

1. Record the website’s network requests

  1. Open the website in Chrome and open Developer Tools (right-click the page and choose Inspect, or use the browser’s Developer Tools shortcut).
  2. Select the Network panel. Keep Developer Tools open while recording; requests made before it was open will not appear in the panel.
  3. Reload the page to capture initial requests. Then reproduce the specific action you care about: search, form submission, pagination, opening a detail view, or another interaction.
  4. Filter the request list to narrow it down. Inspect likely data requests, but remember that the list also contains images, scripts, stylesheets, analytics, and other resources.
  5. Select a request and examine its method, full URL, query parameters, request headers, payload, status, response, initiator, and timing. Chrome’s Network panel separates these details into views such as Headers, Preview, Response, Initiator, and Timing. See the official Chrome guide to inspecting network activity and the Network features reference.

A request that returns JSON is often an API call, but its format alone is not proof. Use the action that triggered it, its initiator, and the response content to understand its role. An endpoint name by itself tells you little about required headers, input shape, session state, or permissions.

2. Understand the request before replaying it

Write down the observed exchange before changing anything. Separate values that define the request from values that are incidental to your current browser session.

Inspect What to record Why it matters
Method and URL HTTP method, host, path, query string Identifies the operation and its inputs. Query values may contain personal or secret data.
Headers Content-Type, Accept, Authorization, cookies, and relevant custom headers Shows how the browser identifies the session and formats the request. Do not copy credentials into shared examples.
Body JSON, form fields, or other payload, with sensitive values redacted Shows what the action submitted and helps reproduce it accurately.
Status and response Status code, response headers, and body shape Distinguishes a successful application response from an error or an unexpected redirect.
Initiator and timing Which script or action started the call; when it ran and how long it took Connects a request to the user action and can reveal dependent calls or slow responses.

Do not assume a browser request is intended as a stable public API. It may depend on an undocumented session, short-lived token, browser-specific header, or internal implementation detail. If the service publishes API documentation or an OpenAPI description, compare the observed traffic with it. Use both sources: documentation can omit or misstate behavior, while traffic only reveals paths reached by the actions and account you observed. OWASP describes this as API reconnaissance, not proof of the complete contract: API Reconnaissance.

3. Replay a captured request

For repeatable checks, take a request you are allowed to use and reproduce its method, URL, required headers, and body. Begin with a read-only request where possible. The examples below show a generic JSON GET request; replace the example host and path with the authorized endpoint you observed. Add only the parameters and headers that request needs.

cURL

curl --include \
  --request GET \
  --header 'Accept: application/json' \
  'https://api.example.test/v1/items?limit=10'

For a JSON POST request, use the observed method and a sanitized payload:

curl --include \
  --request POST \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{"query":"example"}' \
  'https://api.example.test/v1/search'

Python

Install the HTTP client with python -m pip install requests. This example checks the status and parses JSON, while allowing connection and response timeouts.

import requests

url = "https://api.example.test/v1/items"
params = {"limit": 10}
headers = {"Accept": "application/json"}

response = requests.get(url, params=params, headers=headers, timeout=(5, 30))
print("Status:", response.status_code)
response.raise_for_status()
print(response.json())

For a POST request, pass a Python dictionary as json so the library encodes JSON and sets the content type:

import requests

response = requests.post(
    "https://api.example.test/v1/search",
    json={"query": "example"},
    headers={"Accept": "application/json"},
    timeout=(5, 30),
)
print("Status:", response.status_code)
response.raise_for_status()
print(response.json())

Node.js

On a current Node.js release with global fetch, build query parameters with URLSearchParams so values are encoded correctly:

const url = new URL('https://api.example.test/v1/items');
url.search = new URLSearchParams({ limit: '10' });

const response = await fetch(url, {
  headers: { Accept: 'application/json' },
  signal: AbortSignal.timeout(30000),
});

console.log('Status:', response.status);
if (!response.ok) {
  throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}
console.log(await response.json());

For POST, send a JSON string and the matching content type:

const response = await fetch('https://api.example.test/v1/search', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ query: 'example' }),
  signal: AbortSignal.timeout(30000),
});

console.log('Status:', response.status);
if (!response.ok) {
  throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}
console.log(await response.json());

When a request depends on authentication, use a test account and supply credentials through a local secret store or environment variable. Do not paste live cookies or tokens into source code, issue trackers, chat, or a committed collection. If you export a HAR file or request collection, review and redact it before sharing.

4. Make the test repeatable

A captured request can be a useful seed for a test, but a one-time replay is not a test plan. Define expected behavior for valid input, invalid input, and the authorized identity that sends the request.

  1. State the expected result. Record the expected status, content type, and response fields for a valid request. Avoid relying on incidental fields such as generated timestamps unless they are part of the contract.
  2. Check input handling. With approved test data, try a missing required field and a malformed value. Confirm the service returns a documented or sensible client error and does not expose internal details.
  3. Check authentication and authorization. Where in scope, test without credentials, with a valid test identity, and with an identity lacking the required role or scope.
  4. Check object access only with approved test objects. A different object ID in a URL does not grant permission to access that object. Use accounts and objects provided for the assessment.
  5. Save the request and assertions. An API client can make replay easier and preserve checks for later runs. Postman’s Browser Tool can capture traffic while you interact with a site, open selected requests as HTTP requests, then edit, resend, test, and save them in collections. Its browser tool has its own cookies and browser session, so do not assume it inherits the signed-in session from your main browser. See Postman’s Browser Tool traffic guide.

A 200 response does not prove that access control is correct. Check whether the response is allowed for that identity and whether the function should be available to its role. OWASP’s guidance covers object-level authorization, function-level authorization, and a broader REST assessment approach. Keep checks within the approved scope; do not probe other users’ identifiers, perform destructive actions, or use production data unless explicitly authorized.

5. Interpret what you found

  • Observed request: evidence that a particular browser action, account, and session sent a particular exchange.
  • Published contract: evidence of intended operations, inputs, responses, and requirements, subject to omissions or inaccuracies.
  • Repeatable test: an explicit expected outcome, controlled inputs, and a known identity and environment.

Use all three where available. A request the browser never made may still exist in the documented API; an undocumented request may be an internal endpoint or a gap worth discussing with the service owner. Confirm intended behavior and access policy before treating a difference as a defect.

6. Troubleshooting

Symptom Likely cause What to do
The request is missing from Network Recording started after the request, or the page did not repeat the relevant action. Keep Developer Tools open, reload, and perform the action again. Check filters that may hide the request.
There are too many requests to identify the API call The page also loads assets, analytics, and background traffic. Reproduce one action at a time, filter the list, and use the Initiator and response views to connect calls to the action.
Replay returns 401 or 403 Credentials are missing, expired, or lack the required permission; the replay client may not share the browser session. Use an authorized test identity and the documented authentication method. Do not copy a real user’s session token into shared tooling.
Replay returns 400 or 415 A required parameter or body field is absent or malformed, or the content type does not match the payload. Compare the captured request’s query, body, and Content-Type with the documentation; send JSON as JSON, not as an untyped string.
Replay redirects to a login page or returns HTML The endpoint requires a session or the request reached a web route rather than an API route. Check the final URL, response content type, and authentication flow. Use the documented API endpoint if one exists.
Browser request works, script fails The browser supplied cookies, headers, CSRF data, or an origin context absent from the script. Identify which requirement is documented and reproduce only the necessary parts with an approved test account. Do not blindly copy every browser header.
The result differs between runs Data changed, the request depends on session state, or timing and background requests affect the outcome. Use controlled test data, repeat one action at a time, record request timing, and assert stable contract fields rather than incidental values.
Exported traffic contains secrets HAR files and saved requests can include cookies, tokens, and personal data. Restrict access to the export, redact sensitive fields, and rotate exposed credentials according to the service’s procedures.

7. Performance, reliability, and cost

For a manual investigation, capture only the journey you need and replay the smallest request that answers the question. This reduces noise and avoids unnecessary load on the service. Use reasonable timeouts in scripts, keep retries bounded, and retry only requests that are safe to repeat; a retry of a write operation can duplicate an action if the service does not support idempotency. Prefer a test or staging environment for repeated checks when one is available and authorized.

Network timing in Developer Tools helps locate slow requests, but one measurement is not a benchmark. Browser cache, session state, network conditions, and server load can change results. For reliable comparisons, hold the environment and inputs steady and gather repeated measurements under an agreed test plan. Follow the API owner’s rate limits and scope; no universal cost or request limit can be inferred from a captured browser call.

Or skip the browser setup

If you need a clean screenshot of the page alongside your API investigation, ScreenshotNeo is a website screenshot API and MCP server for developers. It is useful for recording the visible result of a page journey, but it does not discover or test the site’s API requests. Its API accepts a URL in one GET request and can return PNG, JPEG, WebP, or PDF; see the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie and consent banners are accepted before capture, and 60+ known consent platforms, newsletter popups, and chat widgets can be removed; each step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses include X-Page-Verdict and X-Billed headers.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
  • The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, no card required.

FAQ

Can I see every API a website has?

No. The Network panel shows requests made during the journeys and session you observed. Use documentation and an authorized test plan to broaden coverage.

Is a browser request a public API?

Not necessarily. A request can be an internal frontend endpoint with session-specific requirements. Check the service’s documentation and terms before building against it.

Does a successful status mean the endpoint is secure?

No. A successful response only describes that request. Authorization must be evaluated against the identity, object, and function that the account is permitted to use.