Extraction API
Your schema in, JSON out, from any URL
No selectors to write and none to break when the site changes: say what each field means, and the page's data comes back in your shape.
By Roger Campos · Last updated: October 2026
TL;DR
POST a URL and a list of fields to /extract and get back an object with exactly those keys, filled from the page: strings, numbers, dates, URLs, emails, nested objects and lists of objects. The page is rendered in a real browser and read whole — text, links, tables and its JSON-LD — and any value it does not give comes back null, never guessed. 24 credits per extraction.
Free plan, no credit card. 1,000 credits a month.
Try it now — no signup
Your result will appear here.
Enter a URL above, or pick one of the example pages.
The request
One POST, one bearer token
curl -X POST https://urlpipe.dev/extract \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/products/trail-runner-3", "sync": true, "fields": [{"name":"name","type":"string"},{"name":"price","type":"number","description":"The current price, without the currency"},{"name":"images","type":"array","items":"url"}]}'import requests
res = requests.post(
"https://urlpipe.dev/extract",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"url": "https://example.com/products/trail-runner-3", "sync": True, "fields": [{"name": "name", "type": "string"}, {"name": "price", "type": "number", "description": "The current price, without the currency"}, {"name": "images", "type": "array", "items": "url"}]},
)const res = await fetch("https://urlpipe.dev/extract", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://example.com/products/trail-runner-3", sync: true, fields: [{"name":"name","type":"string"},{"name":"price","type":"number","description":"The current price, without the currency"},{"name":"images","type":"array","items":"url"}] }),
})$ch = curl_init("https://urlpipe.dev/extract");
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/products/trail-runner-3", "sync" => true, "fields" => [["name" => "name", "type" => "string"], ["name" => "price", "type" => "number", "description" => "The current price, without the currency"], ["name" => "images", "type" => "array", "items" => "url"]]]),
]);
$response = curl_exec($ch);{
"name": "Trail Runner 3",
"price": 129.95,
"images": [
"https://example.com/img/trail-runner-3-side.jpg",
"https://example.com/img/trail-runner-3-top.jpg"
]
}Fields
Describe the fields, not the page
Each field has a name (the key it comes back under), a type and, worth writing, a description — what a model reads to decide which value on the page you mean. Say it the way you would to a person: the sale price if there is one, otherwise the regular price. An optional instructions string applies to every field.
| Type | Comes back as |
|---|---|
| string | Text, as the page writes it |
| number, integer | A number without currency symbols, units or separators |
| boolean | true or false |
| date, datetime | ISO 8601 — 2026-10-08 |
| url | An absolute URL; relative links are resolved against the page |
| An email address | |
| object | A group of values with its own fields |
| array | Every match on the page, in page order — of one type, or of objects with fields |
Up to 50 fields in all, nested at most 3 levels deep. A schema that breaks the rules is refused with a message naming the field, before anything is fetched or spent. The /extract reference has every rule.
Arrays of objects
A list of things, each with its attributes
{
"url": "https://example.com/careers",
"fields": [
{ "name": "company", "type": "string" },
{ "name": "jobs", "type": "array", "description": "Every open position", "items": {
"type": "object",
"fields": [
{ "name": "title", "type": "string" },
{ "name": "location", "type": "string", "description": "City, or Remote" },
{ "name": "posted_on", "type": "date" },
{ "name": "apply_url", "type": "url" }
]
} }
]
}How it works
Null, not a guess
- The page is rendered in a real browser, so content added by JavaScript is there. A URL that serves a PDF or a text file is read from its text — see documents & files.
- The whole page is read, not only its article text: its text, links, tables, image alt text and its structured data — JSON-LD, microdata, meta tags — where prices, dates and identifiers often are.
- A language model with a short reasoning step fills your schema. It is told to leave a value null rather than guess, and its answer is held to your schema: every key there, nothing else, and a value that does not fit its type turned into null.
Use cases
What people build with it
- Price and plan monitoring. The same fields from competitors' pricing pages every week, with
max_agedeciding how fresh each run must be. - Lead and directory enrichment. Company name, contact email, social profiles and address from each site on a list.
- Listings into rows. Job boards, event pages, property listings and catalogues as arrays of objects, ready for a database.
- Documents. An invoice's total, a report's figures or a datasheet's specs from a PDF link, in the same fields you would ask of a page.
Pricing
What it costs
An extraction costs 24 credits, whatever the number of fields. The same URL with the same fields and instructions is answered for free while the stored result is fresher than max_age; change a field, its order or its description and it is a new extraction. A failed request costs nothing. When the standard metadata is all you need — title, image, authors, dates — the metadata API gives it for 1 credit and no model.
Limits
What it does not do
- One page per request. It does not follow pagination or crawl a site; send each page.
- No interaction. No clicks, logins or form fills before the page is read.
- Only what the page says. It does not look values up elsewhere or infer what the page leaves out — those come back null.
- Public pages only. Private and intranet addresses are refused.
Pricing
What it costs
| Call | Credits | Free (calls/mo) | Starter (calls/mo) | Pro (calls/mo) | Scale (calls/mo) |
|---|---|---|---|---|---|
| /extract | 24 credits | 41 | 833 | 2,291 | 7,291 |
Cache hits and failed requests cost nothing. Paid plans are never cut off: past the allowance, extra credits are $1.50 per 1,000 credits. See every plan.
FAQ
Frequently asked questions
No. You describe each field with a name, a type and a description, and the page is read for it. Nothing breaks when the site changes its markup.
It comes back null, and an empty list comes back as []. The model is told never to guess, and every answer is checked against your types.
Yes. An array field whose items are objects returns every match on the page, in page order, each with its own fields.
Yes. Pages are rendered in a real browser before they are read, and a link to a PDF or text file is read from its text.
24 credits per extraction, whatever the number of fields. A repeat of the same request within its freshness window, and a failed request, cost nothing.
Make your first request in five minutes.
Free plan, no card. Confirm your email and your API key is live — you'll be making real requests in minutes.