HTML
Fetch the fully rendered HTML of any page. URLpipe loads the URL in headless Chrome, so JavaScript runs and client-rendered markup is included — not just the initial server response.
This is the cheapest operation we sell: it uses no AI, and costs 1 credit per call — the unit every other endpoint is priced against. See Credits.
POST/html
Get rendered HTML
Body parameters
- Name
url- Type
- string
- Required
- Required
- Description
- The absolute URL of the page to process. Rendered with headless Chrome, so JavaScript runs and redirects are followed. It must not include a username or password (
https://user:pass@example.com).
- Name
page_options- Type
- object
- Description
- Wait for the page, and remove ads, cookie banners or your own elements from it before it is read — gone from this result, not merely hidden. See Page options.
- Name
residential- Type
- boolean
- Description
- Fetch the page from a residential exit — an address on a home broadband line rather than one in a datacentre. Reach for it when a site serves you less than it serves a browser, or nothing at all. Defaults to
false. Adds 25 credits per page fetch on top of what the operation costs, and its results are kept separate from the ordinary ones.
- Name
report_to- Type
- string
- Description
- Webhook URL — an
httporhttpsaddress URLpipe POSTs the result to when it's ready. Optional: without it we deliver to your project's default endpoint if it has one, and otherwise send no webhook at all — the result still waits for you at GET /result/:token. A value we cannot deliver to returns422. Ignored on async=truerequest. Deliveries can be signed so your endpoint can verify they came from us.
- Name
sync- Type
- boolean
- Description
- Process the request synchronously, returning the result inline in the response. Defaults to
false(async: return a token now, and either receive the result at a webhook or fetch it with GET /result/:token). See Async & sync modes for the full contract.
- Name
max_age- Type
- string | integer
- Description
- How fresh a cached result must be to be accepted. Either an integer number of seconds (
3600) or a duration string of the form"<number> <unit>"— unitss/min/h/d/w(e.g."2 hours","3 days","30m"). Defaults to7 days, clamped to a max of30 days;0always bypasses the cache. See Caching for all accepted units.
- Name
labels- Type
- object
- Description
- Your own keys to find and account for this request by — a client, a project, a campaign:
{"client": "acme"}. Returned with the result, in the webhook and in theX-Labelsheader, and your dashboard filters history and totals credits by them. Up to 16 keys; string values. See Labels.
Response
Content type text/plain — the response body is the raw HTML document as a string.
POST/html
curl -X POST https://urlpipe.dev/html \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'import requests
res = requests.post(
"https://urlpipe.dev/html",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"url": "https://example.com"},
)const res = await fetch("https://urlpipe.dev/html", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://example.com" }),
})$ch = curl_init("https://urlpipe.dev/html");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer YOUR_API_KEY",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode(["url" => "https://example.com"]),
]);
$response = curl_exec($ch);Response
<!doctype html>
<html lang="en">
<head>
<title>Example Domain</title>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is for use in illustrative examples…</p>
</div>
</body>
</html>Need clean content instead of raw markup? /markdown strips navigation and boilerplate and returns the main content as Markdown.
Responses
Whatever the status, the response carries metadata headers: the result token, whether it was served from cache and how old that result is, how long we took, what it cost in credits, and the allowance you have left.
Status
When
Body
200 OKThe request succeeded.
Sync: the result, in this endpoint's format (see Response above). Async: a job token and the request's labels — { "token": "…", "status": "accepted", "labels": {} }.
422 Unprocessable EntityThe analysis failed, or a parameter was invalid (a bad max_age, a screenshot_options value out of range, labels that break the rules, or a report_to we will not deliver to).
{ "error": "<message>" }
429 Too Many RequestsThree causes, told apart by the error field: concurrency_limit (too many of your requests already running), rate_limited (sending too fast), or quota_exceeded (Free plan only — out of credits with no card on file to bill the extra to, and checked only on a cache miss; a paid plan keeps serving at the overage rate and is never refused for credits).
All three carry "error" and "message". Extra fields: concurrency_limit → "limit", "running" · rate_limited → "retry_after" · quota_exceeded → "limit", "used", "needed", "resets_at".
504 Gateway TimeoutSync only: the analysis didn't finish within 60s. It keeps running — fetch it via GET /result/:token.
{ "error": "processing_timeout", "token": "…" }
401 UnauthorizedMissing or invalid API key.
{ "error": "invalid_api_key", "message": "…" }
Try it live — no API key needed
Run this endpoint against any URL right in your browser.