Extraction schemas
The small language for describing what to take off a page, including the list type that keeps a row's fields together instead of returning parallel arrays.
Scalars
{ "title": "string", "price": "number", "in_stock": "boolean" }Types are string, number, boolean, string[], number[]. A bare type lets the structured-data tier find the field by name. To pin it down, give a selector:
{ "price": { "type": "number", "selector": ".price", "attribute": "content" } }Lists
A listing page is rows, not columns. Ask for it that way:
{
"listings": {
"type": "list",
"selector": ".product-card",
"limit": 100,
"item": {
"name": { "type": "string", "selector": "h2 a" },
"price": { "type": "number", "selector": ".price" },
"url": { "type": "string", "selector": "h2 a", "attribute": "href" }
}
}
}Why this exists. The alternative — one array per column — is correct exactly while every card carries every field. One card missing a price shortens that array alone, every later pair is off by one, and the result is not an error: it is a table of real names against real prices belonging to different products. Nothing downstream can detect it.
With list, a missing field is a null inside its own record and the rows either side stay aligned.
selector is required for the CSS tier and ignored by the structured-data tier, which finds Product nodes by type — including nested inside ItemList → itemListElement → item, which is how most commerce pages publish a category.
Numbers
Prices arrive as text. number handles currency symbols, spaces, and both separator conventions: ¥1,234,500, $1,299.00, 1.299,00 €, 9 800 円.
A separator that repeats is grouping, in every locale — no notation has two decimal points. This matters more than it sounds: an earlier version treated 1,234,500 as a European decimal and returned null, so every price over a million silently disappeared while smaller ones parsed fine.
The ladder
| Tier | What it does | Cost |
|---|---|---|
css | Reads selectors you supply | none |
structured_data | Reads JSON-LD and OpenGraph the site already publishes | none |
llm | Describes the page to a model | tokens |
Tiers run in the order you list them and results merge: a later tier fills only what its predecessors left empty. Pass ["css", "structured_data"] to guarantee no model is called.
An empty array counts as *not found*, not as a value — otherwise a stale selector returning no rows would stop the ladder and the structured data on the same page would never get a turn.
Last updated