Quick answer
What does the WebReaderAPI read response contain?
A WebReaderAPI /read response holds the page content in markdown (or text or html), its metadata (title, description, author, published_at, language, site_name, word_count), and the facts of the fetch: final_url, status_code, rendered and credits_used. A plain page uses 1 credit, from $0.07/1K pages as of October 2026. No KYC; pay in crypto.
Which fields describe the fetch?
These fields are always present in a successful response.
| Field | Type | Meaning |
|---|---|---|
url | string | The address you sent |
final_url | string | The address actually read, after redirects (5 at most) |
status_code | integer | The HTTP status the site returned for the final address, normally 200 |
fetched_at | string | Time of the fetch, ISO 8601 in UTC |
rendered | boolean | true when a headless browser built the page |
credits_used | integer | Credits billed for this page: 1, 2 with a browser, plus 10 with country; PDFs 1 per started 10 pages |
Which fields describe the page?
Metadata comes from the page itself: its title tag, meta tags, structured data and visible byline. A field the page does not provide is absent, so test for it before use.
| Field | Type | Meaning |
|---|---|---|
title | string | The page or article title |
description | string | The summary the page gives of itself |
language | string | Language code of the content, such as en or de |
author | string | The author named on the page |
published_at | string | Publication date stated by the page |
site_name | string | The site's own name |
word_count | integer | Words in the returned content, after extraction and before max_chars |
Where is the content itself?
One content field is returned, named after the format you chose.
| Field | When | Content |
|---|---|---|
markdown | format=markdown (default) | Headings, paragraphs, lists, tables and links in Markdown; images too with include_images=true |
text | format=text | Plain text, paragraphs separated by blank lines |
html | format=html | Cleaned HTML of the main content, without scripts, styles or ads |
links | include_links=true | List of {url, text} for the links found on the page, absolute URLs |
{
"url": "https://example.com/guides/compost",
"final_url": "https://example.com/guides/compost-at-home",
"status_code": 200,
"fetched_at": "2026-10-22T10:02:44Z",
"rendered": false,
"credits_used": 1,
"title": "Compost at home: a beginner's guide",
"description": "What to put in a compost bin, and what to leave out.",
"language": "en",
"author": "Sam Rivera",
"published_at": "2026-09-14",
"site_name": "Example Garden",
"markdown": "# Compost at home: a beginner's guide\n\nA compost bin turns kitchen scraps into soil...\n\n## What goes in\n\n- Fruit and vegetable peels\n- Coffee grounds\n\nSee also [Choosing a bin](https://example.com/guides/bins).",
"word_count": 1184,
"links": [
{ "url": "https://example.com/guides/bins", "text": "Choosing a bin" }
]
}How do I tell a rendered page from a plain one?
Look at rendered. With render=auto, the API first reads the raw HTML; it starts a browser only when that HTML holds too little text, and then sets rendered to true and credits_used to 2. If a site you read often always comes back rendered, send render=true from the start: the cost is the same and you skip a wasted first step.
If a page comes back unrendered but thin, compare word_count with what you see in a browser. A low count usually means the content needs JavaScript; retry once with render=true.
Why can final_url differ from url?
Because sites redirect. A short link, an old address, a missing trailing slash or a language switch all send you elsewhere. The API follows up to 5 redirects and reports the address it finally read in final_url.
Store final_url as the canonical source of a document in a RAG index. Two different url values that end at the same final_url are the same page, so you can drop the duplicate before embedding it.
What does a job result look like?
GET https://webreaderapi.com/api/v1/jobs/{job_id} returns job_id, status (queued, running or done), results, next_cursor and the job's running credits_used. Each item of results is one of two shapes:
- a full
/readresponse, exactly as described above, for a page that was read; {url, error}for a page that was not, whereerrorhas the samecodeandmessageas an API error.
next_cursor is present while more results are ready; send it back as cursor to get them. The batch and crawl page has a complete polling loop.
{
"job_id": "job_5c1e8a90d2",
"status": "done",
"results": [
{ "url": "https://example.com/blog/post-1", "final_url": "https://example.com/blog/post-1", "status_code": 200, "rendered": false, "credits_used": 1, "title": "Post one", "markdown": "# Post one\n\n...", "word_count": 802 },
{ "url": "https://example.org/members/area", "error": { "code": "target_refused", "message": "The site refused the request.", "target_status": 403 } }
],
"credits_used": 1
}What does an error response look like?
An error comes back with an HTTP status of 400 or more and a JSON body {"error": {"code", "message"}}. When the site itself refused the request, target_status gives the status the site sent. No error is billed. The errors page lists every code with its cause and whether a retry can help.
Response format: common questions
Is the Markdown cut when I set max_chars?
Yes. The content field stops at that many characters. word_count still describes the whole extracted content, so you can see how much was left out.
Why is author or published_at missing?
Because the page does not state it in a way that can be read. Many pages give no author or date. Absent fields are omitted rather than returned empty.
Are the links in the Markdown absolute?
Links in the links list are absolute. Write your code to accept both forms in the Markdown body, and resolve relative ones against final_url.
Can I get the raw HTML of the whole page?
The closest is format=html with main_content=false, which returns the whole page cleaned of scripts and styles. The original byte-for-byte HTML is not part of v1.