Quickstart

Walk through the five API calls — /extract → /assess → /verify → /ask → /review — in five minutes. Pick Python or TypeScript below.

Open the quickstart in Google Colab Prefer to run it in your browser? Skip the install and use the Colab notebook.

1

Get an API key

Sign up, then visit /api-credentials and generate a key. You'll see it once — copy it somewhere safe.

Or skip ahead: /setup issues a test key and writes the setup instructions for Claude Code, Cursor, ChatGPT or n8n — paste them into your agent and it does the wiring below for you.

2

Install the SDK

Python
pip install lenz-io
TypeScript
npm install lenz-io
CLI
pipx install "lenz-io[cli]"

Prefer the terminal? The lenz CLI calls Lenz from your shell — the same four primitives as the SDKs, with --json to pipe into jq. It ships inside the Python package behind the cli extra. Run lenz login once to store your key (or set LENZ_API_KEY), then use the commands below.

To wire Lenz into an agent instead — Claude Code, Claude Desktop, ChatGPT, Cursor, Codex — that's one command in the client's own tooling or a pasted config: see MCP setup.

3

Review a whole draft in one call

Paste any draft. /review pulls out its claims, gives each a fast /assess verdict, and sends the negative and low-confidence ones through the full /verify pipeline, up to five by default (escalate changes the rule). One async call, 2–4 min, the issues back with suggested rewrites.

Python
from lenz_io import Lenz

client = Lenz(api_key="lenz_...")

draft = """
The EU AI Act entered into force on 1 August 2024, and its obligations for
general-purpose models applied from 2 August 2025. Fines for prohibited
practices reach 7% of global annual turnover. About 40% of European
companies had started compliance work by the end of 2024.
"""

# One call: extract, assess, and verify the doubtful claims (async, 2–4 min)
review = client.review_and_wait(text=draft)
print(review.outcome)   # clean | issues_found | incomplete | unchecked
for i in review.issues:
    print(i.verdict, i.confidence, i.claim)
    if i.suggested_rewrite:
        print("  Suggested rewrite:", i.suggested_rewrite)

# Past the cap: send the remaining claims to /verify in one batch
capped = [{"claim": c.claim} for c in review.claims if c.escalation and c.escalation.disposition == "cap"]
results = client.verify_batch_and_wait(claims=capped) if capped else []
deep = next((i for i in review.issues if i.verification_id), None)   # used in step 4
TypeScript
import { Lenz } from "lenz-io";

const client = new Lenz({ apiKey: "lenz_..." });

const draft = `
The EU AI Act entered into force on 1 August 2024, and its obligations for
general-purpose models applied from 2 August 2025. Fines for prohibited
practices reach 7% of global annual turnover. About 40% of European
companies had started compliance work by the end of 2024.
`;

// One call: extract, assess, and verify the doubtful claims (async, 2–4 min)
const review = await client.reviewAndWait({ text: draft });
console.log(review.outcome);   // clean | issues_found | incomplete | unchecked
for (const i of review.issues) {
  console.log(i.verdict, i.confidence, i.claim);
  if (i.suggested_rewrite) console.log("  Suggested rewrite:", i.suggested_rewrite);
}

// Past the cap: send the remaining claims to /verify in one batch
const capped = review.claims.filter((c) => c.escalation?.disposition === "cap").map((c) => ({ claim: c.claim }));
const results = capped.length ? await client.verifyBatchAndWait({ claims: capped }) : [];
const deep = review.issues.find((i) => i.verification_id);   // used in step 4
CLI
# One call: prints the quick verdicts, then rewrites each row as its deep check lands
lenz review draft.md

# only the issues; exit code 0 clean, 1 issues found, 2 incomplete
lenz review draft.md --issues

suggested_rewrite comes from a /verify run, so an issue that stayed on the fast verdict has none. It is not verified itself: review it, or run it through /verify, before you use it. The deep check can change the quick verdict. When it does, rely on the deep check: it is the one that shows its sources and its reasoning.

Credits: 1 per claim assessed + 10 (5 at depth: "low") per claim verified; credits.charged says what the review cost. A resend with the same Idempotency-Key within 24 hours returns the same review.

Or run the ladder yourself: extract → assess → verify

The primitives, call by call, when you want your own escalation rule. Paste any model output. /extract pulls out the atomic claims, one /assess call gives each of them a fast verdict (a list of up to 20, one row per claim, in order), and the low-confidence ones go to the full /verify pipeline in one batch.

rationale is the reasoning of a reviewer who agrees with the panel's verdict; dissent, when set, is the reasoning of the reviewer farthest from it. Both are reviewers' notes, not checked sources; for sourced evidence, call /verify.

text can also be a single public web page URL (http or https, nothing else in the field). Lenz reads the page, or a YouTube video's transcript, and extracts the claims from its first 50,000 characters. Pages behind a login (Facebook, Instagram, Threads, LinkedIn) can't be read. A URL call typically takes 5-40 seconds: SDK 2.13 and later wait up to 90 seconds; with an older SDK, create the client with Lenz(timeout=90) (Python) or new Lenz({ timeoutMs: 90000 }) (Node).

Python
from lenz_io import Lenz

client = Lenz(api_key="lenz_...")

draft = """
The EU AI Act entered into force on 1 August 2024, and its obligations for
general-purpose models applied from 2 August 2025. Fines for prohibited
practices reach 7% of global annual turnover. About 40% of European
companies had started compliance work by the end of 2024.
"""

# 1. Extract atomic claims (free, ~3s)
out = client.extract(text=draft)
claims = out.identified_claims or [out.claim]

# 2. Fast verdict on every claim in ONE call (~10-20s, 3-model panel):
#    a list of up to 20, one row per claim, same order.
quick = client.assess(claims=claims).claims
for c in quick:
    print(c.verdict, c.confidence, c.claim)
    if c.rationale:
        print("  ", c.rationale)

# 3. Escalate the low-confidence ones to /verify in one batch (~90s, multi-model pipeline)
# Rows we ran out of time on are free and worth resending as-is.
retry = [c.claim for c in quick if c.error_code == "timeout"]
quick += client.assess(claims=retry).claims if retry else []
doubtful = [{"claim": c.claim} for c in quick if c.verdict != "Error" and c.confidence == "low"]
results = client.verify_batch_and_wait(claims=doubtful) if doubtful else []
for r in results:
    if r.verification:
        print(r.verification.verdict, r.verification.lenz_score, r.verification.confidence)
deep = next((r.verification for r in results if r.verification), None)   # used in step 4
TypeScript
import { Lenz } from "lenz-io";

const client = new Lenz({ apiKey: "lenz_..." });

const draft = `
The EU AI Act entered into force on 1 August 2024, and its obligations for
general-purpose models applied from 2 August 2025. Fines for prohibited
practices reach 7% of global annual turnover. About 40% of European
companies had started compliance work by the end of 2024.
`;

// 1. Extract atomic claims (free, ~3s)
const out = await client.extract({ text: draft });
const claims = out.identified_claims?.length ? out.identified_claims : [out.claim!];

// 2. Fast verdict on every claim in ONE call (~10-20s, 3-model panel):
//    a list of up to 20, one row per claim, same order.
const quick = (await client.assess({ claims })).claims;
quick.forEach((c) => {
  console.log(c.verdict, c.confidence, c.claim);
  if (c.rationale) console.log("  ", c.rationale);
});

// 3. Escalate the low-confidence ones to /verify in one batch (~90s, multi-model pipeline)
// Rows we ran out of time on are free and worth resending as-is.
const retry = quick.filter((c) => c.errorCode === "timeout").map((c) => c.claim!);
if (retry.length) quick.push(...(await client.assess({ claims: retry })).claims);
const doubtful = quick.filter((c) => c.verdict !== "Error" && c.confidence === "low").map((c) => ({ claim: c.claim! }));
const results = doubtful.length ? await client.verifyBatchAndWait({ claims: doubtful }) : [];
for (const r of results) {
  if (r.verification) console.log(r.verification.verdict, r.verification.lenz_score, r.verification.confidence);
}
const deep = results.find((r) => r.verification)?.verification;   // used in step 4
CLI
# 1. Extract atomic claims (free, ~3s)
lenz extract "$(cat draft.md)"
lenz extract "https://en.wikipedia.org/wiki/Artificial_Intelligence_Act"   # or a public web page, by URL

# 2. Fast verdict via /assess (~10s, 3-model panel) — pass several claims for one row each
lenz assess "Fines for prohibited AI practices under the EU AI Act reach 7% of global annual turnover." "About 40% of European companies had started EU AI Act compliance work by the end of 2024."

# 3. Deep-verify with citations (~90s, multi-model pipeline)
lenz verify "About 40% of European companies had started EU AI Act compliance work by the end of 2024."

# pipe any command through jq for machine-readable output
lenz verify "<claim>" --json | jq '.verdict, .lenz_score'
4

Ask follow-ups on a verification

Once /verify lands, /ask grounds a chat thread on the verification's evidence. Same model, same citations, no re-research per turn.

Python
if deep:   # step 3 escalated at least one claim
    reply = client.ask.send(
        deep.verification_id,
        message="Which source has the strongest evidence?",
    )
    print(reply.content)
TypeScript
if (deep) {   // step 3 escalated at least one claim
  const reply = await client.ask.send(deep.verification_id, {
    message: "Which source has the strongest evidence?",
  });
  console.log(reply.content);
}
Reply format. reply.content is plain text with a small markdown subset: **bold** / *italic*, - or * bullet lists, and blank-line paragraph breaks. The model only produces these — no headings, no tables, no code blocks. Pass it through any markdown library (markdown-it, python-markdown) or display it verbatim.
5

Next steps

Verify your own claims and the full pipeline runs. For production:

  • Switch to webhook delivery instead of polling. Pass webhook_url on verify(); Lenz POSTs the typed payload to your endpoint when the pipeline lands. The SDK ships a LenzWebhooks handler that verifies signatures.
  • Use verify_batch for fan-out of multi-claim LLM output.
  • Pass depth="low" when a shallower check is enough — fewer sources, a shorter debate (opening arguments, no rebuttals), back sooner, the same models, and half the credits (5 instead of 10). You're charged for the depth you asked for, so a low request answered from an existing deeper check still costs 5.
  • Check the full API reference for every endpoint.

Common errors

Every error from the SDK carries cause, fix, doc_url and a request_id you can quote on a support ticket.

LenzAuthError — your API key is missing or invalid. Regenerate at /api-credentials.
LenzRateLimitError — too many requests. retry_after carries seconds until the next allowed call.
LenzQuotaExceededError (HTTP 402) — your credits are spent for this period, and retrying won't clear it. From SDK 2.7.0 the error carries remaining (calls of that kind left — 0 here), resets_at (when the monthly allowance rolls over), and upgrade_url, pointing at /plans. From 2.9.0 the SDK also exposes credit_balance (the wire field is credits_remaining) and cost, which is the difference between "4 credits, this needs 10" (one top-up away; top up on lenz.io/billing) and "0 credits" (a plan decision). cost is depth-aware: a rejected depth="low" verify reports 5.
LenzValidationError — request body is malformed. errors is a list of per-field complaints.
LenzNeedsInputError — the text holds several claims and the pipeline paused for a choice. Inspect the payload, then call client.select(task_id, ...) to resume.
LenzTimeoutError — verify_and_wait exceeded the timeout. The pipeline keeps running server-side; the exception's task_id lets you resume via client.get_status().
Found this useful? The Python SDK lives at github.com/lenzhq/lenz-io-python and the Node SDK at github.com/lenzhq/lenz-io-node. File issues there — we read them.

Using the API in production? Read the Terms of Service →