HTTP API

Anonymous, no key required. Current limits: 10,000 characters per scan, 20 scans/hour per IP (6 with the model signal). API keys with higher limits arrive with accounts.

POST /api/scan — three ways in

The body can be JSON, a form field, or just raw text. The quickest taste, straight from a file, with a human-readable answer:

# pipe any file in, get the report back as plain text:
curl -s --data-binary @draft.txt "https://scanforai.com/api/scan?format=text"

JSON for programs (a JSON body overrides the query-param knobs):

curl -s https://scanforai.com/api/scan \
  -H 'Content-Type: application/json' \
  -d '{"text": "...", "use_model": true, "include_fixes": true, "background": false}'

Set "background": true for batch/scripted scans: they queue behind interactive web scans on the model worker (heuristics are instant either way), in exchange for a much longer queue-wait allowance.

File upload (multipart; .txt, .md, .html, .docx — text is extracted server-side, 1 MB max):

curl -s -F "[email protected]" https://scanforai.com/api/scan

Response (fields are stable; new ones may be added):

{
  "score": 68.4,            // 0..100 tell density — NOT a probability
  "band": "elevated",       // low | moderate | elevated | high
  "label": "strong AI tells",
  "model_used": true,        // was the local perplexity signal in the mix
  "word_count": 1240,
  "coverage": {"active_signals": 9, "total_signals": 11, "words": 1240},
  "signals": [ {"name": "burstiness", "score": 0.82, "weight": 3.0,
                "detail": "sentence-length CV=0.24 (human ~0.5-0.9, ...)",
                "value": 0.24}, ... ],
  "fixes": ["burstiness: ...", ...],
  "scan_id": null,           // anonymous scans are never stored
  "disclaimer": "AI-tell density, not a verdict. ..."
}

Errors: 413 over the character cap, 429 rate-limited (message includes retry seconds), 422 empty text. If the model worker is briefly unavailable the scan still succeeds heuristics-only, with "model_used": false and a "degraded" note.

GET /api/queue

Model-queue state, for showing a wait estimate: {"queued_interactive": 3, "queued_background": 12, "workers": 2, "capacity": 4, "model_loaded": true, "avg_scan_seconds": 2.4, "eta_interactive_seconds": 4.3, "eta_background_seconds": 11.9}workers is live worker processes, capacity their combined inference threads.

GET /api/config

The limits for your tier — what the landing-page counter uses.

GET /api/health

{"ok": true, "version": "0.1.0", "model_worker": true}

Share links (accounts)

POST /api/scans/{id}/share returns an expiring public URL (https://scanforai.com/s/<token>, 7 days) showing the RESULT only — the original text never leaves your account. DELETE /api/scans/{id}/share revokes all links for that scan; deleting the scan or your account revokes them too.

Accounts & API keys

A free account (dashboard) raises the per-scan limit to 50,000 characters, saves scan history with revision comparison, and issues API keys. Keys are sent the same way as the examples above: Authorization: Bearer at_sk_…. Key scopes: full, or read (may scan and read history, cannot modify anything). Authenticated scans are saved by default — pass "save": false to opt out; anonymous scans are never stored.

Fair use

Please don't wrap this in a product that presents scores as verdicts about people. The response's disclaimer field exists to be shown.