LYRENTH
AgentsDocsPricingBenchmarksIndex statsAboutBlogFor site ownersStatusContact
/docs / api
Reference

API reference

Six endpoints, one credential, one document shape. This page lists every call an API key can make, with its request fields, its response, and the errors worth branching on.

Getting in

Base URL and auth

Everything is under https://api.lyrenth.com, over HTTPS, with the key in the Authorization header.

curl -X POST https://api.lyrenth.com/v1/aidocument \
  -H "Authorization: Bearer aiwk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/post"}'

Keys start with aiwk_. A key can call the six endpoints on this page and nothing else: key management, billing, settings and site verification are human acts and need a signed-in dashboard session. That line is deliberate. A leaked key can spend credits, and revoking it ends the incident; it can never change your account. Point a key at an account endpoint and you get a 403 saying so.

Reads

Resolving URLs

Three ways to get a page: JSON, JSON in bulk, or Markdown.

POST/v1/aidocument

Resolve one URL into an AIDocument

The main endpoint. Serves the stored copy when the page is in the index and inside your plan's freshness window, and fetches, cleans and stores it once when it is not, so the next agent to ask gets the fast path. The response is the v2 envelope documented on the AIDocument page. Request bodies are capped at 4 KB.

FieldTypeRequiredDescription
urlstringrequiredThe page to resolve. Must be an http or https URL.
freshness_policystringoptionalcache_first (the default) serves the stored copy while it is inside your plan's freshness window and crawls on a miss. force_refresh skips the lookup and crawls now. Any other value is a 400, so a typo never silently becomes the default.
max_tokensintegeroptionalCaps content.markdown to roughly this many tokens, trimmed at a clean paragraph or sentence boundary. Omit it, or send 0, for the full body.

Responses

200 the AIDocument v2 envelope. 202 with {"status":"indexing"} and a Retry-After header when the page is not in the index and the live-fetch budget is full; the URL is queued and a retry in a few seconds normally lands. A served copy that was older than the freshness window carries the header X-Lyrenth-Stale: 1.

POST/v1/aidocument/batch

Resolve up to 20 URLs in one call

The same read, fanned out. Every URL is isolated: one that fails is reported in its own result and never blocks the others, so the call itself succeeds with a 200 as long as the request was well formed. Billed one credit per successfully-read URL. Request bodies are capped at 16 KB.

FieldTypeRequiredDescription
urlsstring[]requiredOne to 20 URLs. An empty array is a 400, and so is a 21st URL.
freshness_policystringoptionalSame two values as the single read, applied to every URL in the call.
max_tokensintegeroptionalApplied to each returned document separately.
request
{
  "urls": [
    "https://example.com/a",
    "https://example.com/b"
  ],
  "freshness_policy": "cache_first",
  "max_tokens": 4000
}
response
{
  "results": [
    { "url": "https://example.com/a", "ok": true,  "document": { /* AIDocument v2 */ } },
    { "url": "https://example.com/b", "ok": false, "status": "upstream_not_found",
      "error": "origin returned 404",
      "message": "example.com returned 404 for that path. The page may have moved or never existed.",
      "upstream_status": 404, "host": "example.com" }
  ]
}

Each result carries url and ok. A successful one adds document. A failed one adds error, a named status, and, since September 2026, message: the same plain-English explanation a single read returns, plus upstream_status and host when an origin actually answered. Show the message to a person; branch on the status in code.

The status is the specific reason, not a generic one: upstream_forbidden, upstream_bot_filtered, upstream_blocked, upstream_not_found, upstream_gone, upstream_rate_limited, upstream_unavailable_legal, upstream_server_error, upstream_error, taken_down, robots_disallowed, no_content, indexing, or one of the transport codes listed further down. Before September 2026 every origin failure reported upstream_error, so treat an unknown status as a failure you cannot retry blindly.

GET/v1/read?url=…

The same read, as Markdown

One GET, no body, no JSON parsing. The response is text/markdown: a title, the page description, a source line, then the cleaned body. Made for a shell one-liner and for agents that want text rather than structure. Errors are still JSON, so check the content type or the status before treating the body as Markdown.

FieldTypeRequiredDescription
urlqueryrequiredThe page to read. A bare host with no scheme is accepted and treated as https.
freshqueryoptional1, true or yes forces a live re-fetch. This is the simple surface: for the full policy enum use POST /v1/aidocument.
max_tokensqueryoptionalCaps the body the same way the JSON endpoint does. A missing or non-positive value returns the full body.
response body
# Web indexing

> An overview of how search systems index the web.

Source: https://en.wikipedia.org/wiki/Web_indexing

---

Web indexing or...
Housekeeping

Queueing and usage

Neither of these counts against your quota.

POST/v1/submit

Queue a URL for background indexing

Send {"url":"..."}. The URL enters the crawl queue ahead of pages we discovered ourselves, and the call returns immediately with 202. Once it lands, reads serve it from the index at cache speed; a read made before then simply fetches the page live, so there is no need to wait before asking. queued is false when the URL is already indexed or already waiting, which means your intent is satisfied and no new work was created.

response
{
  "status": "accepted",
  "url": "https://example.com/post",
  "queued": true,
  "note": "queued for background indexing; once it lands, reads serve it from the index"
}
GET/v1/quota

Your plan and remaining credits

Takes no parameters. Reading it is what rolls your balance over at the start of a new month, so a call on the first of the month returns the fresh figure. Accounts on an unlimited plan get "unlimited": true instead of the counters.

response
{
  "unlimited": false,
  "tier": "free",
  "used": 143,
  "limit": 2000,
  "remaining": 1857,
  "period_start": "2026-08-01T00:00:00Z",
  "resets_at": "2026-09-01T00:00:00Z"
}
Errors

The error envelope

Two shapes. Branch on error, not on the status alone.

a refused request
{ "error": "url must be a http or https URL" }

Validation, auth and legal refusals put a plain sentence in error. Show it to the developer; it says what to fix.

a failure with a name
{
  "error": "upstream_blocked",
  "message": "example.com blocks our crawler at its edge (WAF / CDN). Their
              firewall or bot-protection layer rejects non-browser clients.
              Try another URL on the same domain, or ask the operator to
              allowlist Lyrenth.",
  "upstream_status": 403,
  "host": "example.com"
}

Anything that reached the origin puts a stable code in error and a plain explanation in message. Transport failures add retryable, so you know whether a second attempt is worth making.

Status codes

StatusWhat it means
200The call succeeded. JSON on every endpoint except GET /v1/read, which returns text/markdown.
202Accepted, not finished. POST /v1/submit always answers 202. A read answers 202 when the page is not in the index and the live-fetch budget is full: the URL is queued, and a Retry-After header says when to try again. A 202 is never billed.
400The request was malformed: a missing url, a URL that is not http or https, an unknown freshness_policy, a negative max_tokens, an empty or oversized urls array, or an unknown name in fields.
401No credential, or a key that is not valid. The error field carries the plain message: "missing session cookie or Authorization: Bearer header" or "invalid api key".
403Either the URL is adult content, which Lyrenth does not index or serve, or the key was pointed at an account endpoint. Keys call the product endpoints on this page; account operations need a signed-in dashboard session.
422The request was fine but no document could be produced. This covers the whole upstream and fetch family below. It is deliberately not a 5xx, so the typed message survives every proxy in between.
429Two different limits, told apart by the window field: "1s" is your plan's per-second API rate, "month" is your monthly credit quota. Both carry Retry-After.
451The URL is removed from the index, or the site's robots.txt disallows it. Lyrenth respects robots.txt on every fetch, so a disallowed URL cannot be indexed on request either.
500The page was fetched but could not be turned into an AIDocument. The body says extract_failed and the failure is logged on our side.
502The origin never answered: a DNS failure, a timeout, a refused or reset connection, a TLS failure, or a redirect loop. Each has its own code and a retryable flag.

Error codes

CodeCause
upstream_blockedThe origin's firewall or bot-protection layer rejected our crawler at its edge.
upstream_bot_filteredA host that serves the page to a browser but blanket-refuses identified crawlers.
upstream_unauthorizedThe origin demands authentication.
upstream_forbiddenA plain 403 with no auth prompt and no protection-layer signal.
upstream_not_foundThe URL does not exist on the origin (404).
upstream_goneThe origin says the URL is permanently removed (410).
upstream_rate_limitedThe origin throttled us (429).
upstream_unavailable_legalA legal restriction at the origin (451).
upstream_server_errorThe origin failed with a 5xx.
no_extractable_contentA 2xx with nothing readable on the page, usually an anti-bot challenge served instead of the article.
not_a_pageThe address is a file, not a web page: a PDF, an image, an archive, an office document, audio, video or another binary file. The message names which, for example "This address is a PDF file, not a web page. Lyrenth reads web pages." It is not billed, and retrying will not help.
origin_dns_not_foundThe domain name does not exist. Retrying will not help.
origin_timeoutThe origin did not answer inside the fetch window. Retryable.
origin_unreachableThe origin refused the connection. Retryable.
origin_tls_failureThe origin's certificate or handshake failed.
origin_redirect_loopMore than ten redirects without a page.
target_not_publicThe address resolves to a private or internal network, which Lyrenth does not fetch.
Limits

Rate and quota

Both are per account, not per key, and both are on the pricing page.

The API rate is a per-second ceiling on the read and submit endpoints, fixed by your plan. Exceeding it answers 429 with "window": "1s", the limit that applied, and Retry-After: 1. It is a pacing limit, not a penalty: waiting a second is the whole fix.

The quota is your monthly credit balance, shared across every key on the account. Running out answers 429 with "window": "month", the date it resets, and an upgrade hint. Credits are reserved before the work and settled after it, so a call that fails, or that comes back 202, costs you nothing.

Generate types from the contract.

The v2 envelope has a JSON Schema. Point your validator or code generator at it.