Read any page with its JavaScript run: Markdown, screenshots, metadata, console errors, Lighthouse.
dev.urlpipe/mcp MCP Server
The dev.urlpipe/mcp Model Context Protocol (MCP) server reads any web page and runs its JavaScript. It captures page content and related diagnostics, including Markdown, screenshots, metadata, console errors, and Lighthouse results.
π οΈ Key Features
Read any page
Execute JavaScript on the page
Extract Markdown
Capture screenshots
Retrieve page metadata
Collect console errors
Run Lighthouse
π Use Cases
Audit rendered content that depends on JavaScript
Document visual output via screenshots
Debug runtime issues using console errors
Assess performance and quality via Lighthouse
Validate or catalog page metadata alongside content
β‘ Developer Benefits
Combine multiple artifacts (Markdown, screenshots, metadata) from the same page
Capture runtime signals (console errors) during page processing
Obtain Lighthouse outputs for performance review
β οΈ Limitations
Scope is limited to βread any pageβ workflows that require JavaScript execution; no other capabilities are described.
The projects this token can reach, each with how many requests it has
made in the last 30 days and where its results are delivered.
Start here: every other tool takes a project_id from this list. A project
is the unit a request is billed and recorded against β credits themselves
belong to the organization, so which project you pick does not change
what a call costs.
What the organization's plan allows, how much of it is left this period,
and what each operation costs.
Worth reading before a run of expensive calls: the operations differ by
seventeen times in price, so the difference between fetch_markdown and
summarize_page over a hundred pages is 100 credits against 1,700. It is
also how to read a refusal β a call that comes back with quota_exceeded
is telling you this number ran out.
Exposes POST /markdown. Renders the page in headless Chrome and converts
its main content to clean Markdown β headings, lists, links and code
kept, navigation, sidebars and cookie banners dropped.
Start here when you want to READ a page. It is the cheapest operation we
sell (1 credit) and by far the most compact thing to put in front of a
model: fetch_html returns the whole DOM, which is usually many times
larger and says nothing extra about what the page means.
Parameters9
project_id
integer
required
Which project this request belongs to. From list_projects.
url
string
required
The page to fetch. Must be a public http(s) URL.
sync
boolean
optional
Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result.
max_age
string
optional
How fresh a stored result must be to be reused, e.g. "2 hours" or "3 days". Default 7 days, maximum 30. A reused result is free. Pass "0" to force a fresh fetch.
residential
boolean
optional
Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic.
report_to
string
optional
Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one.
labels
object
optional
Your own keys to find this request by later, e.g. {"client": "acme"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters.
idempotency_key
string
optional
Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age "0". Up to 255 printable ASCII characters; a UUID is ideal.
page_options
object
optional
What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "Which project this request belongs to. From list_projects."
},
"url": {
"type": "string",
"description": "The page to fetch. Must be a public http(s) URL."
},
"sync": {
"type": "boolean",
"description": "Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result."
},
"max_age": {
"type": "string",
"description": "How fresh a stored result must be to be reused, e.g. \"2 hours\" or \"3 days\". Default 7 days, maximum 30. A reused result is free. Pass \"0\" to force a fresh fetch."
},
"residential": {
"type": "boolean",
"description": "Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic."
},
"report_to": {
"type": "string",
"description": "Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Your own keys to find this request by later, e.g. {\"client\": \"acme\"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters."
},
"idempotency_key": {
"type": "string",
"description": "Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age \"0\". Up to 255 printable ASCII characters; a UUID is ideal."
},
"page_options": {
"type": "object",
"description": "What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.",
"properties": {
"wait_for_selector": {
"type": "string",
"description": "Wait (up to 10s) for an element matching this CSS selector before reading the page."
},
"delay": {
"type": "integer",
"description": "Milliseconds to wait after the page has settled, 0 to 10000."
},
"block_ads": {
"type": "boolean",
"description": "Block the major ad networks and remove their slots from the page."
},
"block_cookie_banners": {
"type": "boolean",
"description": "Remove cookie-consent banners from the page."
},
"remove_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to remove from the page, up to 50."
}
}
}
},
"required": [
"project_id",
"url"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
fetch_html
Exposes POST /html. Returns the page's HTML after JavaScript has run and
redirects have been followed β the DOM a real browser sees, not the empty
shell curl returns. 1 credit.
Use it when you need the markup itself: a specific attribute, a script
tag, a structured-data block. If you want to read the page's content,
fetch_markdown costs the same and returns a fraction of the text.
Parameters9
project_id
integer
required
Which project this request belongs to. From list_projects.
url
string
required
The page to fetch. Must be a public http(s) URL.
sync
boolean
optional
Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result.
max_age
string
optional
How fresh a stored result must be to be reused, e.g. "2 hours" or "3 days". Default 7 days, maximum 30. A reused result is free. Pass "0" to force a fresh fetch.
residential
boolean
optional
Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic.
report_to
string
optional
Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one.
labels
object
optional
Your own keys to find this request by later, e.g. {"client": "acme"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters.
idempotency_key
string
optional
Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age "0". Up to 255 printable ASCII characters; a UUID is ideal.
page_options
object
optional
What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "Which project this request belongs to. From list_projects."
},
"url": {
"type": "string",
"description": "The page to fetch. Must be a public http(s) URL."
},
"sync": {
"type": "boolean",
"description": "Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result."
},
"max_age": {
"type": "string",
"description": "How fresh a stored result must be to be reused, e.g. \"2 hours\" or \"3 days\". Default 7 days, maximum 30. A reused result is free. Pass \"0\" to force a fresh fetch."
},
"residential": {
"type": "boolean",
"description": "Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic."
},
"report_to": {
"type": "string",
"description": "Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Your own keys to find this request by later, e.g. {\"client\": \"acme\"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters."
},
"idempotency_key": {
"type": "string",
"description": "Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age \"0\". Up to 255 printable ASCII characters; a UUID is ideal."
},
"page_options": {
"type": "object",
"description": "What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.",
"properties": {
"wait_for_selector": {
"type": "string",
"description": "Wait (up to 10s) for an element matching this CSS selector before reading the page."
},
"delay": {
"type": "integer",
"description": "Milliseconds to wait after the page has settled, 0 to 10000."
},
"block_ads": {
"type": "boolean",
"description": "Block the major ad networks and remove their slots from the page."
},
"block_cookie_banners": {
"type": "boolean",
"description": "Remove cookie-consent banners from the page."
},
"remove_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to remove from the page, up to 50."
}
}
}
},
"required": [
"project_id",
"url"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
capture_screenshot
Exposes POST /screenshot. Captures the whole rendered page as a PNG β
JavaScript executed, web fonts and images included, exactly as a browser
would draw it. 1 credit, whichever options you use.
Use it to see a page rather than read it: layout, visual regressions,
link previews, or checking what an anti-bot page actually showed us.
screenshot_options size it (viewport, scale, one element, the fold
only), encode it (png, jpeg or webp) and restyle it (dark mode, hidden
elements, your own CSS); page_options wait for the page and take ads
and cookie banners out of it.
Parameters10
project_id
integer
required
Which project this request belongs to. From list_projects.
url
string
required
The page to fetch. Must be a public http(s) URL.
sync
boolean
optional
Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result.
max_age
string
optional
How fresh a stored result must be to be reused, e.g. "2 hours" or "3 days". Default 7 days, maximum 30. A reused result is free. Pass "0" to force a fresh fetch.
residential
boolean
optional
Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic.
report_to
string
optional
Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one.
labels
object
optional
Your own keys to find this request by later, e.g. {"client": "acme"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters.
idempotency_key
string
optional
Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age "0". Up to 255 printable ASCII characters; a UUID is ideal.
screenshot_options
object
optional
How to take the screenshot. Every key is optional; leave it out for a full-page PNG.
page_options
object
optional
What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "Which project this request belongs to. From list_projects."
},
"url": {
"type": "string",
"description": "The page to fetch. Must be a public http(s) URL."
},
"sync": {
"type": "boolean",
"description": "Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result."
},
"max_age": {
"type": "string",
"description": "How fresh a stored result must be to be reused, e.g. \"2 hours\" or \"3 days\". Default 7 days, maximum 30. A reused result is free. Pass \"0\" to force a fresh fetch."
},
"residential": {
"type": "boolean",
"description": "Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic."
},
"report_to": {
"type": "string",
"description": "Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Your own keys to find this request by later, e.g. {\"client\": \"acme\"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters."
},
"idempotency_key": {
"type": "string",
"description": "Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age \"0\". Up to 255 printable ASCII characters; a UUID is ideal."
},
"screenshot_options": {
"type": "object",
"description": "How to take the screenshot. Every key is optional; leave it out for a full-page PNG.",
"properties": {
"full_page": {
"type": "boolean",
"description": "Capture the whole page top to bottom (default true). false captures the viewport only."
},
"full_page_max_height": {
"type": "integer",
"description": "The tallest a full-page or element capture may be, in CSS pixels: 100 to 16384, the default."
},
"viewport_width": {
"type": "integer",
"description": "Browser viewport width in CSS pixels, 320 to 1920. Default 1350."
},
"viewport_height": {
"type": "integer",
"description": "Browser viewport height in CSS pixels, 240 to 1080. Default 797."
},
"device_scale_factor": {
"type": "integer",
"description": "Pixel density, 1 to 3: 2 gives a retina image twice the size. Default 1."
},
"format": {
"type": "string",
"enum": [
"png",
"jpeg",
"webp"
],
"description": "Image format. Default png; webp is usually the smallest."
},
"quality": {
"type": "integer",
"description": "jpeg and webp only, 1 to 100. Default 80."
},
"omit_background": {
"type": "boolean",
"description": "Transparent background where the page sets none. png and webp only."
},
"selector": {
"type": "string",
"description": "CSS selector of one element to capture instead of the page."
},
"dark_mode": {
"type": "boolean",
"description": "Render with prefers-color-scheme: dark."
},
"hide_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to hide before capturing, up to 50. They stay in the page's html; page_options.remove_selectors takes them out of it."
},
"styles": {
"type": "string",
"description": "CSS to inject into the page before capturing."
}
}
},
"page_options": {
"type": "object",
"description": "What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.",
"properties": {
"wait_for_selector": {
"type": "string",
"description": "Wait (up to 10s) for an element matching this CSS selector before reading the page."
},
"delay": {
"type": "integer",
"description": "Milliseconds to wait after the page has settled, 0 to 10000."
},
"block_ads": {
"type": "boolean",
"description": "Block the major ad networks and remove their slots from the page."
},
"block_cookie_banners": {
"type": "boolean",
"description": "Remove cookie-consent banners from the page."
},
"remove_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to remove from the page, up to 50."
}
}
}
},
"required": [
"project_id",
"url"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
capture_console_errors
Exposes POST /console. Loads the page in headless Chrome and returns
what it reported through console.error and console.warn during load, plus
uncaught exceptions and unhandled promise rejections (not console.log). 1 credit.
What you would open DevTools for: broken third-party scripts and
client-side errors you cannot reproduce locally.
Parameters9
project_id
integer
required
Which project this request belongs to. From list_projects.
url
string
required
The page to fetch. Must be a public http(s) URL.
sync
boolean
optional
Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result.
max_age
string
optional
How fresh a stored result must be to be reused, e.g. "2 hours" or "3 days". Default 7 days, maximum 30. A reused result is free. Pass "0" to force a fresh fetch.
residential
boolean
optional
Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic.
report_to
string
optional
Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one.
labels
object
optional
Your own keys to find this request by later, e.g. {"client": "acme"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters.
idempotency_key
string
optional
Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age "0". Up to 255 printable ASCII characters; a UUID is ideal.
page_options
object
optional
What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "Which project this request belongs to. From list_projects."
},
"url": {
"type": "string",
"description": "The page to fetch. Must be a public http(s) URL."
},
"sync": {
"type": "boolean",
"description": "Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result."
},
"max_age": {
"type": "string",
"description": "How fresh a stored result must be to be reused, e.g. \"2 hours\" or \"3 days\". Default 7 days, maximum 30. A reused result is free. Pass \"0\" to force a fresh fetch."
},
"residential": {
"type": "boolean",
"description": "Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic."
},
"report_to": {
"type": "string",
"description": "Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Your own keys to find this request by later, e.g. {\"client\": \"acme\"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters."
},
"idempotency_key": {
"type": "string",
"description": "Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age \"0\". Up to 255 printable ASCII characters; a UUID is ideal."
},
"page_options": {
"type": "object",
"description": "What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.",
"properties": {
"wait_for_selector": {
"type": "string",
"description": "Wait (up to 10s) for an element matching this CSS selector before reading the page."
},
"delay": {
"type": "integer",
"description": "Milliseconds to wait after the page has settled, 0 to 10000."
},
"block_ads": {
"type": "boolean",
"description": "Block the major ad networks and remove their slots from the page."
},
"block_cookie_banners": {
"type": "boolean",
"description": "Remove cookie-consent banners from the page."
},
"remove_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to remove from the page, up to 50."
}
}
}
},
"required": [
"project_id",
"url"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
run_lighthouse_audit
Exposes POST /lighthouse. Runs a real Google Lighthouse audit against the
live page: performance, accessibility, best-practices and SEO scores plus
Core Web Vitals. 2 credits β dearer than a page fetch because the audit
runs in its own limited lane, so it also takes longer than anything else
here.
It fetches the page itself rather than sharing a visit, so asking for it
inside scrape_url costs two page visits, not one.
Parameters10
project_id
integer
required
Which project this request belongs to. From list_projects.
url
string
required
The page to fetch. Must be a public http(s) URL.
sync
boolean
optional
Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result.
max_age
string
optional
How fresh a stored result must be to be reused, e.g. "2 hours" or "3 days". Default 7 days, maximum 30. A reused result is free. Pass "0" to force a fresh fetch.
residential
boolean
optional
Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic.
report_to
string
optional
Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one.
labels
object
optional
Your own keys to find this request by later, e.g. {"client": "acme"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters.
idempotency_key
string
optional
Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age "0". Up to 255 printable ASCII characters; a UUID is ideal.
device
string
optional
Which profile to audit under. Default mobile.
include_audits
boolean
optional
Include the full per-audit detail, not just scores and metrics. Much larger; default false.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "Which project this request belongs to. From list_projects."
},
"url": {
"type": "string",
"description": "The page to fetch. Must be a public http(s) URL."
},
"sync": {
"type": "boolean",
"description": "Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result."
},
"max_age": {
"type": "string",
"description": "How fresh a stored result must be to be reused, e.g. \"2 hours\" or \"3 days\". Default 7 days, maximum 30. A reused result is free. Pass \"0\" to force a fresh fetch."
},
"residential": {
"type": "boolean",
"description": "Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic."
},
"report_to": {
"type": "string",
"description": "Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Your own keys to find this request by later, e.g. {\"client\": \"acme\"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters."
},
"idempotency_key": {
"type": "string",
"description": "Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age \"0\". Up to 255 printable ASCII characters; a UUID is ideal."
},
"device": {
"type": "string",
"enum": [
"mobile",
"desktop"
],
"description": "Which profile to audit under. Default mobile."
},
"include_audits": {
"type": "boolean",
"description": "Include the full per-audit detail, not just scores and metrics. Much larger; default false."
}
},
"required": [
"project_id",
"url"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
extract_metadata
Exposes POST /meta. Reconciles Open Graph, Twitter card and standard page
metadata into one clean object: title, description, language, main image,
favicon, author, publication date and feed. Values injected by JavaScript
are included, because the page is rendered first. 5 credits.
This is the page's metadata, not a dump of its meta tags. If you only
need the title, it is also in the Markdown β and that costs 1 credit.
Parameters9
project_id
integer
required
Which project this request belongs to. From list_projects.
url
string
required
The page to fetch. Must be a public http(s) URL.
sync
boolean
optional
Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result.
max_age
string
optional
How fresh a stored result must be to be reused, e.g. "2 hours" or "3 days". Default 7 days, maximum 30. A reused result is free. Pass "0" to force a fresh fetch.
residential
boolean
optional
Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic.
report_to
string
optional
Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one.
labels
object
optional
Your own keys to find this request by later, e.g. {"client": "acme"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters.
idempotency_key
string
optional
Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age "0". Up to 255 printable ASCII characters; a UUID is ideal.
page_options
object
optional
What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "Which project this request belongs to. From list_projects."
},
"url": {
"type": "string",
"description": "The page to fetch. Must be a public http(s) URL."
},
"sync": {
"type": "boolean",
"description": "Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result."
},
"max_age": {
"type": "string",
"description": "How fresh a stored result must be to be reused, e.g. \"2 hours\" or \"3 days\". Default 7 days, maximum 30. A reused result is free. Pass \"0\" to force a fresh fetch."
},
"residential": {
"type": "boolean",
"description": "Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic."
},
"report_to": {
"type": "string",
"description": "Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Your own keys to find this request by later, e.g. {\"client\": \"acme\"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters."
},
"idempotency_key": {
"type": "string",
"description": "Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age \"0\". Up to 255 printable ASCII characters; a UUID is ideal."
},
"page_options": {
"type": "object",
"description": "What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.",
"properties": {
"wait_for_selector": {
"type": "string",
"description": "Wait (up to 10s) for an element matching this CSS selector before reading the page."
},
"delay": {
"type": "integer",
"description": "Milliseconds to wait after the page has settled, 0 to 10000."
},
"block_ads": {
"type": "boolean",
"description": "Block the major ad networks and remove their slots from the page."
},
"block_cookie_banners": {
"type": "boolean",
"description": "Remove cookie-consent banners from the page."
},
"remove_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to remove from the page, up to 50."
}
}
}
},
"required": [
"project_id",
"url"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
extract_keywords
Exposes POST /keywords. Returns the 5β15 terms and phrases that best
represent the page, ordered by relevance β ranked by a language model,
not by raw frequency. 15 credits.
As with summarize_page: if you are going to reason over the result
yourself, fetch_markdown costs 1 credit and gives you everything.
Parameters9
project_id
integer
required
Which project this request belongs to. From list_projects.
url
string
required
The page to fetch. Must be a public http(s) URL.
sync
boolean
optional
Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result.
max_age
string
optional
How fresh a stored result must be to be reused, e.g. "2 hours" or "3 days". Default 7 days, maximum 30. A reused result is free. Pass "0" to force a fresh fetch.
residential
boolean
optional
Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic.
report_to
string
optional
Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one.
labels
object
optional
Your own keys to find this request by later, e.g. {"client": "acme"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters.
idempotency_key
string
optional
Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age "0". Up to 255 printable ASCII characters; a UUID is ideal.
page_options
object
optional
What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "Which project this request belongs to. From list_projects."
},
"url": {
"type": "string",
"description": "The page to fetch. Must be a public http(s) URL."
},
"sync": {
"type": "boolean",
"description": "Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result."
},
"max_age": {
"type": "string",
"description": "How fresh a stored result must be to be reused, e.g. \"2 hours\" or \"3 days\". Default 7 days, maximum 30. A reused result is free. Pass \"0\" to force a fresh fetch."
},
"residential": {
"type": "boolean",
"description": "Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic."
},
"report_to": {
"type": "string",
"description": "Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Your own keys to find this request by later, e.g. {\"client\": \"acme\"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters."
},
"idempotency_key": {
"type": "string",
"description": "Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age \"0\". Up to 255 printable ASCII characters; a UUID is ideal."
},
"page_options": {
"type": "object",
"description": "What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.",
"properties": {
"wait_for_selector": {
"type": "string",
"description": "Wait (up to 10s) for an element matching this CSS selector before reading the page."
},
"delay": {
"type": "integer",
"description": "Milliseconds to wait after the page has settled, 0 to 10000."
},
"block_ads": {
"type": "boolean",
"description": "Block the major ad networks and remove their slots from the page."
},
"block_cookie_banners": {
"type": "boolean",
"description": "Remove cookie-consent banners from the page."
},
"remove_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to remove from the page, up to 50."
}
}
}
},
"required": [
"project_id",
"url"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
summarize_page
Exposes POST /summarize. Returns a concise Markdown summary of the page's
main content, with the navigation, ads and boilerplate left out.
The most expensive operation we sell, at 17 credits, because it runs a
language model over the page. If YOU are the model that will read it,
fetch_markdown gives you the whole page for 1 credit and you can
summarize it yourself β this tool is for when the summary is the artefact
being produced, not a step on the way to one.
Parameters9
project_id
integer
required
Which project this request belongs to. From list_projects.
url
string
required
The page to fetch. Must be a public http(s) URL.
sync
boolean
optional
Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result.
max_age
string
optional
How fresh a stored result must be to be reused, e.g. "2 hours" or "3 days". Default 7 days, maximum 30. A reused result is free. Pass "0" to force a fresh fetch.
residential
boolean
optional
Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic.
report_to
string
optional
Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one.
labels
object
optional
Your own keys to find this request by later, e.g. {"client": "acme"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters.
idempotency_key
string
optional
Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age "0". Up to 255 printable ASCII characters; a UUID is ideal.
page_options
object
optional
What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "Which project this request belongs to. From list_projects."
},
"url": {
"type": "string",
"description": "The page to fetch. Must be a public http(s) URL."
},
"sync": {
"type": "boolean",
"description": "Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result."
},
"max_age": {
"type": "string",
"description": "How fresh a stored result must be to be reused, e.g. \"2 hours\" or \"3 days\". Default 7 days, maximum 30. A reused result is free. Pass \"0\" to force a fresh fetch."
},
"residential": {
"type": "boolean",
"description": "Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic."
},
"report_to": {
"type": "string",
"description": "Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Your own keys to find this request by later, e.g. {\"client\": \"acme\"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters."
},
"idempotency_key": {
"type": "string",
"description": "Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age \"0\". Up to 255 printable ASCII characters; a UUID is ideal."
},
"page_options": {
"type": "object",
"description": "What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.",
"properties": {
"wait_for_selector": {
"type": "string",
"description": "Wait (up to 10s) for an element matching this CSS selector before reading the page."
},
"delay": {
"type": "integer",
"description": "Milliseconds to wait after the page has settled, 0 to 10000."
},
"block_ads": {
"type": "boolean",
"description": "Block the major ad networks and remove their slots from the page."
},
"block_cookie_banners": {
"type": "boolean",
"description": "Remove cookie-consent banners from the page."
},
"remove_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to remove from the page, up to 50."
}
}
}
},
"required": [
"project_id",
"url"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
scrape_url
Exposes POST /scrape. Runs any subset of the other operations in one
request, served from a single page visit where possible. Each operation
is billed and stored exactly as the individual call would be, so this is
not a discount β with one exception, and it is the reason to use it: a
residential exit is charged per page VISIT, so several results off one
visit pay the surcharge once.
Answers with one object keyed by operation, each entry carrying its own
success and result, so a partial failure still returns everything that
worked.
lighthouse does not share the visit β it runs its own audit on its own
engine β so including it means two page fetches, and two surcharges when
residential is on. So does a screenshot whose screenshot_options set
viewport_width, viewport_height, device_scale_factor, dark_mode or
block_ads: those change how the page loads, so the screenshot gets a
visit of its own.
Parameters13
project_id
integer
required
Which project this request belongs to. From list_projects.
url
string
required
The page to fetch. Must be a public http(s) URL.
sync
boolean
optional
Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result.
max_age
string
optional
How fresh a stored result must be to be reused, e.g. "2 hours" or "3 days". Default 7 days, maximum 30. A reused result is free. Pass "0" to force a fresh fetch.
residential
boolean
optional
Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic.
report_to
string
optional
Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one.
labels
object
optional
Your own keys to find this request by later, e.g. {"client": "acme"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters.
idempotency_key
string
optional
Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age "0". Up to 255 printable ASCII characters; a UUID is ideal.
operations
array
required
Which operations to run off this page.
device
string
optional
Lighthouse only, when it is among the operations. Default mobile.
include_audits
boolean
optional
Lighthouse only: include the full per-audit detail. Default false.
screenshot_options
object
optional
Screenshot only, when it is among the operations: how to take it, exactly as capture_screenshot takes it.
page_options
object
optional
What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "Which project this request belongs to. From list_projects."
},
"url": {
"type": "string",
"description": "The page to fetch. Must be a public http(s) URL."
},
"sync": {
"type": "boolean",
"description": "Wait for the result and return it (default false). When false the call returns a token immediately and you collect the result with get_result."
},
"max_age": {
"type": "string",
"description": "How fresh a stored result must be to be reused, e.g. \"2 hours\" or \"3 days\". Default 7 days, maximum 30. A reused result is free. Pass \"0\" to force a fresh fetch."
},
"residential": {
"type": "boolean",
"description": "Fetch the page from a residential (home ISP) exit instead of a datacentre one. Costs a surcharge per page visit β see get_usage. Use it for sites that block datacentre traffic."
},
"report_to": {
"type": "string",
"description": "Async only: a webhook URL to deliver the result to. Defaults to the project's configured endpoint, if it has one."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Your own keys to find this request by later, e.g. {\"client\": \"acme\"}: they come back with the result, in the webhook and in list_requests, which can filter by them. Up to 16 keys of up to 40 letters, digits, _ - or .; string values up to 256 characters."
},
"idempotency_key": {
"type": "string",
"description": "Makes the call safe to retry: sending it again with the same key within 24 hours returns the first call's token and result instead of starting (and charging for) new work, even with max_age \"0\". Up to 255 printable ASCII characters; a UUID is ideal."
},
"operations": {
"type": "array",
"items": {
"type": "string",
"enum": [
"html",
"screenshot",
"markdown",
"meta",
"summarize",
"keywords",
"lighthouse",
"console"
]
},
"description": "Which operations to run off this page."
},
"device": {
"type": "string",
"enum": [
"mobile",
"desktop"
],
"description": "Lighthouse only, when it is among the operations. Default mobile."
},
"include_audits": {
"type": "boolean",
"description": "Lighthouse only: include the full per-audit detail. Default false."
},
"screenshot_options": {
"type": "object",
"description": "Screenshot only, when it is among the operations: how to take it, exactly as capture_screenshot takes it.",
"properties": {
"full_page": {
"type": "boolean",
"description": "Capture the whole page top to bottom (default true). false captures the viewport only."
},
"full_page_max_height": {
"type": "integer",
"description": "The tallest a full-page or element capture may be, in CSS pixels: 100 to 16384, the default."
},
"viewport_width": {
"type": "integer",
"description": "Browser viewport width in CSS pixels, 320 to 1920. Default 1350."
},
"viewport_height": {
"type": "integer",
"description": "Browser viewport height in CSS pixels, 240 to 1080. Default 797."
},
"device_scale_factor": {
"type": "integer",
"description": "Pixel density, 1 to 3: 2 gives a retina image twice the size. Default 1."
},
"format": {
"type": "string",
"enum": [
"png",
"jpeg",
"webp"
],
"description": "Image format. Default png; webp is usually the smallest."
},
"quality": {
"type": "integer",
"description": "jpeg and webp only, 1 to 100. Default 80."
},
"omit_background": {
"type": "boolean",
"description": "Transparent background where the page sets none. png and webp only."
},
"selector": {
"type": "string",
"description": "CSS selector of one element to capture instead of the page."
},
"dark_mode": {
"type": "boolean",
"description": "Render with prefers-color-scheme: dark."
},
"hide_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to hide before capturing, up to 50. They stay in the page's html; page_options.remove_selectors takes them out of it."
},
"styles": {
"type": "string",
"description": "CSS to inject into the page before capturing."
}
}
},
"page_options": {
"type": "object",
"description": "What to do to the page before anything is read off it. Applies to the result itself: removed ads and banners are gone from html, markdown and summaries too.",
"properties": {
"wait_for_selector": {
"type": "string",
"description": "Wait (up to 10s) for an element matching this CSS selector before reading the page."
},
"delay": {
"type": "integer",
"description": "Milliseconds to wait after the page has settled, 0 to 10000."
},
"block_ads": {
"type": "boolean",
"description": "Block the major ad networks and remove their slots from the page."
},
"block_cookie_banners": {
"type": "boolean",
"description": "Remove cookie-consent banners from the page."
},
"remove_selectors": {
"type": "array",
"items": {
"type": "string"
},
"description": "CSS selectors of elements to remove from the page, up to 50."
}
}
}
},
"required": [
"project_id",
"url",
"operations"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
get_result
Exposes GET /result/:token. Returns the result of a past request, in the
same form the call that made it would have returned β Markdown as
Markdown, a structured operation as its object, a screenshot as an image,
a scrape as its combined object.
This is how an async call is collected: any tool called without
sync: true answers with a token, and this turns that token into the
result. It also re-reads a result somebody already paid for, which is
free β results are kept for 30 days.
A request that has not finished yet answers {"status": "processing"};
call again in a moment.
A project's recent requests, newest first, with each one's token.
Look here before fetching a page: if somebody already pulled it, passing
that token to get_result returns the same result for nothing. A /scrape
appears as one entry β its per-operation children are internal, and the
scrape's own token returns all of them.
Pass labels to see only the requests made with them β every key given
must match its value exactly.
Parameters6
project_id
integer
required
From list_projects.
url
string
optional
Only requests for this exact URL.
operation
string
optional
Only requests running this operation.
labels
object
optional
Only requests made with all of these labels, each value matched exactly.
limit
integer
optional
How many to return. Default 25, maximum 100.
offset
integer
optional
Skip this many, for paging.
Raw schema
{
"type": "object",
"properties": {
"project_id": {
"type": "integer",
"description": "From list_projects."
},
"url": {
"type": "string",
"description": "Only requests for this exact URL."
},
"operation": {
"type": "string",
"enum": [
"html",
"screenshot",
"markdown",
"meta",
"summarize",
"keywords",
"lighthouse",
"console",
"scrape"
],
"description": "Only requests running this operation."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Only requests made with all of these labels, each value matched exactly."
},
"limit": {
"type": "integer",
"description": "How many to return. Default 25, maximum 100."
},
"offset": {
"type": "integer",
"description": "Skip this many, for paging."
}
},
"required": [
"project_id"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
get_request
Everything about one past request except its result: what was asked for,
how long each stage took, whether it came from the store, and how the
webhook delivery went.
This is the metadata; get_result returns the page itself. Reach for this
one when a call did not do what you expected β it says whether the result
was reused, why an analysis failed, and whether the delivery to the
project's endpoint succeeded.
A plain fetch tool does an HTTP GET. On a site that builds itself in the browser β
React, Vue, most docs sites and dashboards β that returns an empty shell, and the agent
either says it can't read the page or guesses what was on it.
URLpipe loads every page in real Chrome first, then hands your agent what it asked
for: the page as Markdown, a screenshot it can look at, its metadata, its console
errors or a Lighthouse audit.
Hosted. Streamable HTTP at https://urlpipe.dev/mcp. Nothing to install or run.
One bearer token. Can be read-only, limited to one project, or set to expire.
14 tools. The nine API operations, plus results, request history, projects and usage.
Same credits as the HTTP API. 1,000 free every month, no card. Cached results cost nothing.
Processed in the EU. Fetching, rendering and storage happen in Europe.
This repository holds the server's registry entry (server.json) and
the setup for each client. The server itself runs at urlpipe.dev.
In the dashboard, open Organization β MCP access and create a token. Choose
Fetch pages and read results for an agent that works normally, or Read past
results only for one that must never spend credits.
Copy it β it is shown once.
Every client sends it as Authorization: Bearer YOUR_TOKEN.
Connect your client
Claude Code
bash
claude mcp add --transport http urlpipe https://urlpipe.dev/mcp \
--scope user \
--header "Authorization: Bearer YOUR_TOKEN"
Or share it with your team in .mcp.json, each person setting their own URLPIPE_TOKEN:
Where your organization has request headers in Customize β Connectors β Add custom
connector, add https://urlpipe.dev/mcp with No sign-in and an authorization
header of Bearer YOUR_TOKEN. Otherwise use the open-source
mcp-remote bridge in
claude_desktop_config.json (needs Node.js):
URLpipe is listed on Smithery,
Glama and
cursor.directory (as a Cursor plugin), and in
the official MCP Registry as dev.urlpipe/mcp. On Smithery, paste your token as it is β the server
accepts it with or without the Bearer prefix.
Step-by-step pages for each client, with a first prompt to try and troubleshooting,
are at urlpipe.dev/integrations.
Tools
Tool
What it returns
Credits
list_projects
The projects the token can reach. Start here: every fetching tool takes a project_id.
0
get_usage
What the plan allows, what is left this period, and what each operation costs.
0
fetch_markdown
The page's main content as clean Markdown, after its JavaScript ran. The one to use for reading.
1
fetch_html
The rendered DOM β what a browser sees, not the shell curl gets.
1
capture_screenshot
A full-page screenshot, returned as an image the model can look at. Viewport, device scale, dark mode, one element by selector, PNG/JPEG/WebP.
1
capture_console_errors
console.error and console.warn during load, uncaught exceptions and unhandled promise rejections.
1
run_lighthouse_audit
A real Lighthouse audit: performance, accessibility, best practices, SEO and Core Web Vitals.
2
extract_metadata
Open Graph, Twitter card and page metadata reconciled into one object.
5
extract_keywords
The 5β15 terms that best represent the page, ranked by a language model.
15
summarize_page
A short Markdown summary of the page's main content.
17
scrape_url
Several of the above from one page visit. Each operation is billed as usual.
sum
get_result
The result of an earlier request, by its token.
0
list_requests
A project's recent requests, newest first.
0
get_request
Everything about one past request except its result: timings, cache, webhook delivery.
0
Async is the default, as it is over HTTP: a fetching tool returns a token straight
away and get_result collects the result. Pass sync: true to wait for it instead.
Most agents work this out from the tool descriptions; saying "use sync: true" in the
prompt saves a round trip.
A repeated request is free. Results are kept for 30 days and reused while they are
fresher than max_age (7 days by default).
Prompts and resources
Clients that show MCP prompts (often as slash commands) get three ready-made ones:
Prompt
Arguments
What it does
read_page
url, question (optional)
Reads the page as Markdown and answers the question, or summarises the page.
audit_page
url, device (optional: mobile or desktop)
A health report: Lighthouse scores, Core Web Vitals and console errors.
compare_pages
url_a, url_b, focus (optional)
Two pages side by side: what each says and how each presents itself.
The resource urlpipe://pricing holds the plans and what each operation costs, so an
agent can check a price before it spends credits.
initialize, tools/list, prompts/list and resources/list answer without a token,
so you can see all of this straight from the server:
Using urlpipe with sync: true, read https://news.ycombinator.com as Markdown and list
the five top stories with their links. Then take a screenshot of the first story's page
and tell me what the page looks like.
What it doesn't do
URLpipe answers questions about one URL at a time. It doesn't crawl whole sites,
discover URLs, or reach localhost and private addresses β it fetches public pages
from its own servers. It follows each site's robots.txt by default.