Base URL and authentication
All requests go to:
Authenticate with an API key as a bearer token. Create and revoke keys in your account under API keys. The key is shown once at creation; store it somewhere safe.
Authorization: Bearer pt_live_xxxxxxxxxxxxxxxx
A key spends the owning account's Bus Pass or token balance. The free trial is web-only, so a keyed scan resolves pass then tokens; out of both returns 402.
Scan a draft
Send the facets that resolve which rulesets apply, plus the draft itself in one of three ways: pasted text, a URL we fetch, or an image we transcribe.
Request fields
| Field | Type | Notes |
|---|---|---|
| source | string | One of paste, url, image. Defaults to paste. |
| asset_type | string | One of email, landing_page, blog, social. |
| state | string | Two-letter state code marketed into, e.g. CA. |
| vertical | string | The regulated vertical. See /api/library for the current vocabulary. |
| covered_entity | boolean | True if the advertiser is a HIPAA covered entity. |
| subject | string | Email subject and preview text. Email only, optional. |
| body | string | The draft copy, for source: paste. HTML or plain text, up to 60,000 characters. |
| url | string | The page to fetch and scan, for source: url. |
| image | object | For source: image: { media_type, data_b64 }. Types: png, jpeg, webp, gif. |
Example
curl https://scan.kesey.agency/api/v1/scan \
-H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"source": "paste",
"asset_type": "email",
"state": "CA",
"vertical": "addiction_treatment",
"covered_entity": false,
"subject": "Beat addiction this month",
"body": "Our program guarantees a full recovery. Call now."
}'
Response
A JSON object with four parts:
scan: the deterministic verdict, findings (each with the quoted line, the rule, and the fix), cleared rulesets, and any standing checks.ai: the advisory pass. It can only add nuance, never clear a finding.intake: the mode, and for url or image the exact text that was scanned, echoed back so the basis is never a black box.spend: which entitlement was spent (passortoken) and what remains.
Rate limits
Requests are limited per key, by default 60 per minute. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. Over the limit returns 429 with a Retry-After header in seconds.
Errors
| Status | error | Meaning |
|---|---|---|
| 400 | bad request | A field is missing or invalid. The message names it. |
| 401 | unauthorized | The key is missing, malformed, or revoked. |
| 402 | out_of_scans | No pass or tokens left. Buy more in your account. |
| 422 | extract failed | A URL would not load or an image was unreadable. Nothing was spent. |
| 429 | rate_limited | Over the per-key rate limit. Retry after the given seconds. |
A scan that fails after spending refunds itself, so a 500 never costs a scan.
What happens to your draft
Machine-readable spec
An OpenAPI description of this endpoint is at /developers/openapi.json.