Reference
The Document.
Every acquisition produces this same shape: a scrape, a crawled page, an ingested file. One output contract, so code that handles a scrape handles a crawl result too.
Fields
| Field | Type | Detail |
|---|---|---|
| url | string | The URL you asked for, canonicalized. |
| final_url | string | Where it ended up after redirects. |
| fetched_at | string | ISO 8601, UTC. |
| status | integer | HTTP status of the final response. |
| content_type | string | Normalized, without parameters. |
| depth | integer | Link distance from the seed. Always 0 for a scrape. |
| title | string | Combined from JSON-LD, OpenGraph, meta, and the document itself. |
| author | string | Same precedence rules. |
| published_date | string | Same precedence rules. |
| language | string | As declared, or detected from the document. |
| description | string | Meta description or OpenGraph equivalent. |
| site_name | string | OpenGraph site name where present. |
| keywords | string[] | As declared by the page. |
| markdown | string | The primary body representation. |
| text | string | Plain text, derived from the same extraction. |
| html | string | Raw HTML. Empty unless include_html was set. |
| links | Link[] | url, text, internal, nofollow. |
| images | Image[] | url, alt, title. |
| tables | Table[] | caption, headers, rows. |
| records | object[] | Repeated structures, product cards, search results, feed entries. |
| metadata | object | Anything that does not fit a fixed field, such as json_ld. |
| trace | Trace | stages[], cache_hit, rendered, redirects[], warnings[]. |
Precedence
When sources disagree about the title, author or date, the merge is deterministic rather than a judgement call: JSON-LD first, then OpenGraph, then standard meta tags, then the document itself. No model decides what the “real” title is, so the same page always produces the same answer.
Anything without a fixed field lands in metadata, json_ld payloads, feed entry data, and similar, rather than being dropped.
markdown, text, html
markdown is the primary representation and what most pipelines want. text is the same extraction without the formatting. html is the raw body and is empty unless you ask for it. It is by far the largest field, and the key is always present so the shape never changes.
records
Repeated structures found on the page, product cards, search results, feed entries. A listing page yields records; an article usually yields none. This is the field to check when you want the page's data rather than its prose.
A full response
Real output, captured against the project's own test fixture:
{
"url": "https://example.com/article",
"final_url": "https://example.com/article",
"fetched_at": "2026-07-30T11:43:46.878250Z",
"status": 200,
"content_type": "text/html",
"depth": 0,
"title": "The Quiet Rise of Underwater Farming",
"author": "Nadia Okafor",
"published_date": "2025-11-03",
"language": "en",
"description": "How submerged greenhouses are reshaping coastal agriculture.",
"keywords": ["aquaculture", "farming", "ocean"],
"site_name": "Ocean Journal",
"markdown": "# The Quiet Rise of Underwater Farming\n\nTwenty meters beneath …",
"text": "The Quiet Rise of Underwater Farming\nTwenty meters beneath the Li …",
"html": "",
"links": [
{ "url": "https://example.com/", "text": "Home", "internal": true, "nofollow": false }
],
"images": [
{ "url": "https://example.com/images/biosphere.jpg",
"alt": "A submerged biosphere with basil plants", "title": "" }
],
"tables": [
{ "caption": "Yield comparison",
"headers": ["Crop", "Land yield", "Biosphere yield"],
"rows": [["Basil", "1.0x", "1.4x"], ["Strawberry", "1.0x", "1.1x"]] }
],
"records": [],
"metadata": {
"json_ld": [
{ "@context": "https://schema.org", "@type": "NewsArticle",
"headline": "The Quiet Rise of Underwater Farming",
"author": { "@type": "Person", "name": "Nadia Okafor" },
"datePublished": "2025-11-03", "inLanguage": "en" }
]
},
"trace": {
"stages": [
{ "name": "fetch", "elapsed_ms": 22.2 },
{ "name": "parse", "elapsed_ms": 1.3 },
{ "name": "content", "elapsed_ms": 185.8 },
{ "name": "metadata", "elapsed_ms": 10.0 },
{ "name": "links", "elapsed_ms": 0.1 },
{ "name": "media", "elapsed_ms": 0.0 },
{ "name": "records", "elapsed_ms": 0.1 },
{ "name": "combine", "elapsed_ms": 0.0 }
],
"cache_hit": false,
"rendered": false,
"redirects": [],
"warnings": []
}
}The trace at the end gets its own page.