Extract structured data with a JSON Schema
Pull typed data from one or many pages with a prompt or a JSON Schema, expand a path with /*, cap the pages fetched and re-run free on pages still in the cache.
Updated
POST /api/v1/extract reads one or more pages and returns one JSON result that follows your prompt, your JSON Schema or both. This guide shows the request, how pages are combined, the /* expansion, the page cap and why a second run over the same pages can cost nothing.
Make a request
Send urls and at least one of prompt or schema. Without both, the call is a 400.
curl -X POST https://api.wayfern.com/api/v1/extract \
-H "Authorization: Bearer $WAYFERN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://example.com/pricing", "https://example.com/plans/enterprise"],
"prompt": "every pricing tier with its monthly price in US dollars and included seats",
"schema": {
"type": "object",
"properties": {
"tiers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"monthlyUsd": { "type": "number" },
"seats": { "type": "integer" }
},
"required": ["name", "monthlyUsd"]
}
}
},
"required": ["tiers"]
}
}' | Field | Default | What it does |
|---|---|---|
urls | required | The pages to read. Duplicates are removed. |
prompt | none | What to extract, in plain words, up to 4,000 characters. |
schema | none | The JSON Schema the result must follow. |
maxPages | 10 | Most source pages to fetch, from 1 to 100. It also caps the bill. |
maxAge | 3,600,000 | Use a cached copy of a page up to this many milliseconds old. |
proxy, country and timeout also apply. The default timeout is 30 seconds per page.
Read the result
{
"success": true,
"data": {
"data": {
"tiers": [
{ "name": "Starter", "monthlyUsd": 0, "seats": 1 },
{ "name": "Growth", "monthlyUsd": 49, "seats": 5 }
]
},
"sources": [
{ "url": "https://example.com/pricing", "status": "fetched", "billed": true },
{ "url": "https://example.com/plans/enterprise", "status": "failed", "billed": false, "error": "not_found" }
]
},
"credits": 1
} data.data is the result. data.sources lists each page with its status: fetched, cached or failed. billed says whether the page cost a credit.
How pages are combined
Wayfern fetches up to 4 pages at a time. It turns each page into main-content Markdown, then sends all pages to the language model in one call. The model merges facts across the pages. When pages disagree, it prefers the most specific one. A field that no page states is null.
- Each page adds up to 10,000 tokens of Markdown. With more than 10 pages, each page gets a smaller share.
- The answer is capped at 800 tokens. Ask for compact output, and split a large job into several calls.
- When the language model is not available, the call fails with
503 llm_unavailableand costs nothing. - When no page loads, the call fails with the first page error and costs nothing.
Expand a path with /*
A URL that ends in /* expands to the URLs that map finds for it. For example, https://example.com/docs/* maps https://example.com/docs.
- The expansion stops at
maxPages, counted with the plain URLs in the list. - The map keeps every URL on the same host. It does not filter by the path, so URLs outside
/docs/can appear. Short paths come first. - To stay inside one path, call map yourself with
search, then send the URLs you keep. - The expansion is billed as a map call, at 2 credits per started 10 URLs. That charge is not in the
creditsof the extract response.
Re-run on cached pages
Extract bills 1 credit per uncached source page. A page served from the cache costs nothing. Failed pages cost nothing.
Each fetched page goes into the cache for 1 hour, unless it is larger than 2 MB. With the default maxAge of one hour, a second run over the same URLs within that hour reads the cached pages. You can change the prompt or the schema and pay nothing for the pages.
import os
import requests
auth = {"Authorization": f"Bearer {os.environ['WAYFERN_API_TOKEN']}"}
urls = ["https://example.com/pricing"]
for prompt in ["plan names only", "plan names with monthly price and seats"]:
res = requests.post(
"https://api.wayfern.com/api/v1/extract",
headers=auth,
json={"urls": urls, "prompt": prompt},
timeout=180,
)
res.raise_for_status()
body = res.json()
billed = sum(1 for s in body["data"]["sources"] if s["billed"])
print(body["credits"], "credits,", billed, "pages billed:", body["data"]["data"]) The first run costs 1 credit. The second run costs 0 when the page is still cached.
- Set
maxAgeto0to force a live fetch. A live fetch is billed. - The cache keeps a page for at most 1 hour, so a
maxAgeabove one hour has no further effect. - The cache entry is keyed on the URL,
proxyandcountry. A scrape with default options uses a different entry, so it does not fill the extract cache.
Before the call starts, Wayfern checks that your balance covers every page as uncached. The route allows 20 requests a minute. For one page, the scrape json format is another option at 1 + 4 credits. See Caching and freshness and the Extract reference.