Fetch a page in one call
The same work as the extract call, with the request held open until the page is done and the Markdown returned directly.
Last updated 14 September 2026 · Markdown
POST https://api.quorumtech.ch/kerf/v1/render
Fetch and convert a page in one call.
The same work as POST /kerf/v1/extract/url, with the request held open until the page is
done and the extraction returned directly rather than as a job. Simpler for a script that
has nothing else to do while it waits; a page that needs a browser can take tens of
seconds, and a client with its own timeout should prefer the job route.
Costs the same: a tenth of a credit a page.
The simplest call to write: one request, one answer, no job to follow. It suits a script that has nothing else to do while it waits, and a page you expect to be quick.
The cost is the timeout. A page that needs a browser can take tens of seconds, and a client with its own deadline will give up while the work carries on here. If you are fetching pages you have not seen before, prefer POST /kerf/v1/extract/url with a wait: it degrades to a job instead of to a dropped connection.
{
"url": "https://example.com/"
}
Request
The address, and the few choices that change what comes back. The defaults suit reading a page: the fetch picks its own path, links are kept, images are not, and the site's robots.txt is not consulted.
A JSON body with these fields:
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| url | string | yes | The page to fetch. http or https, and a public address. |
| wait | integer | no | Milliseconds to hold the request open for a page that finishes quickly, so a short fetch needs no second call. Past it you get a 202 with a job to collect. Ignored by /kerf/v1/render, which always waits. |
| mode | auto or fast or browser | no | auto fetches the page directly and falls back to a browser when what came back is a shell rather than the article. fast never opens a browser and fails instead. browser always opens one. The price is the same for all three. Default "auto". |
| furniture | drop or end | no | Navigation, cookie notices, share bars and footers: dropped, or kept and moved to the end of the Markdown where they do not interrupt the reading. Default "drop". |
| hidden | include or drop | no | Text the page holds but does not show yet: inactive tabs, closed accordions, collapsed panels. Included, it is written after the visible text under a <!-- folded --> comment, which is where a pricing page keeps its yearly prices. drop leaves it out. Default "include". |
| linksEnabled | boolean | no | Keep links as Markdown links. Off, the text stays and the addresses go. Default true. |
| imagesEnabled | boolean | no | Include every image with an address. Off, only images carrying a description are kept, as alt text. Default false. |
| followRobots | boolean | no | Consult the site's robots.txt before fetching, and refuse the page with robots_disallowed when it disallows it. Off by default because this fetches one page you named rather than crawling a site. Default false. |
| location | us or gb or de or ch or in or sg | no | Fetch the page from a country, so prices, currency and terms are the ones a visitor there sees. A region the fleet does not offer answers 400 location_unavailable. When the country cannot be reached the page is fetched from our own address and report.locationFallback says so; with strictLocation it answers 424 location_blocked instead, and nothing is charged. |
| strictLocation | boolean | no | With location: fail with location_blocked rather than fall back to our own address. Nothing is charged for a page that fails this way. Default false. |
| optimiseForRAG | boolean | no | Shape the Markdown for a reader that cuts it into chunks and retrieves by similarity, so a chunk holds whole facts: a name stays with its figures and a table row keeps its column names. Fewer headings and no Markdown tables come back, so leave it off when people or a table renderer read the result. How the shaping is done changes as the extractor does; depend on the outcome, not on the shape of a line. Default false. |
| humanizeEnabled | boolean | no | Render in a browser that moves its cursor the way a person does. Slower, rarely needed, and it implies mode: browser. Cannot be combined with location. Default false. |
Example request
curl -X POST "https://api.quorumtech.ch/kerf/v1/render" \
-H "Authorization: Bearer $QUORUM_API_KEY" \
-H "content-type: application/json" \
-d '{
"url": "https://example.com/"
}'
Responses
200
The extraction.
| Field | Type | Description |
| --- | --- | --- |
| url | string | The address asked for. |
| finalUrl | string | The address actually read, after any redirects. |
| status | integer | The HTTP status the site answered with. |
| title | string | May be null. |
| markdown | string | The page, in reading order. |
| report | ExtractionReport | What the fetch did, for logging and for deciding whether to trust a thin result. The fields below are stable; the report also carries diagnostics that change as the extractor does, so read it by name rather than by shape. |
| report.path | fast or browser | Whether the page was fetched directly or rendered in a browser. |
| report.words | integer | Words in the Markdown. A page that should be an article and is not is usually a wall rather than an empty page. |
| report.items | integer | How many blocks the page was read as, counting paragraphs, headings, list items and table rows. |
| report.tables | integer | Tables recovered, including ones drawn as boxes rather than marked up as tables. |
| report.images | integer | |
| report.links | integer | |
| report.uncertain | boolean | The layout did not settle into one reading order, so the Markdown may not read the way the page does. Browser path only. |
| report.interstitial | string | A wall the page put up, when it did: what kind, and whether waiting cleared it. Present only when one was met. |
| report.fastFallback | string | Why the direct read was not served, when the page went to a browser after one: client_rendered, a script_filled: reason, or the direct read's failure. |
| report.location | string | The region the page was asked from, when one was. |
| report.locationFallback | object | Present when a location could not be served and the page came from our own address instead. to is direct; reason is refused or thin. |
| report.locationFallback.to | string | |
| report.locationFallback.reason | string | |
| report.optimiseForRAG | boolean | Whether the Markdown was shaped for retrieval. |
| renderMs | integer | Milliseconds spent fetching and converting. |
| cached | boolean | Present and true when this came from a recent fetch of the same page rather than a new one. |
Errors
The body is { "error": { "code", "message" } }; the full catalogue with what to do about each code is on the Errors page.
| Status | Codes | When |
| --- | --- | --- |
| 400 | | The request does not match the schema. error.issues names each failing field. |
| 401 | | The key is missing, not recognised, or revoked. |
| 402 | payment_required, credits_exhausted, remaining, needed | The workspace has no active API subscription (payment_required), or is prepaid and has no credits left for this request (credits_exhausted, with remaining and needed). Manage billing and add credits at https://developer.quorumtech.ch/billing. |
| 403 | insufficient_scope, url_not_allowed, robots_disallowed | The key is not scoped to Kerf (insufficient_scope), the address is one this service does not fetch (url_not_allowed: a private or loopback address, or a redirect to one), or the site's robots.txt disallows the page and you asked for it to be followed (robots_disallowed). |
| 424 | site_refused, location_blocked, load_failed, timeout | The page could not be fetched, and the site is the reason rather than us: it refused to serve the page (site_refused, which is the site answering 401, 403, 407, 429 or 451, or a wall it would not lower), the country asked for with strictLocation could not serve it (location_blocked), or it would not load (load_failed, timeout). Nothing is charged for a request that answers this, and sending it again unchanged answers the same way. |
| 429 | rate_limited, busy | Over the key's requests-per-minute limit (rate_limited), or the fetching fleet's queue is full (busy). Both carry Retry-After; a busy clears in seconds. |
| 503 | no_edge_available | No part of the fetching fleet is answering (no_edge_available). Retry shortly. |
Cost
A tenth of a credit ($0.001) a page, the same as the job route, charged on the answer because the page is in it. A page the site refuses charges nothing.
Related articles
Watch an extraction
Server-sent events for one job: its state now, every change after it, and the finished page. Closes when the job does.
Read an extraction's progress
One frame of a job's state without its Markdown, for a progress bar that should not carry a page's text with every poll.
Collect an extraction
Poll a job that answered 202. Returns its state while it runs and the Markdown once it is done.
Fetch a page
Give Kerf an address and get the page as Markdown, in the order a person reads it, as a job you can wait for or collect.
Still need help?
Ask Quincy in the chat bubble below, or write to support@quorumtech.ch and we will help you directly.