Synchronous scans are the interactive hot path: submit one text and receive
the document score, band, confidence, and per-sentence results in a single
response. 0 means 0% AI-like, and 100 means 100% AI-like. Sentence scores are
computed in document context, and every scan runs at sentence granularity.
Create a scan:
text
POST /v1/scans
Request body:
json
{
"text": "The onboarding checklist now includes an owner for every launch task. A shorter release window should reduce customer support handoffs."
}
Scan request limits from the OpenAPI contract:
text
text: per-scan character cap by plan (over the cap: 413 scan_input_too_large)
free plan: at most 15,000 characters; paid plans: at most 50,000 characters
anonymous App Check scans: at most 5,000 characters and 5 scans per day
Texts over the plan's per-scan character cap (15,000 on the free plan,
50,000 on paid plans) are rejected with 413 scan_input_too_large;
use the asynchronous score job API (POST /v1/score-jobs) for longer
documents or batches. Scans accept the same credentials as score jobs — API
keys, Firebase ID tokens, and anonymous App Check tokens — and require the
score_jobs:create scope. Scan input counts against the same quotas as score
jobs: the anonymous daily scan quota and the free-plan monthly word
allowance.
Successful response:
json
{
"scanId": "scan_000301",
"is_ai": 47,
"band": "mixed",
"confidence": 0.62,
"sentences": [
{
"text": "The onboarding checklist now includes an owner for every launch task.",
"start": 0,
"end": 70,
"is_ai": 22,
"band": "likely_human"
},
{
"text": "A shorter release window should reduce customer support handoffs.",
"start": 71,
"end": 137,
"is_ai": 68,
"band": "likely_ai"
}
],
"createdAt": "2026-07-06T18:00:00Z"
}
Sentence granularity returns submitted sentence text with exact start/end
offsets into the submitted text. The most AI-like sentences carry the
likely_ai band.
If the scan backend is temporarily unreachable, the API responds with
503 scan_backend_unavailable; retry after a few seconds or fall back to the
asynchronous score job API. Synchronous scans have no job lifecycle — there
is nothing to poll or cancel.
Shared Scan Results
Paid dashboard accounts can create read-only public links for stored scan
results from dashboard history. Share creation is handled by the internal
dashboard API and requires a Plus or Pro plan. Firestore remains deny-all for
direct clients; public reads go through the API.
Read a shared scan:
text
GET /v1/shared/{shareId}
The shareId is an unguessable capability identifier such as
share_0123456789abcdef0123456789abcdef. This endpoint does not require an
API key, Firebase ID token, or App Check token.
Successful response:
json
{
"shareId": "share_0123456789abcdef0123456789abcdef",
"is_ai": 47,
"band": "mixed",
"confidence": 0.62,
"sentences": [
{
"text": "The onboarding checklist now includes an owner for every launch task.",
"start": 0,
"end": 70,
"is_ai": 22,
"band": "likely_human"
}
],
"createdAt": "2026-07-06T18:00:00Z"
}
When the owner enables text redaction, sentence text fields are omitted but
offsets, scores, and bands remain so clients can render a shape-only heatmap.
Revoked shares and unknown share IDs both return the same
404 shared_scan_not_found response. There is no published request-rate quota
for this unauthenticated endpoint; capability unguessability, uniform 404s, and
revocation are the primary controls.
File Upload Scans
Pro accounts can upload a PDF, DOCX, or TXT file and scan the server-extracted
text. The raw file is retained in the API artifacts bucket at
uploads/{uploadId}. Extracted text and the scan or score result are persisted
through the normal scans or score_jobs collections.
Upload the file bytes to signedUrl with PUT. Use the same Content-Type
sent in the create request and include the signed
x-goog-content-length-range: 0,10485760 header. The API also re-checks the
stored object size before extraction.
Trigger extraction and scoring:
text
POST /v1/uploads/{uploadId}/scan
By default, small extracted files use the synchronous scan path and larger
files queue a sentence-granularity score job. Batch clients can force every
upload through the async job path:
json
{
"forceAsync": true
}
For extracted text up to 15,000 characters, the API returns the synchronous
scan result plus pages when the source was a PDF:
json
{
"uploadId": "upl_000301",
"status": "scanned",
"pages": [
{
"page": 1,
"start": 0,
"end": 70
}
],
"scanId": "scan_000301",
"is_ai": 47,
"band": "mixed",
"confidence": 0.62,
"sentences": [
{
"text": "The onboarding checklist now includes an owner for every launch task.",
"start": 0,
"end": 70,
"is_ai": 22,
"band": "likely_human"
}
],
"createdAt": "2026-07-06T18:00:00Z"
}
For extracted text over 15,000 characters, or when forceAsync is true, file
scans create a sentence-granularity score job instead of returning
413 scan_input_too_large:
PDF pages are one-based and use start/end offsets into the extracted text, so
clients can group sentence heatmaps by page. DOCX and TXT uploads return an
empty pages list.
Failure codes:
text
402 upgrade_required: account is not on Pro
413 file_too_large: file is over 10 MB
422 file_unsupported: not PDF, DOCX, or TXT
422 file_encrypted: encrypted PDF
422 file_too_complex: DOCX expands too large, PDF has too many pages, or extraction times out
422 file_no_text: scanned/image-only file or empty extraction
Share Links
Completed scans you own can be published as read-only share links and managed
entirely via the API (scope shares:manage; requires a Plus or Pro plan;
these are the same endpoints the dashboard uses):
text
POST /v1/scans/{scanId}/share create (or return the existing active share)
PATCH /v1/scans/{scanId}/share update text redaction: {"redactText": true}
DELETE /v1/scans/{scanId}/share revoke the link
GET /v1/shared/{shareId} public, unauthenticated read
With redactText enabled the public share keeps sentence offsets, scores, and
bands but omits sentence text. Revoking a share makes the public link return
404 shared_scan_not_found; revocation works on any plan so downgraded owners
can always kill an existing public link. Free-plan share creation and
redaction updates return 402 upgrade_required.