API reference
Check text for AI writing and rewrite drafts in your own voice over HTTP. Both endpoints take and return JSON, and use the same models as the website.
Authentication
Every request needs an API key. Sign in with Google, then create a key on your API keys page. Keys start with zl_live_ and are shown once, so store them somewhere safe. Send the key as a bearer token:
Authorization: Bearer zl_live_…Keep keys on your server. Anyone with a key can spend your allowance, and you can revoke one at any time from the API keys page.
Limits and headers
API calls count against the same monthly allowance as the website. Allowances reset on the first of the month (UTC).
| Plan | Checking | Rewriting | Requests |
|---|---|---|---|
| Free | 300,000 words a month | 10,000 words a month | 20 a minute |
| Pro (coming soon) | 3,000,000 words a month | 200,000 words a month | 120 a minute |
Every metered response carries these headers:
| Header | Meaning |
|---|---|
x-plan | Your plan: free or pro. |
x-ratelimit-limit | Requests allowed per minute. |
x-quota-limit | Words in this period's allowance for the endpoint. |
x-quota-remaining | Words left after this request. |
x-quota-reset | When the allowance resets, as an ISO 8601 timestamp. |
retry-after | Seconds to wait, sent with a 429 when you hit the per-minute limit. |
Check text
POST https://zerollm.app/api/v1/detect
Send { "text": string } with at least 30 words and at most 20,000 characters. The response scores the whole text and every sentence. score runs from 0 (reads as human) to 1 (reads as AI); sentences scoring 0.5 or more are the ones the website marks. start and end are character offsets into the text you sent.
curl https://zerollm.app/api/v1/detect \
-H "Authorization: Bearer zl_live_…" \
-H "Content-Type: application/json" \
-d '{"text": "Paste at least 30 words of the text you want checked."}'{
"score": 0.82,
"label": "ai",
"confidence": "high",
"ai_fraction": 0.6,
"signals": {
"classifier": { "probability": 0.84, "logit": 1.66 },
"binoculars": { "score": null, "threshold": 0.9 },
"style": { "aiism_per_100w": 1.4, "contractions_per_100w": 0.2 }
},
"contributions": { "classifier_logit": 2.1, "aiism_per_100w": 0.6 },
"reasons": [
{ "signal": "classifier", "leans": "ai", "text": "The classifier recognizes patterns common in model output." }
],
"sentences": [
{ "start": 0, "end": 118, "score": 0.91 },
{ "start": 119, "end": 204, "score": 0.34 }
],
"phrases": [
{ "start": 0, "end": 8, "text": "Moreover", "category": "transition" }
],
"stats": { "words": 212, "sentences": 9, "ms": 640, "calibration": "v3" }
}| Field | Type | Description |
|---|---|---|
score | number | Overall likelihood the text is AI-written, 0 to 1. |
label | "ai" | "mixed" | "human" | The verdict the score falls into. |
confidence | "high" | "medium" | "low" | How firm the verdict is. Short texts are low. |
ai_fraction | number | Share of the text in sentences that read as AI. |
sentences | array | Each sentence's start, end and score. |
phrases | array | Stock phrases models overuse, with their offsets and category. |
reasons | array | Plain-language reasons, each leaning ai, human or neutral. |
contributions | object | How much each signal moved the score. Keys appear only for signals that ran. |
signals | object | Raw signal values behind the score. |
stats | object | Word and sentence counts and processing time in milliseconds. |
const res = await fetch("https://zerollm.app/api/v1/detect", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ZEROLLM_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ text }),
});
if (!res.ok) throw new Error((await res.json()).error);
const detection = await res.json();Rewrite text
POST https://zerollm.app/api/v1/humanize
Rewrites a draft so it reads like your own writing, keeping every fact, name and number. With refine on, the result is checked and sentences that still read as AI are reworked, up to twice. The call returns once the rewrite is finished.
| Parameter | Type | Default | Description |
|---|---|---|---|
text | string | required | The draft, up to 20,000 characters. |
mode | string | standard | The voice to write in. See the list below. |
strength | string | balanced | How much to change. |
refine | boolean | true | Recheck and rework flagged sentences. |
curl https://zerollm.app/api/v1/humanize \
-H "Authorization: Bearer zl_live_…" \
-H "Content-Type: application/json" \
-d '{"text": "Your draft here.", "mode": "professional", "strength": "balanced", "refine": true}'{
"text": "The rewritten draft, in the voice you asked for.",
"score": 0.21,
"label": "human",
"refine_passes": 1,
"detection": { "score": 0.21, "label": "human", "sentences": [] },
"notices": []
}detection is the full check of the final text, in the same shape as the detect response. notices lists anything worth knowing, such as the detector being unavailable for the final check.
Errors
Errors come back with a JSON body of { "error": string } written to be shown to a person as is.
| Status | When |
|---|---|
400 | Unknown mode or strength. |
401 | The API key is missing, invalid or revoked. |
413 | The text is over 20,000 characters. |
422 | Not enough text: detect needs 30 words, humanize a few sentences. |
429 | Per-minute limit (with retry-after) or the period's allowance is used up. |
502 | The rewrite model returned an error. Safe to retry. |
503 | The detector is briefly unavailable. Safe to retry. |
Voices and strengths
| mode | Voice |
|---|---|
standard | Clear, natural, everyday voice |
academic | Measured and precise, for papers |
professional | Direct and concise, for work |
casual | Relaxed, like talking to a friend |
simple | Short sentences and everyday words |
creative | More voice and rhythm |
| strength | Change |
|---|---|
light | Keep most of your wording |
balanced | Rebuild sentences, keep the order |
strong | Rewrite from scratch, keep every point |