Structured extraction
Send a JSON Schema. Get it back filled from the web, with a confidence on every field.
A model asked to fill a form will fill it. That is the failure mode: the invented field looks exactly like the sourced one, and by the time it reaches your database nobody can tell them apart.
Extraction is easy. Extraction you can trust is not.
Turning a page into JSON stopped being hard some time ago. What is still hard is knowing which values are real — and every downstream system you own treats a confident guess and a sourced fact as the same row.
The distinction that matters is between four outcomes that look alike in a dashboard: a filled field, a field that was searched for and not found, a field that exists and is genuinely empty, and a field the server never processed. Collapsing those four into "blank" is how a spreadsheet full of nulls turns into a decision nobody can defend.
- null means the extractor looked and found no evidence. It is a statement, not an absence.
- An empty string, [] or {} means the source itself was empty — which is a value.
- A path missing from fields means the server never took a position on it, and that is a bug worth reporting rather than a result worth storing.
How the guardrail works
Step 1
You choose the evidence
Send a query and one licensed search supplies the evidence blocks, or send up to ten urls and each one is read in parallel. A URL that fails does not sink the request: seven readable pages produce an extraction from seven blocks, not an error.
Step 2
The schema is checked before anything is spent
A json_schema that is not an object, is not serializable, exceeds 20,000 characters or nests more than eight levels deep is rejected with 400 invalid_json_schema before a single upstream call is made — so a malformed schema costs nothing.
Step 3
Unsupported values are nulled by the server
Every field the extractor returns must point at the evidence block it came from. Any value that does not is set to null before the response is built and before the receipt is signed. The model does not get to decide whether it was careful enough.
The call, in full
curl -X POST https://api.dataswap.io/v1/extract/schema \
-H "Authorization: Bearer $DATASWAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://example.com/pricing"],
"json_schema": {
"type": "object",
"properties": {
"plan_name": { "type": "string" },
"monthly_usd": { "type": "number" },
"free_trial": { "type": "boolean" }
}
}
}'Ten URLs maximum per call; pass query instead and the search finds the evidence for you.
{
"data": { "plan_name": "Standard", "monthly_usd": 99, "free_trial": null },
"fields": [
{ "path": "plan_name", "value": "Standard", "source_idx": 0, "confidence": 0.91 },
{ "path": "monthly_usd", "value": 99, "source_idx": 0, "confidence": 0.88 },
{ "path": "free_trial", "value": null, "source_idx": null, "confidence": 0 }
],
"credits_used": 3,
"request_id": "req_2b71c4de",
"provenance": { "source_class": "mixed", "inference": "eu-only", "payload_sha256": "..." }
}free_trial came back null because no evidence block supported a value — that is the guardrail, not a missing answer.
The calls, and what they cost
| Endpoint | What it does | Price |
|---|---|---|
POST /v1/extract/schema | Your JSON Schema, filled from a query or from up to ten URLs. | 3 credits quoted; model_tier: premium costs double($0.0060) |
POST /v1/extract/catalog | A storefront URL in, its products out as structured JSON. Runs as a job. | 1 credit per product extracted |
GET /v1/jobs/{id} | Collect the result of a long-running extraction. | no charge for the poll |
POST /v1/onpage/instant | One URL, parsed: title, headings, structure and on-page signals. | 2 credits($0.0040) |
The quote covers the synthesis; the reads that gather the evidence are part of the same settled cost, so a request over ten URLs is not the same price as one over a single page. A call that retrieves nothing readable fails and is refunded rather than returning a schema filled from nothing.
1 credit = $0.002 on every plan. Full plans and prepaid packs are on the pricing section.
What this does not do yet
Fields carry the index of the supporting evidence block, not its URL. When you pass urls, the index is the position in the list you sent, so the mapping is yours. When you pass query, you get the confidence and the guarantee but not a clickable source — we would rather write that here than have you find it in production.
A schema deeper than eight levels or larger than 20,000 characters is rejected. Deep nesting is a real limit of constrained decoding, not a quota we could lift by charging you more.
It is not a crawler. It reads the URLs you name; it does not follow links, discover pages or work through a sitemap.
It has no browser. Content that exists only after client-side rendering may not be readable.
Questions we get asked
- What stops the extractor from inventing a value?
- A server-side check runs before the response exists. Each returned field has to reference the evidence block that supports it; any field that does not is set to null, and the nulling happens before the provenance receipt is signed. This is code in the request path rather than a prompt instruction.
- What does confidence mean here?
- It is the extractor’s own score for that field, normalized and passed through untouched. It is a sorting aid for your review queue, not a probability we have calibrated against ground truth — and we will not dress it up as one.
- Can I send my own URLs instead of a query?
- Yes, up to ten per call. They are read in parallel and partial failure is tolerated: the pages that respond produce the extraction, and the ones that do not are simply absent from the evidence.
- What does model_tier: premium change?
- It runs a larger model for deep or ambiguous schemas and costs exactly double the standard quote. Everything else — the evidence guardrail, the shape of the response, the receipt — is identical.
- How is this different from POST /v1/context?
- Context returns passages for a model to read; Schema-Fill returns typed fields for a system to store. Same licensed sourcing, same EU-only inference, different output contract — pick by what consumes the result.
Keep reading
- Grounding for AI agentsWhen passages beat fields — the input for a model rather than a database.
- Dataswap vs FirecrawlCrawling versus licensed extraction, including the jobs Firecrawl does and we do not.
- Extraction referenceCatalog extraction, job polling and the async contract.
- BlogNotes on how this is built and measured.
Put the prism to work.
Create an account and get your API key — no card required. Start on the six live bands and the twelve inference tools; the eight new capabilities land on the same key as they ship.