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 12 September 2026

Fetch a page in one call

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 | drop or include | no | Text the page hides from a reader: dropped, or included. Including it picks up content behind tabs and accordions, and also the text of menus that were never meant to be read. Default "drop". | | 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. |

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. | | 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). | | 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. | | 502 | site_refused, load_failed | The page could not be fetched: the site refused to serve it (site_refused, which is the site answering 401, 403, 407, 429 or 451), or it would not load (load_failed). Nothing is charged for a request that answers this. | | 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.

Still need help?

Ask Quincy in the chat bubble below, or write to support@quorumtech.ch and we will help you directly.