API Reference
Complete reference for every PageBolt endpoint, parameter, and integration.
Quick Start
Get your first screenshot in under 5 minutes. Three steps:
Sign up free
Create an account at pagebolt.dev/signup. No credit card required — you get 100 requests/month on all 11 APIs.
Get your API key
Your API key is generated automatically when you verify your email. Find it in the Dashboard.
Make your first API call
Copy this cURL command, replace YOUR_API_KEY, and run it:
curl -X POST https://pagebolt.dev/api/v1/screenshot \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "format": "webp"}' \
-o screenshot.webp
What's next?
Screenshot API →
30+ parameters, device presets, dark mode, ad blocking, style themes
Video Recording →
Record demo videos with cursor effects, click highlighting, and voice narration
PDF Generation →
Convert URLs or HTML to production-grade PDFs
MCP Server →
Use PageBolt from Claude Desktop, Cursor, or Windsurf
Authentication
All API requests require an API key. Pass it via the x-api-key header. For security, query parameter authentication is not supported — API keys should never appear in URLs, logs, or browser history.
curl -H "x-api-key: pf_live_your_key_here" \
https://pagebolt.dev/api/v1/screenshot
Base URL
https://pagebolt.dev/api/v1
All endpoint paths in this documentation are relative to this base URL.
Error Responses
All errors return JSON with an error field.
| Status | Meaning |
|---|---|
| 400 | Bad request — check your parameters |
| 401 | Invalid or missing API key |
| 402 | Quota exceeded (subscription) or insufficient balance (PAYG) — upgrade or top up |
| 429 | Rate limit exceeded — slow down |
| 500 | Server error — try again or contact support |
{
"error": "Either \"url\", \"html\", or \"markdown\" is required"
}
Rate Limits
Rate limits are per-user, based on your plan. Check response headers:
| Header | Description |
|---|---|
| X-RateLimit-Limit | Requests per minute allowed |
| X-RateLimit-Remaining | Requests remaining this window |
| X-Usage-Current | Requests used this month |
| X-Usage-Limit | Monthly request limit |
/v1/screenshot
Capture a screenshot of a URL, raw HTML, or Markdown content. Returns the image as binary data (or base64 JSON with metadata).
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | — | URL to capture (required if no html or markdown) |
| html | string | — | Raw HTML to render |
| markdown | string | — | Markdown to render NEW |
Viewport
| Parameter | Type | Default | Description |
|---|---|---|---|
| width | integer | 1280 | Viewport width in pixels (max 3840) |
| height | integer | 720 | Viewport height in pixels (max 2160) |
| deviceScaleFactor | number | 1 | Device pixel ratio, use 2 for retina (max 3) |
| viewportDevice | string | — | Device preset, e.g. "iphone_14_pro". Overrides width/height/DPR. NEW |
| viewportMobile | boolean | false | Enable mobile meta viewport NEW |
| viewportHasTouch | boolean | false | Enable touch events NEW |
| viewportLandscape | boolean | false | Enable landscape mode NEW |
Output
| Parameter | Type | Default | Description |
|---|---|---|---|
| format | string | png | png, jpeg, or webp |
| quality | integer | 80 | JPEG/WebP quality 1–100 |
| fullPage | boolean | false | Capture the full scrollable page |
| selector | string | — | CSS selector for element capture |
| omitBackground | boolean | false | Transparent background (PNG/WebP only) NEW |
| clip | object | — | Crop region {x, y, width, height} in pixels NEW |
Full Page Controls NEW
| Parameter | Type | Default | Description |
|---|---|---|---|
| fullPageScroll | boolean | auto | Auto-scroll to trigger lazy-loaded images before capture |
| fullPageScrollDelay | integer | 400 | Milliseconds to wait between scroll steps |
| fullPageScrollBy | integer | viewport height | Pixels per scroll step |
| fullPageMaxHeight | integer | — | Maximum screenshot height cap in pixels |
Timing
| Parameter | Type | Default | Description |
|---|---|---|---|
| delay | integer | 0 | Wait ms before capture (max 10000) |
| waitUntil | string | networkidle2 | load, domcontentloaded, networkidle0, networkidle2 NEW |
| waitForSelector | string | — | Wait for a CSS selector to appear before capturing NEW |
| navigationTimeout | integer | 25000 | Navigation timeout in milliseconds NEW |
Emulation NEW
| Parameter | Type | Default | Description |
|---|---|---|---|
| darkMode | boolean | false | Emulate dark color scheme (prefers-color-scheme: dark) |
| reducedMotion | boolean | false | Reduce animations (prefers-reduced-motion) |
| mediaType | string | — | "screen" or "print" |
| timeZone | string | — | Timezone ID, e.g. "America/New_York" |
| geolocation | object | — | {latitude, longitude, accuracy?} |
Customization
| Parameter | Type | Default | Description |
|---|---|---|---|
| userAgent | string | — | Custom User-Agent string NEW |
| cookies | array | — | Array of "name=value" strings or {name, value, domain?} objects NEW |
| headers | object | — | Extra HTTP headers to send with the page request NEW |
| authorization | string | — | Authorization header value, e.g. "Bearer <token>" NEW |
| authState | object | — | Full browser auth state — injects cookies and localStorage before navigation. Use the Auth State Extractor to capture this from your browser. NEW |
| bypassCSP | boolean | false | Bypass Content-Security-Policy on the page NEW |
| hideSelectors | array | — | CSS selectors to hide before capture NEW |
| click | string | — | Click a CSS selector before capture NEW |
| injectCss | string | — | Custom CSS to inject into the page |
| injectJs | string | — | Custom JavaScript to execute before capture |
Blocking
| Parameter | Type | Default | Description |
|---|---|---|---|
| blockBanners | boolean | false | Hide cookie consent banners (GDPR popups, OneTrust, CookieBot, etc.) |
| blockAds | boolean | false | Block advertisements NEW |
| blockChats | boolean | false | Block live chat widgets (Intercom, Crisp, Drift, etc.) NEW |
| blockTrackers | boolean | false | Block analytics trackers (GA, Hotjar, Segment, etc.) NEW |
| blockRequests | array | — | URL patterns to block (substring match) NEW |
| blockResources | array | — | Resource types to block: document, stylesheet, image, media, font, script, xhr, fetch, etc. NEW |
Anti-Detection & Proxy NEW
| Parameter | Type | Default | Description |
|---|---|---|---|
| stealth | boolean | false | Mask browser fingerprints to bypass bot detection walls. Uses an ephemeral browser instance per request. |
| proxy | string | — | Route requests through your proxy. Format: "http://user:pass@host:port". Supports http, https, socks4, socks5. |
Both parameters are supported on Screenshot, PDF, Sequence, and Video endpoints.
Metadata NEW
| Parameter | Type | Default | Description |
|---|---|---|---|
| extractMetadata | boolean | false | Extract page metadata (title, description, OG tags, favicon, HTTP status, canonical, lang) |
Response Format
| Parameter | Type | Default | Description |
|---|---|---|---|
| response_type | string | binary | Set to "json" for base64-encoded response with metadata |
When response_type is "json" and extractMetadata is true, the response includes:
{
"data": "iVBORw0KGgo...",
"format": "png",
"content_type": "image/png",
"size_bytes": 284512,
"duration_ms": 1243,
"metadata": {
"title": "Page Title",
"description": "Meta description",
"ogTitle": "OG Title",
"ogDescription": "OG Description",
"ogImage": "https://example.com/og.png",
"ogType": "website",
"favicon": "/favicon.ico",
"httpStatusCode": 200,
"canonical": "https://example.com",
"lang": "en"
}
}
Content-Type header (image/png, image/jpeg, or image/webp). Status 200 on success.
Styling (Post-Processing)
Add presentation-ready styling to your screenshots — curated theme presets, gradient backgrounds, glassmorphism, neon glows, noise textures, and more. Pass a style object to apply post-capture beautification.
Theme Presets NEW
Use style.theme for a one-param curated style. Themes set frame, background, shadow, padding, and border radius automatically. You can override any individual property alongside the theme.
| Theme | Frame | Background | Tier |
|---|---|---|---|
| notion | macOS light | Solid warm white | Free |
| paper | None | Solid white | Free |
| vercel | None | Solid black | Free |
| glass | Minimal | Frosted glass | Free |
| ocean | macOS | Ocean gradient | Free |
| sunset | macOS | Sunset gradient | Free |
| linear | Minimal | Midnight gradient | Starter+ |
| arc | macOS | Aurora gradient | Starter+ |
| glassDark | None | Dark frosted glass | Starter+ |
| glassWarm | None | Warm frosted glass | Starter+ |
| spotlight | None | Radial spotlight | Starter+ |
| neonBlue | Minimal | Midnight + blue glow | Starter+ |
| neonPurple | Minimal | Midnight + purple glow | Starter+ |
| neonGreen | Minimal | Slate + green glow | Starter+ |
| lavender | macOS | Lavender gradient | Starter+ |
| ember | Minimal | Ember gradient | Starter+ |
| dots | Minimal | Dot grid pattern | Starter+ |
| grid | Minimal | Grid line pattern | Starter+ |
Manual Style Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| style.theme | string | — | Theme preset name (see table above). Sets all style properties at once. Individual overrides still apply. NEW |
| style.frame | string | none | macos, windows, minimal, or none. Adds a browser chrome bar above the screenshot. |
| style.frameTheme | string | dark | light, dark, or auto. Controls the frame bar color scheme. |
| style.background | string | none | Gradient preset: ocean, sunset, forest, midnight, aurora, lavender, peach, arctic, ember, slate, neon. Special: glass (frosted), spotlight, dots, grid, noise, solid, or none. |
| style.bgColor | string | #1e3a5f | Hex color for solid background, e.g. "#1e3a5f". |
| style.bgColors | array | — | Custom gradient: array of exactly 2 hex colors, e.g. ["#667eea","#764ba2"]. Overrides preset. |
| style.padding | integer | 40 | Padding around the screenshot in pixels (0–120). |
| style.borderRadius | integer | 12 | Corner radius in pixels (0–40). |
| style.shadow | string | md | none, xs, sm, md, lg, xl, or 2xl. Drop shadow intensity. |
{ "style": { "theme": "linear" } } for instant polished results. The glass background creates a frosted glassmorphism effect using a blurred version of the screenshot itself as the backdrop. The neon* themes add a colored glow border and shadow — great for dark-mode hero images.
Styled screenshot example
curl -X POST https://pagebolt.dev/api/v1/screenshot \
-H "x-api-key: pf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://my-saas.com",
"format": "png",
"style": {
"frame": "macos",
"background": "ocean",
"shadow": "lg",
"padding": 60,
"borderRadius": 16
}
}' \
-o styled-screenshot.png
Example 1: Basic screenshot
curl -X POST https://pagebolt.dev/api/v1/screenshot \
-H "x-api-key: pf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://my-saas.com",
"width": 1280,
"height": 720,
"format": "png",
"fullPage": false
}' \
-o screenshot.png
Example 2: Device preset + dark mode
curl -X POST https://pagebolt.dev/api/v1/screenshot \
-H "x-api-key: pf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://my-saas.com",
"viewportDevice": "iphone_15_pro_max",
"darkMode": true,
"format": "webp",
"quality": 90
}' \
-o linear-mobile-dark.webp
Example 3: Metadata extraction
const res = await fetch('https://pagebolt.dev/api/v1/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.PAGEBOLT_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://my-saas.com',
extractMetadata: true,
response_type: 'json',
format: 'webp',
}),
});
const { data, metadata } = await res.json();
console.log(metadata.title); // "My SaaS — Dashboard"
console.log(metadata.ogImage); // "https://my-saas.com/og.png"
console.log(metadata.httpStatusCode); // 200
// Save the screenshot
const buf = Buffer.from(data, 'base64');
fs.writeFileSync('my-saas.webp', buf);
Example 4: Ad blocking + transparent background
import requests, os
resp = requests.post(
"https://pagebolt.dev/api/v1/screenshot",
headers={
"x-api-key": os.environ["PAGEBOLT_KEY"],
"Content-Type": "application/json",
},
json={
"url": "https://my-blog.com",
"blockAds": True,
"blockBanners": True,
"blockChats": True,
"blockTrackers": True,
"omitBackground": True,
"format": "png",
},
)
with open("clean-screenshot.png", "wb") as f:
f.write(resp.content)
Example 5: Full page with lazy image scroll
curl -X POST https://pagebolt.dev/api/v1/screenshot \
-H "x-api-key: pf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://www.apple.com/iphone",
"fullPage": true,
"fullPageScroll": true,
"fullPageScrollDelay": 500,
"fullPageMaxHeight": 15000,
"format": "jpeg",
"quality": 85
}' \
-o apple-fullpage.jpg
Example 6: Geolocation and timezone
curl -X POST https://pagebolt.dev/api/v1/screenshot \
-H "x-api-key: pf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://weather.com",
"timeZone": "Asia/Tokyo",
"geolocation": {
"latitude": 35.6762,
"longitude": 139.6503,
"accuracy": 100
},
"width": 1440,
"height": 900,
"darkMode": true
}' \
-o weather-tokyo.png
Example 7: Authenticated page with cookies & headers
const res = await fetch('https://pagebolt.dev/api/v1/screenshot', {
method: 'POST',
headers: {
'x-api-key': process.env.PAGEBOLT_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://myapp.com/dashboard',
cookies: [
{ name: 'session', value: 'abc123xyz', domain: 'myapp.com' },
{ name: 'theme', value: 'dark', domain: 'myapp.com' },
],
headers: {
'Accept-Language': 'en-US',
},
authorization: 'Bearer eyJhbGciOiJIUzI1NiIs...',
waitForSelector: '.dashboard-loaded',
darkMode: true,
}),
});
const screenshot = Buffer.from(await res.arrayBuffer());
fs.writeFileSync('dashboard.png', screenshot);
Authenticated Recording
Recording pages behind OAuth, SSO, or session-based auth requires injecting your existing browser session into Pagebolt. The authState parameter handles this for all endpoints — /screenshot, /video, /sequence, and /inspect.
authState parameter lets you hand that session to Pagebolt so it starts already authenticated.
The authState format
{
"authState": {
"cookies": [
{
"name": "session_id",
"value": "abc123xyz",
"domain": "myapp.com",
"path": "/",
"secure": true,
"httpOnly": true
}
],
"localStorage": [
{
"origin": "https://myapp.com",
"items": [
{ "name": "access_token", "value": "eyJhbGciOiJIUzI1NiIs..." },
{ "name": "user_id", "value": "usr_abc123" }
]
}
]
}
}
Step 1 — Extract your session
Log into your app in Chrome, then open DevTools (F12) and paste this snippet into the Console tab. It extracts your cookies and localStorage into the exact authState format and copies it to your clipboard.
(function() {
var origin = location.origin;
var cookies = document.cookie.split(';').filter(Boolean).map(function(c) {
var parts = c.trim().split('=');
var name = parts[0].trim();
var value = parts.slice(1).join('=').trim();
return { name: name, value: value, domain: location.hostname };
});
var lsItems = Object.keys(localStorage).map(function(k) {
return { name: k, value: localStorage.getItem(k) };
});
var authState = {
cookies: cookies,
localStorage: [{ origin: origin, items: lsItems }]
};
var json = JSON.stringify({ authState: authState }, null, 2);
console.log(json);
try { copy(json); console.log('%c✓ Copied to clipboard', 'color: #4ade80'); }
catch(e) { console.log('%c↑ Copy the JSON above', 'color: #facc15'); }
})();
HttpOnly cookies by design. If your app uses only HttpOnly session cookies, use the Pagebolt Chrome extension instead — it has access to all cookies including HttpOnly ones.
Step 2 — Pass it to the API
const res = await fetch('https://pagebolt.dev/api/v1/video', {
method: 'POST',
headers: {
'x-api-key': process.env.PAGEBOLT_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
steps: [
{ action: 'navigate', url: 'https://myapp.com/dashboard' },
{ action: 'click', selector: '.sidebar-nav-reports', note: 'Open reports' },
{ action: 'scroll', y: 400 },
],
authState: {
cookies: [
{ name: 'session_id', value: 'abc123xyz', domain: 'myapp.com' }
],
localStorage: [
{
origin: 'https://myapp.com',
items: [
{ name: 'access_token', value: 'eyJhbGciOiJIUzI1NiIs...' }
]
}
]
},
format: 'mp4',
pace: 'slow',
cursor: { style: 'highlight' },
}),
});
const result = await res.json();
// result.data is base64-encoded MP4
Using the Chrome extension
The Pagebolt Chrome extension has a Capture Auth button in its side panel. Click it while on your logged-in app and your full session state — including HttpOnly cookies — is captured automatically and included in every recording you make from the extension.
/v1/screenshot
Convenience endpoint — pass all parameters as query strings. Ideal for embedding in <img> tags or opening directly in a browser.
url parameter is required. Booleans are "true" / "false" strings. Array params like hideSelectors can be repeated (e.g. hideSelectors=.ad&hideSelectors=.banner).
curl -H "x-api-key: pf_live_your_key" \
"https://pagebolt.dev/api/v1/screenshot?\
url=https://my-saas.com&\
viewportDevice=iphone_14_pro&\
blockBanners=true&\
blockAds=true&\
format=webp" \
-o my-saas-mobile.webp
Signed Embed URLs
Generate HMAC-signed URLs that render a screenshot, OG image, or PDF and can be placed directly in an <img> tag (screenshot, og-image) or linked/iframed (pdf) — no backend proxy required. Hobby plan and above.
The signature binds all parameters — changing any part of the URL invalidates it. Expiry is enforced server-side (max 7 days).
POST /dashboard/sign-embed from your backend to get a signed URL, passing endpoint = "screenshot", "og-image", or "pdf" (defaults to screenshot). Embed that URL in your HTML. When the browser loads it, PageBolt verifies the signature and serves the result — your API key is never exposed to the browser.
Step 1 — Generate the signed URL (server-side)
curl -X POST https://pagebolt.dev/dashboard/sign-embed \
-H "Authorization: Bearer YOUR_JWT_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"endpoint": "screenshot",
"params": {
"url": "https://example.com",
"fullPage": "true",
"blockBanners": "true",
"format": "png"
},
"expiry_hours": 24
}'
Response
{
"url": "https://pagebolt.dev/api/v1/embed/screenshot?url=https%3A%2F%2Fexample.com&fullPage=true&blockBanners=true&format=png&_key=pf_live_abc123&_exp=1740528000&_sig=a3f9e2...",
"endpoint": "screenshot",
"expires_at": "2026-02-26T12:00:00.000Z",
"key_prefix": "pf_live_abc123"
}
Step 2 — Embed in HTML
<!-- Screenshot or OG image -->
<img src="https://pagebolt.dev/api/v1/embed/screenshot?url=...&_key=...&_exp=...&_sig=..." alt="Screenshot" />
<img src="https://pagebolt.dev/api/v1/embed/og-image?title=...&_key=...&_exp=...&_sig=..." alt="OG card" />
<!-- PDF (link or iframe) -->
<a href="https://pagebolt.dev/api/v1/embed/pdf?url=...&_key=...&_exp=...&_sig=...">Download PDF</a>
Endpoints & parameters
| Field | Type | Description |
|---|---|---|
| endpoint | string | screenshot, og-image, or pdf. Default: screenshot. |
| params | object | Capture parameters (all values as strings). For screenshot and pdf, url is required and accepts the same options as the matching POST endpoint. For og-image, pass template fields (template, title, subtitle, colors, etc.). Custom HTML templates are not supported over signed URLs. |
| expiry_hours | number | How long the URL is valid. Default: 24. Max: 168 (7 days). |
POST /dashboard/sign-screenshot endpoint still works and is equivalent to endpoint: "screenshot".
/v1/style
NEWApply styling (frame, background, shadow, theme) to an existing screenshot image. Accepts a base64-encoded image and returns the styled result. Does not count toward your monthly quota — this endpoint performs image processing only, no browser rendering.
curl -X POST https://pagebolt.dev/api/v1/style \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "iVBORw0KGgo...base64-encoded-screenshot...",
"format": "png",
"style": {
"theme": "ocean"
}
}'
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
image | string | Yes | Base64-encoded image data (PNG, JPEG, or WebP). Max 10MB. |
format | string | No | Output format: png, jpeg, webp. Default: png |
style | object | Yes | Style configuration. Use theme for presets, or set individual properties. |
style.theme | string | No | Theme preset name: notion, ocean, sunset, linear, arc, etc. See Screenshot API for full list. |
style.frame | string | No | macos, windows, minimal, or none |
style.background | string | No | Background preset: ocean, sunset, glass, solid, dots, grid, etc. |
style.shadow | string | No | none, xs, sm, md, lg, xl, 2xl |
style.padding | integer | No | Padding in pixels (0–120). Default: 40 |
style.borderRadius | integer | No | Corner radius in pixels (0–40). Default: 12 |
style.bgColor | string | No | Hex color for solid background, e.g. #1e3a5f |
response_type | string | No | json returns base64 JSON. Default: binary image. |
Response
Binary image by default. If response_type: "json":
{
"data": "iVBORw0KGgo...base64...",
"format": "png",
"content_type": "image/png",
"size_bytes": 245120,
"duration_ms": 340
}
/v1/pdf
Generate a PDF from a URL or HTML content. Returns the PDF as binary data.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | — | URL to render (required if no html) |
| html | string | — | Raw HTML to render (required if no url) |
| format | string | A4 | Paper format: A4, Letter, Legal, Tabloid |
| landscape | boolean | false | Landscape orientation |
| printBackground | boolean | true | Include CSS backgrounds |
| margin | string | — | CSS margin for all sides (e.g. "1cm") |
| margins | object | — | {top, right, bottom, left} in CSS units |
| displayHeaderFooter | boolean | false | Show header and footer |
| headerTemplate | string | — | HTML template for header |
| footerTemplate | string | — | HTML template for footer |
| scale | number | 1 | Rendering scale (0.1–2) |
| pageRanges | string | — | e.g. "1-5, 8" |
| width | integer | — | Viewport width for rendering |
| delay | integer | 0 | Wait ms before rendering (max 10000) |
| response_type | string | binary | Set to "json" for base64 response |
Example 8: Generate an invoice PDF
curl -X POST https://pagebolt.dev/api/v1/pdf \
-H "x-api-key: pf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1>Invoice #1234</h1><p>Amount: $99.00</p>",
"format": "A4",
"margin": "2cm",
"printBackground": true,
"displayHeaderFooter": true,
"footerTemplate": "<div style=\"font-size:10px;text-align:center;width:100%\">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span></div>"
}' \
-o invoice.pdf
/v1/og-image
Generate dynamic Open Graph / social card images. Use built-in templates or provide custom HTML.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| template | string | default | default, minimal, or gradient |
| html | string | — | Custom HTML template (overrides template) |
| title | string | — | Main title text |
| subtitle | string | — | Subtitle text |
| logo | string | — | Logo image URL |
| bgColor | string | #0f172a | Background color (hex) |
| textColor | string | #f8fafc | Text color (hex) |
| accentColor | string | #6366f1 | Accent color (hex) |
| bgImage | string | — | Background image URL |
| width | integer | 1200 | Image width (max 2400) |
| height | integer | 630 | Image height (max 1260) |
| format | string | png | png, jpeg, or webp |
| response_type | string | binary | Set to "json" for base64 response |
Example 9: Create a social card
curl -X POST https://pagebolt.dev/api/v1/og-image \
-H "x-api-key: pf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"template": "gradient",
"title": "How to Build a SaaS in 2026",
"subtitle": "A practical guide for solo founders",
"accentColor": "#8b5cf6",
"bgColor": "#1e1b4b",
"logo": "https://mysite.com/logo.png"
}' \
-o social-card.png
/v1/sequence
NewExecute a multi-step browser automation sequence. Navigate pages, interact with elements (click, fill forms, scroll), and capture multiple screenshots and PDFs in a single browser session. Local URLs are not supported — Puppeteer runs on our servers, not yours. To record a local dev server, expose it first with ngrok http 3000 and use the public ngrok URL.
Top-Level Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| steps | array | — | Required. Array of step objects (max 20) |
| viewport | object | — | {width, height} — default 1280×720 |
| darkMode | boolean | false | Emulate dark color scheme |
| blockBanners | boolean | false | Auto-hide cookie consent banners |
| deviceScaleFactor | number | 1 | Pixel ratio (max 3, use 2 for retina) |
Available Actions
| Action | Required Fields | Description |
|---|---|---|
| navigate | url | Go to a URL (http/https only) |
| click | selector | Click an element by CSS selector |
| fill | selector, value | Clear and type into an input field |
| select | selector, value | Select a dropdown option by value |
| hover | selector | Hover over an element (reveals tooltips, dropdowns) |
| scroll | selector or x, y | Scroll to element or absolute position |
| wait | ms | Wait fixed milliseconds (max 10000) |
| wait_for | selector | Wait for element to appear and become visible (optional timeout, max 15s) |
| evaluate | script | Run JavaScript in page context (max 5000 chars, max 2 per sequence) |
| screenshot | name | Capture current state. Optional: format, fullPage, quality, selector, style |
| name | Generate PDF. Optional: format (A4, Letter, Legal, Tabloid, A3, A5), landscape, margin, scale |
style object to any screenshot step to apply post-capture styling (frames, backgrounds, shadows, themes). Uses the same options as the Screenshot API style parameter. Example: { "action": "screenshot", "name": "hero", "style": { "theme": "ocean" } }
Example 10: Capture a login flow
curl -X POST https://pagebolt.dev/api/v1/sequence \
-H "x-api-key: pf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"steps": [
{"action": "navigate", "url": "https://example.com/login"},
{"action": "screenshot", "name": "login-page"},
{"action": "fill", "selector": "#email", "value": "user@example.com"},
{"action": "fill", "selector": "#password", "value": "demo123"},
{"action": "click", "selector": "button[type=submit]"},
{"action": "wait_for", "selector": ".dashboard", "timeout": 5000},
{"action": "screenshot", "name": "dashboard", "fullPage": true}
],
"viewport": {"width": 1440, "height": 900},
"blockBanners": true
}'
Response
{
"outputs": [
{
"name": "login-page",
"type": "screenshot",
"format": "png",
"content_type": "image/png",
"data": "iVBORw0KGgo...",
"size_bytes": 145832,
"step_index": 1,
"duration_ms": 342
},
{
"name": "dashboard",
"type": "screenshot",
"format": "png",
"content_type": "image/png",
"data": "iVBORw0KGgo...",
"size_bytes": 287654,
"step_index": 6,
"duration_ms": 521
}
],
"step_results": [
{"step_index": 0, "action": "navigate", "status": "ok", "duration_ms": 2341},
{"step_index": 1, "action": "screenshot", "status": "ok", "name": "login-page", "duration_ms": 342},
{"step_index": 2, "action": "fill", "status": "ok", "duration_ms": 87},
{"step_index": 3, "action": "fill", "status": "ok", "duration_ms": 92},
{"step_index": 4, "action": "click", "status": "ok", "duration_ms": 45},
{"step_index": 5, "action": "wait_for", "status": "ok", "duration_ms": 1823},
{"step_index": 6, "action": "screenshot", "status": "ok", "name": "dashboard", "duration_ms": 521}
],
"steps_completed": 7,
"total_steps": 7,
"total_duration_ms": 5251,
"usage": {
"outputs_charged": 2,
"remaining": 4998
}
}
/v1/video
NewRecord a professional demo video of multi-step browser automation with cursor highlighting and click effects. Each video costs 3 API requests. Local URLs are not supported — Puppeteer runs on our servers, not yours. To record a local dev server, expose it first with ngrok http 3000 and use the public ngrok URL.
Top-Level Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| steps | array | — | Required. Array of step objects (same actions as sequence, except no screenshot/pdf) |
| viewport | object | — | {width, height} — default 1280×720 |
| format | string | mp4 | mp4, webm, or gif |
| framerate | number | 30 | Frames per second: 24, 30, or 60 |
| darkMode | boolean | false | Emulate dark color scheme |
| blockBanners | boolean | true | Auto-hide cookie consent banners |
| deviceScaleFactor | number | 1 | Pixel ratio 1–3 (use 2 for retina) |
| pace | number | string | 1.0 | Controls video speed. Number 0.25–6.0 (higher = slower/more deliberate) or preset: "fast" (0.5×), "normal" (1×), "slow" (2×), "dramatic" (3×), "cinematic" (4.5×). Scales cursor movement, pauses between steps, typing speed, and all cosmetic delays. |
| response_type | string | binary | Set to "json" for base64-encoded response with metadata |
| host | boolean | string | false | Host the video at a shareable pagebolt.dev/v/:id URL. true = private (owner-only); "public" = anyone with the link. Returns JSON with url, embed_url, file_url, visibility, and expires_at. |
| public | boolean | false | Shorthand for host: "public". |
| title | string | — | Optional title shown on the hosted watch page and in link previews. |
Hosted videos
Add host: true to get a shareable pagebolt.dev/v/:id watch page and an embed_url for an <iframe> instead of a raw blob. Videos are private by default (owner-only); use host: "public" for a link anyone can open. Hosted videos auto-expire by plan: Free 7 days, Hobby 30 days, Starter/Growth/Scale 90 days.
Cursor Options
| Parameter | Type | Default | Description |
|---|---|---|---|
| cursor.visible | boolean | true | Show animated cursor in the recording |
| cursor.style | string | highlight | highlight, circle, spotlight, dot, or classic NEW |
| cursor.color | string | #3B82F6 | Cursor color (hex) |
| cursor.size | number | 20 | Cursor size in pixels (8–60) |
| cursor.smoothing | boolean | true | Smooth cursor movement between actions |
| cursor.opacity | number | 1.0 | Cursor opacity (0.1–1.0) |
| cursor.persist | boolean | false | Keep cursor visible between actions instead of fading out NEW |
Zoom Options
| Parameter | Type | Default | Description |
|---|---|---|---|
Per-step zoom — Add a zoom object to individual click/dblclick steps to zoom into that specific action.
{ "action": "click", "selector": "#btn", "zoom": { "enabled": true, "level": 1.5 } }
| |||
Click Effect Options
| Parameter | Type | Default | Description |
|---|---|---|---|
| clickEffect.enabled | boolean | true | Show visual feedback on clicks |
| clickEffect.style | string | ripple | ripple, pulse, or ring |
| clickEffect.color | string | cursor color | Click effect color (hex) — defaults to cursor.color |
Frame Options NEW
| Parameter | Type | Default | Description |
|---|---|---|---|
| frame.enabled | boolean | false | Add browser window frame chrome overlay |
| frame.style | string | macos | macos, windows, or minimal |
| frame.theme | string | auto | light, dark, or auto (matches darkMode) |
| frame.showUrl | boolean | true | Show the current URL in the frame’s address bar |
Background Options NEW
| Parameter | Type | Default | Description |
|---|---|---|---|
| background.enabled | boolean | false | Add a decorative background behind the browser viewport |
| background.type | string | gradient | gradient or solid |
| background.gradient | string | ocean | Preset: ocean, sunset, forest, midnight, aurora, or custom |
| background.color | string | #1e3a5f | Background color for solid type (hex) |
| background.colors | array | — | Array of 2 hex colors for custom gradient |
| background.padding | number | 40 | Padding around viewport in pixels (0–120) |
| background.borderRadius | number | 12 | Border radius of viewport in pixels (0–40) |
Audio Guide Options NEW
Add AI-generated narration to your demo videos. Two modes are available:
Per-step mode (default) — Add narration text to individual steps. Each clip is spoken before the action executes.
Script mode — Provide a single audioGuide.script with {{N}} step markers for continuous narration. Steps execute when the narration reaches each marker, creating a natural flow. Example: "Welcome to our app. {{1}} Here are the features. {{2}} Click to get started."
| Parameter | Type | Default | Description |
|---|---|---|---|
| audioGuide.enabled | boolean | false | Enable Audio Guide narration |
| audioGuide.provider | string | azure | TTS provider: azure or openai |
| audioGuide.voice | string | ava | Voice preset name (see list below) |
| audioGuide.speed | number | 1.0 | Speech rate (0.5–2.0) |
| audioGuide.pitch | string | default | Voice pitch (Azure only) |
| audioGuide.volume | string | default | Audio volume (Azure only) |
| audioGuide.style | string | — | Speaking style (Azure only): narration-professional, cheerful, etc. |
| audioGuide.styleDegree | number | 1.0 | Style intensity (Azure only, 0.01–2.0) |
| audioGuide.model | string | tts-1 | OpenAI model (OpenAI only) |
| audioGuide.script | string | — | Script mode narration. A single text with {{N}} step markers (0-indexed). When provided, per-step narration fields are ignored. Max 5000 chars. |
Available Voices
| Provider | Voice | Gender |
|---|---|---|
| Azure | ava | Female |
| andrew | Male | |
| emma | Female | |
| brian | Male | |
| aria | Female | |
| guy | Male | |
| jenny | Female | |
| davis | Male | |
| christopher | Male | |
| michelle | Female | |
| OpenAI | alloy | Neutral |
| echo | Male | |
| fable | Neutral | |
| nova | Female | |
| onyx | Male | |
| shimmer | Female |
Available Actions
| Action | Required Fields | Description |
|---|---|---|
| navigate | url | Go to a URL (http/https only) |
| click | selector | Click an element by CSS selector |
| dblclick | selector | Double-click an element by CSS selector |
| fill | selector, value | Clear and type into an input field |
| select | selector, value | Select a dropdown option by value |
| hover | selector | Hover over an element (reveals tooltips, dropdowns) |
| scroll | selector or x, y | Scroll to element or absolute position |
| wait | ms | Wait fixed milliseconds (max 10000). Add live: true to capture animated content. |
| wait_for | selector | Wait for element to appear and become visible (optional timeout, max 15s) |
| evaluate | script | Run JavaScript in page context (max 5000 chars, max 2 per sequence) |
Step Notes and Narration NEW
Add a note string to any step to display a text overlay in the video — perfect for guided product tours, onboarding walkthroughs, and tutorial videos. The note appears as a styled callout near the target element before the action executes. Use narration with audioGuide.enabled for spoken voice-over on each step.
| Parameter | Type | Max Length | Description |
|---|---|---|---|
| note | string | 500 chars | Optional. Text overlay displayed before the action executes. Available on navigate, click, dblclick, fill, hover, and scroll actions. |
| narration | string | 500 chars | Optional. TTS narration spoken before the action when audioGuide.enabled is true. Available on same actions as note. |
Plan Limits
| Plan | Base + Per-Step Budget | Max Steps | Formats |
|---|---|---|---|
| Free | 20s + 5s/step | 10 | mp4 |
| Starter | 30s + 5s/step | 20 | mp4, webm |
| Growth | 45s + 6s/step | 30 | mp4, webm, gif |
| Scale | 60s + 6s/step | 50 | mp4, webm, gif |
base + (steps × per-step budget × pace). Using pace: "slow", pace: "dramatic", or pace: "cinematic" automatically extends the allowed recording time so your video doesn't time out.
Example: Record a guided demo video
curl -X POST https://pagebolt.dev/api/v1/video \
-H "x-api-key: pf_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"steps": [
{ "action": "navigate", "url": "https://pagebolt.dev" },
{ "action": "scroll", "selector": "#pricing" },
{ "action": "wait", "ms": 3000 },
{ "action": "click", "selector": "#get-started",
"note": "Click here to sign up for PageBolt" }
],
"viewport": { "width": 1280, "height": 800 },
"format": "mp4",
"blockBanners": true,
"pace": "normal",
"cursor": { "style": "highlight", "color": "#3B82F6", "persist": true },
"clickEffect": { "enabled": true, "style": "ripple" },
"frame": { "enabled": true, "style": "macos", "showUrl": true },
"background": {
"enabled": true, "type": "gradient", "padding": 120,
"borderRadius": 12, "gradient": "sunset"
}
}' \
-o demo.mp4
Example: Record a demo video with Audio Guide
curl -X POST https://pagebolt.dev/api/v1/video \
-H "x-api-key: pf_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"steps": [
{ "action": "navigate", "url": "https://pagebolt.dev",
"narration": "Welcome to PageBolt, the screenshot and automation API." },
{ "action": "scroll", "selector": "#pricing",
"narration": "Let me show you our pricing plans." },
{ "action": "click", "selector": "#get-started",
"note": "Click here to sign up",
"narration": "Click Get Started to create your free account." }
],
"viewport": { "width": 1280, "height": 800 },
"format": "mp4",
"blockBanners": true,
"pace": "normal",
"audioGuide": {
"enabled": true,
"provider": "azure",
"voice": "ava",
"speed": 1.0
}
}' \
-o demo-with-narration.mp4
Example: Script Mode — continuous narration with step markers
curl -X POST https://pagebolt.dev/api/v1/video \
-H "x-api-key: pf_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"steps": [
{ "action": "navigate", "url": "https://pagebolt.dev" },
{ "action": "scroll", "selector": "#features" },
{ "action": "click", "selector": "#tab-python" },
{ "action": "scroll", "selector": "#pricing" },
{ "action": "click", "selector": "#get-started" }
],
"viewport": { "width": 1280, "height": 800 },
"format": "mp4",
"blockBanners": true,
"audioGuide": {
"enabled": true,
"provider": "azure",
"voice": "ava",
"script": "Welcome to PageBolt — the web capture API. {{1}} Here are the core features — screenshots, PDFs, video recording, and more. {{2}} And it works in any language — here is the Python example. {{3}} Pricing is simple and transparent — start free. {{4}} Click here to get your free API key."
}
}' \
-o demo-script-mode.mp4
In script mode, the narration is synthesized as a single continuous audio track. Step markers {{N}} (0-indexed) tell the video engine when to execute each step, synchronized to the narration timing. Steps not referenced by a marker still execute at their natural position.
JSON Response (when response_type=json)
{
"data": "base64...",
"format": "mp4",
"content_type": "video/mp4",
"size_bytes": 2457600,
"duration_ms": 12400,
"frames": 372,
"steps_completed": 5,
"total_steps": 5,
"step_results": [
{"step_index": 0, "action": "navigate", "status": "ok", "duration_ms": 2341},
{"step_index": 1, "action": "click", "status": "ok", "duration_ms": 512},
{"step_index": 2, "action": "fill", "status": "ok", "duration_ms": 1087},
{"step_index": 3, "action": "click", "status": "ok", "duration_ms": 445},
{"step_index": 4, "action": "wait", "status": "ok", "duration_ms": 2000}
],
"usage": {
"video_cost": 3,
"remaining": 4847
}
}
Content-Type header (video/mp4, video/webm, or image/gif). Status 200 on success.
/v1/observe
NewThe agent perception endpoint. In a single page load it returns a compact, token-budgeted observation of any page: id-indexed interactive elements (role, name, CSS selector, state), a heuristic page-type classification (login, signup, search, article, form…), and grouped suggested actions. Optionally bundle readable content (Markdown), the ARIA tree, and a screenshot — all from one request.
Try it live — no signup/v1/observe turns any un-instrumented URL into an agent-ready observation, headless and server-side. It is far more token-efficient than a raw screenshot or full DOM dump — a simple login page is just a few hundred tokens. Feed the returned selectors straight into /v1/sequence to act.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | — | Required (if no html). URL to observe. |
| html | string | — | Raw HTML to observe (alternative to url). |
| maxElements | integer | 40 | Cap on interactive elements returned (1–150). Lower = fewer tokens. |
| includeRects | boolean | false | Include bounding boxes {x,y,w,h} per element. |
| includeContent | boolean | false | Also extract main readable content as Markdown (Readability). |
| includeAriaTree | boolean | false | Also include the interesting-only ARIA accessibility tree. |
| includeScreenshot | boolean | false | Also capture a screenshot (base64) in the same page load. |
| screenshotFormat | string | jpeg | jpeg, png, or webp (when includeScreenshot). |
| includeConsole | boolean | false | Also capture browser console output (log/info/warn/error) and uncaught page errors during load. Returned under console. |
| viewportDevice | string | — | Device preset (e.g. "iphone_14_pro"). See /devices. |
| darkMode | boolean | false | Emulate dark color scheme. |
| waitUntil | string | networkidle2 | load, domcontentloaded, networkidle0, networkidle2. |
| waitForSelector | string | — | Wait for this CSS selector before observing. |
| cookies | array | — | Array of "name=value" strings or {name, value, domain?} objects. |
| headers | object | — | Extra HTTP headers. |
| authorization | string | — | Authorization header value. |
| blockBanners | boolean | false | Hide cookie consent banners. |
| blockAds / blockChats / blockTrackers | boolean | false | Block ads, chat widgets, or trackers. |
Response
Returns a compact JSON observation:
{
"url": "https://example.com/login",
"title": "Sign in",
"pageType": "login",
"lang": "en",
"metadata": { "description": "...", "httpStatusCode": 200 },
"headings": [ { "level": 1, "text": "Log in" } ],
"elements": [
{ "id": "e1", "role": "textbox", "type": "email", "name": "Email",
"selector": "#email", "state": ["required"] },
{ "id": "e2", "role": "textbox", "type": "password", "name": "Password",
"selector": "#pw", "state": ["required"] },
{ "id": "e3", "role": "button", "type": "submit", "name": "Sign in",
"selector": "button[type='submit']" }
],
"forms": [
{ "selector": "form", "action": "/login", "method": "POST",
"fieldIds": ["e1", "e2", "e3"] }
],
"actions": [
{ "intent": "login", "elementIds": ["e1", "e2", "e3"] }
],
"stats": { "elementCount": 3, "estimatedTokens": 281 },
"duration_ms": 1180
}
pageType is one of login, signup, search, article, form, or generic. When requested, the response also includes content (Markdown + word count), ariaTree, and screenshot ({ format, base64 }).
cURL Example
curl -X POST https://pagebolt.dev/api/v1/observe \
-H "x-api-key: pf_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/login",
"blockBanners": true
}'
/v1/sequence — observe to understand the page, then act using the returned selectors.
includeContent / includeAriaTree data) is extracted from a third-party page and may contain hidden or visible prompt-injection — e.g. fake instructions like “ignore previous instructions” aimed at your agent. Feed it to your LLM strictly as data, never as instructions, and never let page content trigger actions the user didn’t ask for. The PageBolt MCP server wraps this output in explicit UNTRUSTED PAGE CONTENT markers for you.
/v1/act
NewThe goal-driven automation endpoint. Give it a URL and a plain-English goal — PageBolt runs an observe → plan → act → verify loop server-side until the goal is met. You get back a structured trace of every action it took plus a success/failure status. This is the “hands” that sit on top of /v1/observe (the “eyes”) — no need to author selectors or a step list yourself.
/v1/act vs /v1/sequence. Use /v1/sequence when you already know the exact steps and selectors (deterministic, cheapest). Use /v1/act when you only know the outcome you want (“log in and go to billing”, “accept the cookie banner and start a trial”) and want PageBolt to figure out the steps.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | — | Required. The page to start on. |
| goal | string | — | Required. Plain-English description of the outcome you want (min 3 chars). |
| maxSteps | integer | 8 | Cap on planning iterations. Clamped to your plan ceiling (Starter 10, Growth 15, Scale 20). |
| allowedDomains | array | [start host] | Hosts the agent may navigate to. Defaults to the start URL’s host only — navigation elsewhere is rejected. |
| credentials | object | — | { username, password }. Referenced in the loop as {{username}} / {{password}}, substituted only at execution time. Never logged, never sent to the planner LLM. |
| session_id | string | — | Run inside an existing session (reuses cookies/login). Otherwise an ephemeral browser is used and discarded. |
Response
Returns the run status, a redacted action trace, and the final observation:
{
"status": "succeeded",
"goal": "Click the only link on the page to open the IANA website.",
"steps_taken": 1,
"final_url": "https://www.iana.org/help/example-domains",
"summary": "The link was clicked and the IANA website opened.",
"trace": [
{ "step": 1, "action": "click",
"target": "a[href='https://iana.org/domains/example']",
"thought": "Click the only link on the page.", "result": "ok" }
],
"final_observation": {
"url": "https://www.iana.org/help/example-domains",
"title": "Example Domains", "pageType": "article", "elementCount": 12
},
"usage": { "inputTokens": 2422, "outputTokens": 328,
"plannerCalls": 2, "act_cost": 3, "remaining": 4997 }
}
status is one of succeeded, failed, max_steps, or timeout. Any value the agent filled from your credentials appears in the trace as <redacted> — the real secret is never returned.
cURL Example
curl -X POST https://pagebolt.dev/api/v1/act \
-H "x-api-key: pf_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/login",
"goal": "Log in and open the billing page",
"credentials": { "username": "me@example.com", "password": "..." },
"allowedDomains": ["app.example.com"],
"maxSteps": 10
}'
maxSteps, a 90s wall-clock limit, or when it repeats an action without progress.
allowedDomains and internal/private URLs are blocked (SSRF-safe). Page text is treated as untrusted data — the planner is instructed to pursue only your goal and never follow instructions embedded in page content. This is a powerful endpoint: scope allowedDomains tightly and avoid pointing it at destructive flows.
/v1/inspect
NewInspect a web page and get a structured map of all interactive elements, headings, forms, links, and images — each with a unique CSS selector ready for use with /v1/sequence. Returns compact JSON optimized for AI agents.
/v1/sequence to discover what elements exist on a page and get reliable CSS selectors. Returns text (2-15 KB) instead of an image, saving tokens and improving accuracy.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | — | Required (if no html). URL to inspect. |
| html | string | — | Raw HTML to inspect (alternative to url). |
| width | integer | 1280 | Viewport width in pixels (1–3840). |
| height | integer | 720 | Viewport height in pixels (1–2160). |
| viewportDevice | string | — | Device preset (e.g. "iphone_14_pro"). See /devices. |
| darkMode | boolean | false | Emulate dark color scheme. |
| waitUntil | string | networkidle2 | load, domcontentloaded, networkidle0, networkidle2. |
| waitForSelector | string | — | Wait for this CSS selector before inspecting. |
| cookies | array | — | Array of "name=value" strings or {name, value, domain?} objects. |
| headers | object | — | Extra HTTP headers. |
| authorization | string | — | Authorization header value. |
| authState | object | — | Full browser auth state (cookies + localStorage). See Authenticated Recording. |
| userAgent | string | — | Override the browser User-Agent. |
| blockBanners | boolean | false | Hide cookie consent banners. |
| blockAds | boolean | false | Block advertisements. |
| blockChats | boolean | false | Block live chat widgets. |
| blockTrackers | boolean | false | Block tracking scripts. |
| bypassCSP | boolean | false | Bypass Content-Security-Policy. |
| hideSelectors | array | — | CSS selectors to hide before inspecting. |
| injectCss | string | — | Custom CSS to inject. |
| injectJs | string | — | Custom JavaScript to execute before inspecting. |
| includeConsole | boolean | false | Also capture browser console output (log/info/warn/error) and uncaught page errors during load. Returned under console — useful for debugging a page's runtime, not just its DOM. |
Response
Always returns JSON with the following structure:
{
"url": "https://example.com",
"title": "Example Page",
"metadata": {
"description": "Page description",
"ogTitle": "OG Title",
"lang": "en",
"httpStatusCode": 200
},
"elements": [
{
"tag": "button",
"role": "button",
"text": "Sign Up",
"selector": "#signup-btn",
"attributes": { "id": "signup-btn", "type": "submit" },
"rect": { "x": 120, "y": 340, "width": 160, "height": 40 }
},
{
"tag": "input",
"role": "input",
"text": "",
"selector": "input[name='email']",
"attributes": { "type": "email", "name": "email", "placeholder": "Enter email" },
"rect": { "x": 20, "y": 280, "width": 300, "height": 36 }
}
],
"headings": [
{ "level": 1, "text": "Welcome", "selector": "h1" }
],
"forms": [
{ "selector": "form#signup", "action": "/signup", "method": "POST",
"fields": ["input[name='email']", "input[name='password']"] }
],
"links": [
{ "text": "About", "href": "/about", "selector": "nav a[href='/about']" }
],
"images": [
{ "alt": "Logo", "src": "/logo.png", "selector": "img[alt='Logo']",
"rect": { "x": 10, "y": 10, "width": 120, "height": 40 } }
],
"duration_ms": 1234
}
cURL Example
curl -X POST https://pagebolt.dev/api/v1/inspect \
-H "x-api-key: pf_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"blockBanners": true
}'
/v1/sequence for reliable browser automation — inspect first, then interact using the returned selectors. Like /v1/observe, the returned page text is untrusted — treat it as data, not instructions (see the security note above).
/v1/extract
NewLoad a URL in a real browser, strip navigation/ads/boilerplate using Mozilla Readability, and return the main article content as clean Markdown. Includes title, word count, author, and excerpt.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | — | Required. URL to load and extract content from. |
| width | integer | 1280 | Viewport width in pixels. |
| waitUntil | string | networkidle2 | load, domcontentloaded, networkidle0, networkidle2. |
| waitForSelector | string | — | Wait for a CSS selector before extracting. |
| cookies | array | — | Array of "name=value" strings (for authenticated pages). |
| headers | object | — | Extra HTTP headers. |
| authorization | string | — | Authorization header value. |
| userAgent | string | — | Override browser User-Agent. |
| blockBanners | boolean | false | Hide cookie consent banners before extracting. |
| blockAds | boolean | false | Block advertisements. |
| blockTrackers | boolean | false | Block tracking scripts. |
Response
{
"url": "https://example.com/article",
"title": "Example Article Title",
"markdown": "# Example Article Title\n\nThe main article content...",
"wordCount": 842,
"excerpt": "A short summary of the article content.",
"byline": "Jane Smith",
"siteName": "Example Blog",
"lang": "en",
"duration_ms": 1840
}
Example
curl -X POST https://pagebolt.dev/api/v1/extract \
-H "x-api-key: pf_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://en.wikipedia.org/wiki/Web_scraping",
"blockBanners": true
}'
/v1/audit
NewLoad a URL in a real browser and run a full WCAG 2.1 A/AA accessibility audit using axe-core by Deque. Returns violations with impact level, affected nodes, and fix guidance — plus passes and incomplete items.
violations array maps directly to audit findings in VPAT reports.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | — | Required. URL to audit. |
| width | integer | 1280 | Viewport width in pixels (affects responsive layout). |
| viewportDevice | string | — | Device preset for mobile auditing (e.g. "iphone_14_pro"). |
| waitUntil | string | networkidle2 | load, domcontentloaded, networkidle0, networkidle2. |
| waitForSelector | string | — | Wait for a CSS selector before auditing (useful for SPAs). |
| cookies | array | — | Authenticate before auditing (audit logged-in views). |
| headers | object | — | Extra HTTP headers. |
| userAgent | string | — | Override browser User-Agent. |
| blockBanners | boolean | false | Hide cookie banners before auditing. |
Response
{
"url": "https://example.com",
"summary": {
"violations": 4,
"passes": 38,
"incomplete": 2,
"critical": 1,
"serious": 2,
"moderate": 1,
"minor": 0
},
"violations": [
{
"id": "color-contrast",
"impact": "serious",
"description": "Elements must have sufficient color contrast",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.10/color-contrast",
"nodes": [
{
"html": "<a href=\"/about\" class=\"nav-link\">About</a>",
"target": [".nav-link"],
"failureSummary": "Fix any of: Element has insufficient color contrast of 2.5:1 (foreground: #888888, background: #ffffff, required: 4.5:1)"
}
]
}
],
"passes": [
{ "id": "document-title", "description": "Documents must have <title> element" }
],
"incomplete": [],
"duration_ms": 2310
}
Example
curl -X POST https://pagebolt.dev/api/v1/audit \
-H "x-api-key: pf_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"blockBanners": true
}'
/v1/aria
NewCapture the ARIA accessibility tree of a page — the semantic structure that screen readers and AI agents perceive, with roles, names, and nesting. Accepts a url or raw html.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | — | Required unless html is provided. URL to capture. |
| html | string | — | Raw HTML to render instead of a URL. |
| width | integer | 1280 | Viewport width in pixels. |
| viewportDevice | string | — | Device preset (e.g. "iphone_14_pro"). |
| waitUntil | string | networkidle2 | load, domcontentloaded, networkidle0, networkidle2. |
| waitForSelector | string | — | Wait for a CSS selector before capturing (useful for SPAs). |
| darkMode | boolean | false | Emulate dark color scheme. |
| blockBanners | boolean | false | Hide cookie consent banners before capturing. |
Response
{
"url": "https://example.com",
"title": "Example Domain",
"tree": {
"role": "RootWebArea",
"name": "Example Domain",
"children": [
{ "role": "heading", "name": "Example Domain", "level": 1 },
{ "role": "link", "name": "More information..." }
]
},
"duration_ms": 1320
}
Example
curl -X POST https://pagebolt.dev/api/v1/aria \
-H "x-api-key: pf_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"blockBanners": true
}'
/v1/diff
NewRender two pages and pixel-compare them. Returns the percentage of pixels changed, the changed-pixel count, and a base64 diff image that highlights exactly what moved. Compare two URLs (e.g. production vs staging) or two HTML snapshots.
changed_pct exceeds your threshold — catch unintended UI changes before users do.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url_a | string | — | Required unless html_a is provided. First page to compare. |
| url_b | string | — | Required unless html_b is provided. Second page to compare. |
| html_a / html_b | string | — | Raw HTML alternatives to url_a / url_b. |
| threshold | number | 0.1 | Per-pixel matching sensitivity, 0–1. Lower = stricter. |
| width | integer | 1280 | Viewport width in pixels. |
| height | integer | — | Viewport height in pixels. |
| viewportDevice | string | — | Device preset (e.g. "iphone_14_pro"). |
| blockBanners | boolean | false | Hide cookie consent banners on both pages before comparing. |
Response
{
"url_a": "https://example.com",
"url_b": "https://staging.example.com",
"changed_pct": 2.43,
"changed_pixels": 19847,
"total_pixels": 816000,
"diff_image": "data:image/png;base64,iVBORw0KGgo...",
"duration_ms": 3210
}
Example
curl -X POST https://pagebolt.dev/api/v1/diff \
-H "x-api-key: pf_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url_a": "https://example.com",
"url_b": "https://staging.example.com",
"threshold": 0.1,
"blockBanners": true
}'
Persistent Browser Sessions NEW
Sessions let you keep a browser page alive across multiple API calls. Cookies, localStorage, and authentication state persist between requests — useful for multi-step agent workflows that need to log in once and then interact with authenticated pages repeatedly.
403.Important: Sessions are in-memory only. They do not survive server restarts. Build your workflows to handle a missing session gracefully (catch the
404 and re-authenticate).
Limits
| Limit | Value | Notes |
|---|---|---|
| Max concurrent sessions (global) | 3 | Shared across all users. Returns 503 if full. |
| Max sessions per API key | 2 | Returns 429 if exceeded. |
| Inactivity timeout | 10 minutes | Resets on each request that uses the session. |
| Hard cap | 30 minutes | Absolute maximum regardless of activity. |
Example: Log in once, screenshot multiple pages
# 1. Create a session
SESSION=$(curl -s -X POST https://pagebolt.dev/api/v1/sessions \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' | jq -r .session_id)
# 2. Log in (auth cookies are now stored in the session)
curl -s -X POST https://pagebolt.dev/api/v1/sequence \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"session_id\": \"$SESSION\",
\"steps\": [
{\"action\": \"navigate\", \"url\": \"https://app.example.com/login\"},
{\"action\": \"fill\", \"selector\": \"#email\", \"value\": \"user@example.com\"},
{\"action\": \"fill\", \"selector\": \"#password\", \"value\": \"hunter2\"},
{\"action\": \"click\", \"selector\": \"button[type=submit]\"},
{\"action\": \"screenshot\", \"name\": \"logged-in\"}
]
}" > /dev/null
# 3. Take screenshots of authenticated pages (session still alive)
curl -X POST https://pagebolt.dev/api/v1/screenshot \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"session_id\": \"$SESSION\", \"url\": \"https://app.example.com/dashboard\"}" \
-o dashboard.png
# 4. Clean up when done
curl -X DELETE https://pagebolt.dev/api/v1/sessions/$SESSION \
-H "x-api-key: YOUR_API_KEY"
/v1/sessions
NewCreate a new persistent browser session. Returns a session_id you can pass to /screenshot and /sequence requests.
Request body
| Parameter | Type | Default | Description |
|---|---|---|---|
| cookies | array | — | Optional. Array of cookie objects to pre-set on the session page before any navigation. |
| viewport | object | 1280×720 | Optional. { "width": 1280, "height": 720 } |
| stealth | boolean | false | Optional. Use puppeteer-extra stealth plugin for this session. Stealth sessions get their own isolated browser instance. |
Response
{
"session_id": "sess_a1b2c3d4e5f6g7h8i9j0k1l2",
"expires_at": "2026-02-23T10:30:00.000Z",
"expires_in_seconds": 600,
"note": "Sessions expire after 10 minutes of inactivity. They do not persist across server restarts."
}
/v1/sessions
List all active sessions owned by the current API key, with idle time and remaining TTL for each.
Response
{
"sessions": [
{
"session_id": "sess_a1b2c3d4e5f6g7h8i9j0k1l2",
"created_at": "2026-02-23T10:20:00.000Z",
"expires_at": "2026-02-23T10:32:15.000Z",
"idle_seconds": 45,
"expires_in_seconds": 555
}
],
"count": 1
}
/v1/sessions/:id
Destroy a session immediately, releasing the browser page. Returns 204 No Content on success, or 404 if the session has already expired or belongs to a different key.
Always destroy sessions when your workflow is complete rather than waiting for the inactivity timeout to free the slot.
/v1/usage
Check your current usage and plan limits programmatically. No parameters needed.
curl https://pagebolt.dev/api/v1/usage \
-H "x-api-key: pf_live_your_key"
Response
{
"plan": "starter",
"usage": {
"current": 1247,
"limit": 5000,
"remaining": 3753
}
}
/v1/devices
NewReturns the list of available device presets for the viewportDevice parameter. No parameters needed.
curl https://pagebolt.dev/api/v1/devices \
-H "x-api-key: pf_live_your_key"
Response (excerpt)
{
"devices": [
{"id": "iphone_se", "name": "iPhone SE", "width": 375, "height": 667, "deviceScaleFactor": 2, "mobile": true},
{"id": "iphone_14_pro", "name": "iPhone 14 Pro", "width": 393, "height": 852, "deviceScaleFactor": 3, "mobile": true},
{"id": "iphone_15_pro_max", "name": "iPhone 15 Pro Max", "width": 430, "height": 932, "deviceScaleFactor": 3, "mobile": true},
{"id": "ipad_pro_12", "name": "iPad Pro 12.9\"", "width": 1024, "height": 1366, "deviceScaleFactor": 2, "mobile": true},
{"id": "pixel_8_pro", "name": "Google Pixel 8 Pro", "width": 412, "height": 915, "deviceScaleFactor": 3.5, "mobile": true},
{"id": "galaxy_s24_ultra", "name": "Galaxy S24 Ultra", "width": 412, "height": 915, "deviceScaleFactor": 3.5, "mobile": true},
{"id": "macbook_pro_14", "name": "MacBook Pro 14\"", "width": 1512, "height": 982, "deviceScaleFactor": 2, "mobile": false},
{"id": "desktop_4k", "name": "Desktop 4K", "width": 3840, "height": 2160, "deviceScaleFactor": 1, "mobile": false}
]
}
id value as the viewportDevice parameter.
MCP Server (AI Agent Integration)
PageBolt includes a built-in Model Context Protocol (MCP) server that lets AI coding assistants call the PageBolt API directly. Say "take a screenshot of example.com" in your IDE and get the result inline.
Available Tools
| Tool | Description |
|---|---|
| take_screenshot | Capture a screenshot of a URL, HTML, or Markdown. Returns an inline image. Supports all 50+ screenshot parameters. |
| generate_pdf | Generate a PDF from a URL or HTML. Saves to disk and returns the file path. |
| create_og_image | Create an OG/social card image from templates or custom HTML. Returns an inline image. |
| run_sequence | Execute a multi-step browser automation sequence with multiple outputs. |
| record_video | Record a demo video of browser automation with cursor effects, click highlighting, step notes, and Audio Guide narration. Saves to disk. Costs 3 requests. NEW |
| inspect_page | Inspect a page and get a structured map of interactive elements with CSS selectors. Use before run_sequence. |
| list_devices | List all available device presets for viewport emulation. |
| check_usage | Check your current API usage and plan limits. |
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
| PAGEBOLT_API_KEY | Yes | — | Your PageBolt API key (from the Dashboard) |
| PAGEBOLT_BASE_URL | No | https://pagebolt.dev | PageBolt API URL. Override for self-hosted instances. |
Example 11: Claude Desktop / Cursor config
Add this to your Claude Desktop config (~/.claude/claude_desktop_config.json) or Cursor MCP config (.cursor/mcp.json):
{
"mcpServers": {
"pagebolt": {
"command": "npx",
"args": ["-y", "pagebolt-mcp"],
"env": {
"PAGEBOLT_API_KEY": "pf_live_your_key_here"
}
}
}
}
Example Prompts
Once configured, you can ask your AI agent things like:
CI / CD Integration
NEWAutomatically generate polished demo videos from pull requests. An AI agent reads your PR diff, inspects the deployed preview, and records a video showing the feature in action — then posts it as a PR comment. No config files or scripts required.
Requirements
| Requirement | Details |
|---|---|
| PageBolt API key | Get one free at pagebolt.dev/dashboard |
| AI provider API key | Anthropic (Claude) or OpenAI (GPT-4o). The agent uses tool-calling to inspect and record. |
| Preview deployment | A publicly accessible URL for the PR (e.g. Vercel preview, Netlify deploy preview, or any staging URL). |
GitHub Action
The simplest way to add demo videos to your PRs. Add this workflow file to your repository:
Basic Setup (Vercel)
name: PageBolt Demo Video
on:
pull_request:
types: [opened, synchronize]
jobs:
demo-video:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Wait for Vercel preview
uses: patrickedqvist/wait-for-vercel-preview@v1.3.2
id: preview
with:
token: ${{ secrets.GITHUB_TOKEN }}
max_timeout: 300
- name: Record demo video
uses: Custodia-Admin/pagebolt/ci/action@main
with:
api-key: ${{ secrets.PAGEBOLT_API_KEY }}
ai-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
preview-url: ${{ steps.preview.outputs.url }}
Action Inputs
| Input | Required | Default | Description |
|---|---|---|---|
| api-key | Yes | — | Your PageBolt API key |
| ai-api-key | Yes | — | Anthropic or OpenAI API key for the AI agent |
| preview-url | Yes | — | Preview deployment URL for this PR |
| ai-provider | No | anthropic | AI provider: anthropic or openai |
| ai-model | No | auto | Override the AI model (e.g. claude-sonnet-4-20250514) |
| spec-dir | No | .pagebolt/demos | Path to YAML demo spec files (optional, for non-agent mode) |
| output-dir | No | ./pagebolt-artifacts | Directory to save video artifacts |
| github-token | No | github.token | GitHub token for posting PR comments |
| verbose | No | false | Enable verbose logging |
Action Outputs
| Output | Description |
|---|---|
| results-json | JSON string of all recording results |
| artifact-dir | Path to directory containing video artifacts |
| videos-recorded | Number of videos successfully recorded |
Netlify Example
name: PageBolt Demo Video
on:
deployment_status:
jobs:
demo-video:
if: github.event.deployment_status.state == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Record demo video
uses: Custodia-Admin/pagebolt/ci/action@main
with:
api-key: ${{ secrets.PAGEBOLT_API_KEY }}
ai-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
preview-url: ${{ github.event.deployment_status.target_url }}
CLI Reference
Use pagebolt-ci directly for local testing or non-GitHub CI systems.
Installation
npm install -g pagebolt-ci
# Or use npx without installing:
npx pagebolt-ci run --api-key pf_live_... --preview-url https://preview.example.com
Commands
pagebolt-ci run
Record demo videos. Automatically chooses between spec-based mode (if .pagebolt/demos/*.yml files exist) and autonomous agent mode.
| Flag | Description |
|---|---|
| --api-key | PageBolt API key (or set PAGEBOLT_API_KEY) |
| --preview-url | URL of the preview deployment to record |
| --ai-provider | anthropic or openai |
| --ai-api-key | API key for the AI provider |
| --ai-model | Override AI model (e.g. claude-sonnet-4-20250514) |
| --pr-title | PR title (passed to the agent for context) |
| --pr-description | PR description / body (helps the agent understand the feature) |
| --output-dir | Directory for video output (default: ./pagebolt-artifacts) |
| --base-url | PageBolt API base URL (default: https://pagebolt.dev) |
| --verbose | Enable verbose logging |
Example: Agent Mode
pagebolt-ci run \
--api-key pf_live_your_key \
--ai-provider anthropic \
--ai-api-key sk-ant-your_key \
--preview-url https://preview-abc123.vercel.app \
--pr-title "feat: Add user onboarding flow" \
--output-dir ./demo-videos \
--verbose
pagebolt-ci comment
Post or update a PR comment with recording results. Usually called automatically by the GitHub Action after run completes.
| Flag | Description |
|---|---|
| --github-token | GitHub token with PR comment permissions |
| --repo | Repository in owner/repo format |
| --pr-number | PR number to comment on |
| --results-file | Path to _results.json from the run command |
Agent Mode (Zero-Config)
When no spec files are found in .pagebolt/demos/, the CLI automatically enters Agent Mode. An AI agent (Claude or GPT-4o) autonomously:
- Reads the PR title, description, and diff to understand the feature
- Calls
inspect_pageon the preview URL to discover interactive elements - Plans a demo flow — which buttons to click, which inputs to fill, what sequence tells the best story
- Calls
record_videowith the planned steps, producing a polished MP4
inspect_page (discover page elements) and record_video (record a demo). It uses up to 15 turns of reasoning to plan and execute the recording.
Video Settings
Agent-recorded videos automatically include polished defaults:
| Setting | Default |
|---|---|
| Cursor | Classic pointer, always visible, smooth movement |
| Frame | macOS dark window chrome |
| Background | Gradient with 120px padding and 40px border radius |
| Click effects | Ripple effect on every click |
| Pace | Normal (1x) |
| Step notes | Tooltip annotations on every meaningful step |
| Blocking | Ads, banners, chat widgets, and trackers blocked |
Supported AI Providers
| Provider | Recommended Model | Notes |
|---|---|---|
| anthropic | claude-sonnet-4-20250514 | Best results. Excellent tool-calling and planning. |
| openai | gpt-4o | Good alternative. Supports function calling. |
Spec-Based Mode (Optional)
For deterministic recordings (no AI), create YAML spec files in .pagebolt/demos/:
name: Onboarding Flow
description: Demo of the new user onboarding experience
triggers:
paths:
- src/components/onboarding/**
- src/pages/welcome.*
viewport:
width: 1280
height: 720
steps:
- action: navigate
url: "{{preview_url}}/welcome"
note: Opening the welcome page
- action: wait
ms: 1500
- action: click
selector: "#get-started-btn"
note: Click Get Started
- action: fill
selector: "#company-name"
value: Acme Corp
note: Enter company name
- action: click
selector: "#continue-btn"
note: Continue to next step
When spec files exist and match changed files in the PR, spec-based mode takes priority over agent mode. Use {{preview_url}} as a variable placeholder that resolves to the PR's preview deployment URL.
Node.js / TypeScript SDK
Official typed client for Node.js 18+. Full TypeScript support with autocomplete for every parameter.
Installation
npm install pagebolt
Quick Start
import { PageBolt } from 'pagebolt';
const pb = new PageBolt({ apiKey: process.env.PAGEBOLT_KEY });
// Screenshot
const img = await pb.screenshot({
url: 'https://example.com',
darkMode: true,
format: 'webp',
});
fs.writeFileSync('shot.webp', img);
// Video with Audio Guide
const video = await pb.video({
steps: [
{ action: 'navigate', url: 'https://example.com' },
{ action: 'click', selector: '#cta' },
],
audioGuide: { enabled: true, voice: 'ava' },
});
Error Handling
import { PageBolt, RateLimitError, QuotaExceededError } from 'pagebolt';
try {
await pb.screenshot({ url });
} catch (e) {
if (e instanceof RateLimitError) {
console.log(`Retry in ${e.retryAfterMs}ms`);
} else if (e instanceof QuotaExceededError) {
console.log('Monthly quota reached');
}
}
The SDK includes built-in retry logic for transient errors (429, 5xx) with exponential backoff. See the full API reference on GitHub or npm.
Python SDK
Official Python client with Pydantic models, sync & async support. Requires Python 3.9+.
Installation
pip install pagebolt
Quick Start (Sync)
from pagebolt import PageBolt, ScreenshotOptions
pb = PageBolt(api_key="pb_live_...")
# Take a screenshot with typed options
img = pb.screenshot(ScreenshotOptions(
url="https://example.com",
dark_mode=True,
format="webp",
block_banners=True,
))
Path("shot.webp").write_bytes(img)
Async Support
from pagebolt import AsyncPageBolt
async def main():
pb = AsyncPageBolt(api_key="pb_live_...")
img = await pb.screenshot({ "url": "https://example.com" })
Snake_case parameters are auto-converted to camelCase for the API. See the full docs on GitHub or PyPI.
FAQ
Common questions about PageBolt's API behaviour.
Can I capture a local dev server (localhost)?
Not directly. Puppeteer runs on PageBolt's servers — not on your machine — so URLs like
http://localhost:3000 or
http://127.0.0.1:8080 resolve to
our server's loopback, not yours. Private IP ranges are also blocked as an SSRF safeguard.
The fix is to expose your local server with a tunnel, then pass the public URL to PageBolt:
# ngrok (most common)
ngrok http 3000
# → Forwarding: https://abc123.ngrok-free.app → localhost:3000
# Cloudflare Tunnel (no account required for quick tests)
cloudflared tunnel --url http://localhost:3000
# → https://random-name.trycloudflare.com
Then use the public tunnel URL in your API call:
{
"url": "https://abc123.ngrok-free.app",
"fullPage": true
}
This works for all endpoints — /screenshot,
/sequence,
/video, and
/pdf.
ngrok's free tier is sufficient for development and CI use; Cloudflare Tunnel requires no account at all.
Questions? Email support@pagebolt.dev
© 2026 PageBolt. All rights reserved.