Page Analysis
The Page Analysis endpoint scans any webpage and returns a structured breakdown of its content: interactive elements, headings, forms, images, navigation links, and text blocks. Each element includes its bounding rectangle, so you can map every item to its exact position on the page.
Page Analysis is available as a Pro feature with unlimited requests. Free and Starter plans get 20 trial runs per month.
Endpoint
POST /v1/analyze
Send a JSON body with the target URL and options. The response is always JSON with two top-level keys: meta and result.
Request Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | required | The page to analyze. Must start with http:// or https://. |
| include | array | [] (all) | Which element categories to return. Accepts any combination of: headings, interactive, forms, images, text, navigation. An empty array returns all categories. |
| visible_only | boolean | true | When true, only elements visible in the viewport are returned. Set to false to include off-screen and hidden elements. |
| scope | string | null | A CSS selector to limit analysis to a specific section of the page. For example, main or #content. Only elements inside the matched container are returned. |
| with_screenshot | boolean | false | When true, a viewport screenshot is captured alongside the analysis. The URL is returned in meta.screenshot_url. This costs one extra API request (2 total). |
| width | integer | 1280 | Viewport width in pixels (320–3840). |
| height | integer | 800 | Viewport height in pixels (200–2160). |
| device | string | desktop | Device preset: desktop, mobile, or tablet. Sets viewport dimensions and User-Agent automatically. |
| dark_mode | boolean | false | Emulate prefers-color-scheme: dark. |
| block_ads | boolean | false | Block ad networks before the page loads. |
| block_cookies | boolean | true | Remove cookie consent banners. |
| block_chats | boolean | false | Block live-chat widgets (Intercom, Drift, etc.). |
| delay | integer | 0 | Wait this many seconds after page load before analyzing (0–10). |
| timeout | integer | 30 | Maximum time in seconds the browser waits for the page to load (5–60). |
| stealth | boolean | false | Enable stealth mode to bypass bot detection. |
| css | string | null | Custom CSS injected before analysis (max 10 000 chars). |
| js | string | null | Custom JavaScript injected before analysis (max 10 000 chars). |
| hide_selectors | array | null | CSS selectors for elements to hide before analysis. Array of strings. |
| wait_for_selector | string | null | Wait for this CSS selector to appear before starting analysis. |
| user_agent | string | null | Custom User-Agent string. |
| proxy | string | null | HTTP/HTTPS proxy URL for the request. |
| timezone | string | null | IANA timezone (e.g. America/New_York). |
| geolocation | object | null | Emulate geolocation. Object with latitude (-90 to 90), longitude (-180 to 180), and optional accuracy. |
| headers | object | null | Custom HTTP headers sent with the request. Key-value pairs. |
| cookies | array | null | Cookies to set before loading the page. Array of cookie objects. |
Basic Example
curl -X POST https://api.screenshotrun.com/v1/analyze \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"include": ["headings", "interactive", "images"]
}'
$response = Http::withToken(env('SCREENSHOT_API_KEY'))
->timeout(60)
->post('https://api.screenshotrun.com/v1/analyze', [
'url' => 'https://example.com',
'include' => ['headings', 'interactive', 'images'],
]);
$data = $response->json();
$headings = $data['result']['headings'];
$images = $data['result']['images'];
const response = await fetch("https://api.screenshotrun.com/v1/analyze", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SCREENSHOTRUN_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com",
include: ["headings", "interactive", "images"],
}),
});
const { meta, result } = await response.json();
console.log(`Found ${result.headings.length} headings`);
import requests
response = requests.post(
"https://api.screenshotrun.com/v1/analyze",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"url": "https://example.com",
"include": ["headings", "interactive", "images"],
},
timeout=60,
)
data = response.json()
headings = data["result"]["headings"]
images = data["result"]["images"]
Response Format
The response contains two top-level objects: meta with request metadata and result with the analysis data.
Meta Object
| Field | Type | Description |
|---|---|---|
| status | integer | HTTP status code of the analyzed page. |
| took | number | Processing time in seconds. |
| screenshot_url | string|null | URL of the captured screenshot (only if with_screenshot: true). |
| requests | integer | Number of API requests consumed (1 without screenshot, 2 with). |
| trial | boolean | true if the request was made on a trial (non-Pro) account. |
| left | integer | Remaining trial runs this month (only present for trial users). |
| tokens | integer | Estimated token count of the result (for LLM context budgeting). |
| bytes | integer | Size of the JSON result in bytes. |
Result Object
The result object contains the analyzed page data. Each category is an array of elements.
| Field | Type | Description |
|---|---|---|
| url | string | The final URL after any redirects. |
| page | object | Page metadata: title, description, language, viewport dimensions. |
| headings | array | All heading elements (h1–h6) with level, text, and rect. |
| interactive | array | Clickable and focusable elements: buttons, links, inputs. Includes tag, text, href, role, type, and rect. |
| forms | array | Form elements with action, method, fields count, and rect. |
| images | array | Images with src, alt text, natural width/height, and rect. |
| navigation | array | Navigation links extracted from <nav> elements. Each has text, href, and rect. |
| stats | object | Summary counts for each category: headings, interactive, forms, images, navigation, text. |
Bounding Rectangles
Every element includes a rect array with four values: [x, y, width, height] in pixels, measured from the top-left corner of the viewport. This lets you draw overlays, build visual maps, or calculate element positions relative to the page.
{
"tag": "a",
"text": "Sign Up",
"href": "/register",
"rect": [920, 24, 96, 40]
}
Full Response Example
{
"meta": {
"status": 200,
"took": 2.4,
"screenshot_url": null,
"requests": 1,
"trial": false,
"tokens": 3200,
"bytes": 12800
},
"result": {
"url": "https://example.com",
"page": {
"title": "Example Domain",
"description": "This domain is for use in illustrative examples.",
"language": "en",
"viewport": { "width": 1280, "height": 800 }
},
"headings": [
{ "level": 1, "text": "Example Domain", "rect": [200, 80, 880, 48] }
],
"interactive": [
{ "tag": "a", "text": "More information...", "href": "https://www.iana.org/domains/example", "rect": [200, 160, 180, 20] }
],
"forms": [],
"images": [],
"navigation": [],
"stats": {
"headings": 1,
"interactive": 1,
"forms": 0,
"images": 0,
"navigation": 0,
"text": 2
}
}
}
Filtering with include
By default, the API returns all element categories. If you only need specific data, pass the include parameter to reduce the response size and speed up processing.
{
"url": "https://example.com",
"include": ["headings", "images"]
}
The response will only contain headings and images arrays. Other categories will be empty arrays.
Available categories:
| Value | What it returns |
|---|---|
headings | h1–h6 elements with level, text, and position |
interactive | Buttons, links, inputs, and other clickable/focusable elements |
forms | Form elements with action URL, method, and field count |
images | Images with src, alt text, dimensions, and position |
text | Text blocks and paragraphs |
navigation | Links inside <nav> elements |
Scoping to a Section
The scope parameter limits analysis to elements inside a specific CSS selector. This is useful for analyzing just the main content area, a sidebar, or a specific component.
{
"url": "https://example.com",
"scope": "main",
"include": ["headings", "interactive"]
}
Only headings and interactive elements inside the <main> element will be returned. Header, footer, and sidebar content is excluded.
Analysis with Screenshot
Set with_screenshot: true to capture a viewport screenshot alongside the analysis. The screenshot URL is returned in meta.screenshot_url.
curl -X POST https://api.screenshotrun.com/v1/analyze \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"with_screenshot": true,
"width": 1440,
"height": 900
}'
Double request cost
When with_screenshot is enabled, the request consumes 2 API calls from your quota instead of 1. The meta.requests field in the response confirms how many requests were used.
Trial Limits
Page Analysis is a Pro feature. Non-Pro accounts (Free and Starter) get 20 trial analysis runs per calendar month. The counter resets on the 1st of each month.
| Plan | Analysis runs |
|---|---|
| Free | 20 / month (trial) |
| Starter | 20 / month (trial) |
| Pro | Unlimited |
| Growth | Unlimited |
| Business | Unlimited |
When the trial limit is reached, the API returns a 402 error:
{
"error": {
"code": "TRIAL_LIMIT_REACHED",
"message": "You have used all 20 trial analysis runs this month. Upgrade to Pro for unlimited access.",
"status": 402
}
}
Trial users can check remaining runs via the meta.left field in every successful response.
Error Responses
| Status | Code | Description |
|---|---|---|
402 | TRIAL_LIMIT_REACHED | Monthly trial analysis limit exhausted. Upgrade to Pro. |
422 | ANALYSIS_FAILED | The page could not be analyzed (timeout, invalid URL, etc.). |
422 | — | Validation error (missing url, invalid parameters). |
429 | USAGE_LIMIT_REACHED | Monthly API quota exhausted. Upgrade your plan. |
Use Cases
Page Analysis works well for a range of automation and monitoring tasks:
- SEO audits — check heading hierarchy, missing alt text, broken navigation structure
- Accessibility checks — find interactive elements without labels, images without alt attributes, forms missing field names
- Competitive analysis — map out a competitor's page structure, CTA placement, and navigation patterns
- Content monitoring — detect changes in page structure over time by comparing analysis snapshots
- AI agent input — feed structured page data to LLMs for reasoning about web content without sending full HTML
- Visual regression — combine
with_screenshotand element positions to verify layout changes
Playground
You can try Page Analysis without writing code in the Playground. Switch to the Analysis tab, enter a URL, select the categories you want, and run the scan. The Playground shows the results in an interactive table with a visual map of element positions.
MCP Integration
If you use the ScreenshotRun MCP Server, your AI agent can run page analysis by calling the analyze_page tool. The MCP server passes the request to the same POST /v1/analyze endpoint documented here.
Next Steps
- Screenshots — capture website screenshots via the core API
- Screenshot Options — full parameter reference for all capture settings
- MCP Server — connect Page Analysis to AI agents
- Error Codes — complete error code reference