Quickstart
Walk through the five API calls —
/extract → /assess → /verify → /ask → /review —
in five minutes. Pick Python or TypeScript below.
Prefer to run it in your browser? Skip the install and use the Colab notebook.
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.
Install the SDK
pip install lenz-io
npm install lenz-io
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.
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.
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
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
# 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).
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
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
# 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'
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.
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)
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.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.
Next steps
Verify your own claims and the full pipeline runs. For production:
- Switch to webhook delivery instead of polling. Pass
webhook_urlonverify(); Lenz POSTs the typed payload to your endpoint when the pipeline lands. The SDK ships aLenzWebhookshandler 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 alowrequest 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.
retry_after
carries seconds until the next allowed call.
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.
errors is a list of per-field complaints.
client.select(task_id, ...) to resume.
verify_and_wait exceeded the
timeout. The pipeline keeps running server-side; the exception's
task_id lets you resume via client.get_status().
Using the API in production? Read the Terms of Service →