stylometrics.ai Opens in a new tab. Your draft stays in this tab.

API documentation

Use the AI detector, Declaude and our writing and image utilities from your server with shared prepaid credit.

Get a key

Sign in to your account and create a key with the tools you need: AI detector (checker), Declaude (rewrite), summarize, email, image-to-text or remove-background. New keys start with stylometrics_live_. Existing keys do not gain access automatically. Keep the key on your server and select only the permissions your integration needs.

Creating a key requires a sign-in within the last five minutes. If asked to sign in again, confirm the new email link and return to your original account tab to retry. Existing credit can be used immediately; a top-up cannot resolve a permission error.

Send Authorization: Bearer YOUR_API_KEY and Content-Type: application/json with every POST. Server clients use the Bearer key alone: do not send session cookies, an Origin header or a CSRF token. The browser’s own session flow has separate CSRF protections. Never combine a key with a signed-in browser cookie.

Base URL: https://stylometrics.ai. These are synchronous JSON endpoints. They do not stream or send email.

Utility endpoints
MethodPathPurpose
GET/api/toolsPublic availability listing. No key required.
POST/api/tools/{tool}/quoteCheck the input and get a maximum price.
POST/api/tools/{tool}/runReserve credit, run once and return the result.
POST/api/tools/{tool}/statusRecover the financial state of an existing request.

Replace {tool} with one of the four tool IDs above. Use the canonical path exactly, without a trailing slash or encoded characters. GET /api/tools returns tools, an array of id, available and reason. This checks service configuration; a quote still checks your account, tool permission and active pricing policy.

AI detector

Select AI detector permission for the key. First quote the exact input, then accept the returned priceCents before scanning. Input must contain 40–4,000 words and fit within 64,000 bytes. The quote also reports current limits and availability.

# Use a shared key with AI detector permission. Save this exact input.
umask 077
cat > detector-input.json <<'JSON'
{
  "text": "Our team meets on Friday to review the garden project. Bring your notes about planting dates, the equipment we still need, and any questions from volunteers. We will agree on a watering schedule and decide who can help at the weekend. Please tell the coordinator before Thursday if you cannot attend, so we can share your suggestions with everyone at the meeting."
}
JSON
curl --fail-with-body --silent --show-error \
  https://stylometrics.ai/api/quote \
  -H "Authorization: Bearer $STYLOMETRICS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @detector-input.json > detector-quote.json
cat detector-quote.json

# After accepting priceCents, save one request reference per submission.
# Reuse detector-run.json after a timeout; do not generate a replacement reference.
request_key="$(python3 -c 'import uuid; print(uuid.uuid4())')"
jq --arg quote_id "$(jq -er '.quoteId' detector-quote.json)" \
   --arg request_key "$request_key" \
   '. + {quoteId: $quote_id, idempotencyKey: $request_key}' \
   detector-input.json > detector-run.json
curl --fail-with-body --silent --show-error --max-time 660 \
  https://stylometrics.ai/api/scan \
  -H "Authorization: Bearer $STYLOMETRICS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @detector-run.json > detector-result.json

Save the original response’s results and composite. Scores describe experimental writing patterns, not a probability of AI authorship. After an uncertain response, check the same request reference:

jq '{action: "status", idempotencyKey}' detector-run.json > detector-status.json
curl --fail-with-body --silent --show-error \
  https://stylometrics.ai/api/scan \
  -H "Authorization: Bearer $STYLOMETRICS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @detector-status.json

Status and retries recover the financial receipt, but do not recover the original detector results. A completed receipt may have resultsUnavailable: true. Do not automatically submit another paid scan. Detector errors use error.code and error.message.

Declaude

Select Declaude permission on the same shared key. GET https://declaude.org/api/v1/account checks permitted credit without a charge; POST https://declaude.org/api/v1/declaude rewrites text. Follow the Declaude request and retry examples(opens in a new tab). These endpoints use a separate request format and string-valued error code.

Quote, then run

The examples below use synthetic text and local files to preserve the exact request for recovery. Run them in a private directory. Protect these files as you would the submitted text, and delete them when no longer needed. You need curl, jq and Python 3.

1. Get a price

# Set STYLOMETRICS_API_KEY securely in your server environment.
# This example uses curl and jq. Never ship the key in browser code.
umask 077
cat > caller.json <<'JSON'
{
  "text": "The garden club meets Friday at 6 pm in the village hall. Entry costs $5. Bring one plant to exchange.",
  "mode": "summarize",
  "length": "short",
  "format": "bullets"
}
JSON

# 1. Get a maximum price; quoting does not run the model.
jq '{caller_request: .}' caller.json > quote-request.json
curl --fail-with-body --silent --show-error \
  https://stylometrics.ai/api/tools/summarize/quote \
  -H "Authorization: Bearer $STYLOMETRICS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @quote-request.json > quote.json
cat quote.json

2. Accept the price and run once

Continue only after a successful quote, and only if you accept its maximum_nanos. This step spends credit. Save the request before sending it.

# 2. After accepting maximum_nanos, save this job's request once.
# Generate a NEW unique key for each NEW job; preserve it for retries.
request_key="$(python3 -c 'import uuid; print(uuid.uuid4())')"
jq --arg quote_id "$(jq -er '.quote_id' quote.json)" \
   --arg idempotency_key "$request_key" \
   '{caller_request: ., quote_id: $quote_id, idempotency_key: $idempotency_key}' \
   caller.json > run-request.json
curl --fail-with-body --silent --show-error \
  https://stylometrics.ai/api/tools/summarize/run \
  -H "Authorization: Bearer $STYLOMETRICS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @run-request.json > result.json

3. Recover if the response is lost

Do not repeat step 2 to recover a missing response: that creates a new request key. Keep run-request.json, check its status below and follow the recovery rules.

# 3. If the response is lost, check the SAME key before doing anything else.
jq '{idempotency_key}' run-request.json > status-request.json
curl --fail-with-body --silent --show-error \
  https://stylometrics.ai/api/tools/summarize/status \
  -H "Authorization: Bearer $STYLOMETRICS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @status-request.json

Request fields

Send a JSON object. Quote accepts only caller_request. Run accepts caller_request, quote_id and idempotency_key. Status accepts only idempotency_key. Unknown fields are rejected. A request key must contain 16–128 ASCII letters, digits, underscores or hyphens; UUIDs work. Send it in the JSON body, not an Idempotency-Key header.

A quote uploads the full text or image to Stylometrics for validation and pricing. It does not run the AI provider. An admitted run sends that input to the configured external model provider.

Summary / Shorten · summarize

{
  "text": "The garden club meets Friday at 6 pm in the village hall. Entry costs $5. Bring one plant to exchange.",
  "mode": "summarize",
  "length": "short",
  "format": "bullets"
}

text
Required, nonblank, at most 40,000 UTF-8 bytes.
mode
summarize (default) or shorten.
length
short, medium (default) or long.
format
paragraph (default) or bullets.
target_words
Optional integer from 20 to 2,000. Overrides the length target. Word counts are approximate.

Email Helper · email

{
  "text": "Can we meet on Friday to discuss the garden club?",
  "mode": "reply",
  "intent": "Friday works. Ask what time they prefer.",
  "tone": "friendly",
  "length": "short"
}

text
Required notes or incoming email, at most 36,000 UTF-8 bytes.
mode
compose (default) or reply.
intent
Required nonblank reply instructions in reply mode; optional additional notes in compose mode. At most 4,000 UTF-8 bytes. Defaults to an empty string.
tone
neutral (default), friendly or formal.
length
short, medium (default) or long.

The result is plain text containing a subject line and email body. It is a draft for you to review; nothing is sent.

Image to Text · image-to-text

{
  "image_data_url": "data:image/png;base64,REPLACE_WITH_COMPLETE_BASE64_IMAGE"
}

Send one complete still PNG, JPEG or WebP as image_data_url, using its matching MIME type and standard padded base64. The decoded image must be at most 6 MiB (6,291,456 bytes) and 2,048 pixels on each side. Resize before sending; API clients do not get the website’s automatic resize. URLs, PDFs, SVGs and animations are unsupported. The example’s base64 placeholder must be replaced with a real image.

The result is plain text in the original language. Unreadable text may be marked [illegible]; an image without text returns [No visible text]. Review the transcription, especially names and numbers.

Remove Background · remove-background

{
  "image_data_url": "data:image/png;base64,REPLACE_WITH_COMPLETE_BASE64_IMAGE"
}

Use the same image format, 6 MiB input limit and 2,048-pixel dimensions as Image to Text. The prepared image is processed by fal.ai. The result is a transparent PNG in result.image_data_url, a data:image/png;base64,... URL. Decode the base64 to save the PNG. The output is at most 24 MiB decoded; allow roughly 32 MiB for its base64 JSON response.

Pricing and responses

The website and API use the same credit and pricing. Successful requests cost 3× provider processing cost, including the current OpenRouter funding fee of 5.5% where applicable. Background removal currently has a fixed provider price of $0.01 per successful image and costs $0.03 of credit; the OpenRouter funding fee does not apply to fal.ai. There is no minimum charge. Provider cost is rounded up to whole nanodollars, then the funding fee is applied and rounded up before multiplying by three.

All *_nanos values are decimal strings in billionths of a US dollar: "1000000000" means $1. Use integer or decimal arithmetic. The model and budget are fixed by the service; the request cannot choose another model or supply a custom provider.

Quote response

{
  "quote_id": "00000000-0000-4000-8000-000000000001",
  "expires_ms": 1791559500000,
  "maximum_nanos": "21146949",
  "price_multiplier": 3
}

maximum_nanos is the most credit that this request can reserve, not its final charge. expires_ms is a JSON number containing a Unix timestamp in milliseconds; use the returned value rather than assuming a fixed lifetime. The values above are examples. Quote the exact input you intend to run, and run it before expiry.

Run request

{
  "caller_request": {
    "text": "The garden club meets Friday at 6 pm in the village hall. Entry costs $5. Bring one plant to exchange.",
    "mode": "summarize",
    "length": "short",
    "format": "bullets"
  },
  "quote_id": "00000000-0000-4000-8000-000000000001",
  "idempotency_key": "garden-summary-example-0001"
}

Successful first response

{
  "job_id": "00000000-0000-4000-8000-000000000002",
  "state": "completed",
  "customer_nanos": "189900",
  "provider_nanos": "63300",
  "result": {
    "text": "- Friday at 6 pm, village hall.\n- Entry: $5; bring one plant to exchange."
  },
  "result_expires_ms": 1791559980000
}

provider_nanos includes the funding fee. customer_nanos is the final charge only after settlement; unused reserved credit is released. Summary, Email Helper and Image to Text return result.text. Remove Background returns result.image_data_url. Treat the text as untrusted plain text, and escape it when displaying it in HTML. result_expires_ms is the temporary recovery copy’s expiry as a Unix timestamp in milliseconds, or null when there is no recovery copy; the timestamp is not a guarantee of availability until then.

Recover without a second charge

Persist the tool, original account and API key identity, exact caller_request, quote_id and idempotency_key before calling run. If the connection drops or returns an uncertain error, check status using the same tool and key. A retry of run must keep the same input and request key. Changed input with a reused key is rejected.

{
  "idempotency_key": "garden-summary-example-0001"
}

{
  "job_id": "00000000-0000-4000-8000-000000000002",
  "state": "completed",
  "replayed": true,
  "customer_nanos": "189900",
  "results_unavailable": false,
  "result": {
    "text": "- Friday at 6 pm, village hall.\n- Entry: $5; bring one plant to exchange."
  },
  "result_expires_ms": 1791559980000
}

Request states
StateWhat to do
not_foundNo admitted request was found for this tool, key and account. You may retry the saved run request. If its quote expired, obtain a new quote for the same input and keep the same request key.
authorizedCredit is reserved. Processing has not been claimed yet. Keep the original request key; do not submit another job.
runningProcessing has started. Wait and check again with the same request key.
unknownThe provider outcome is uncertain. Background-removal reservations close without charge after five minutes; other utility reservations are retained for reconciliation. Do not start a replacement job unless you intend to pay for another request.
completedThe final charge is known. Status or a same-key retry can return a temporary recovery copy when available. Save the result yourself for longer-term use.
failed / cancelledThe request is terminal and its customer reservation has been released. A fresh job requires a new quote and request key.

For authorized, running and unknown, customer_nanos describes reserved credit, not a final charge. Status for not_found has no job_id. Poll conservatively and respect any Retry-After header.

Status and replayed run responses can include the original result.text or result.image_data_url after financial completion, with replayed: true, results_unavailable: false and result_expires_ms. Recovery never makes another provider call or charge.

Background-removal PNGs are saved encrypted for 24 hours and survive server restarts and deployments. Use the original active API key. You can send either { "idempotency_key": "your-original-key" } or { "job_id": "the-job-uuid" } to /api/tools/remove-background/status; do not send both. Website jobs are recoverable through the same canonical account after signing in again. Keys cannot retrieve website jobs or another key’s output.

A successful background-removal receipt includes result_sha256, the SHA-256 digest of the decoded PNG bytes. Verify the digest and decode the PNG before treating a download as complete. Retry status on a failed download using the same reference. Requests still unresolved five minutes after admission fail with a zero customer charge; a late provider response cannot charge them afterward.

If the server confirms that a saved PNG is missing or corrupt before result_expires_ms, it returns delivery_failed: true, refunded_nanos and the net customer_nanos: "0". The original charge and provider cost remain in the billing record. This credit adjustment happens once and cannot be requested by reporting a client download error or dissatisfaction with the cutout. Expired results are unavailable without an automatic credit adjustment.

Summary, Email Helper and Image to Text use an output-only memory cache lasting at most 10 minutes. They require the same session or API key and server instance. Restarts, deployments or capacity eviction can remove those copies sooner. A cache miss returns results_unavailable: true without result; the financial receipt remains available. Save the result yourself for longer-term use.

Uploaded source text and images are not saved for recovery. Text-tool output stays in a temporary memory cache; background-removal PNGs are saved encrypted on disk for 24 hours. Save successful results in your own application. Keep the original request reference and authentication. Stylometrics does not automatically retry an uncertain provider call. Background-removal holds close without charge after five minutes; other utility holds remain until reconciliation.

Do not create a new request key merely because a request timed out. If you deliberately start another job after a completed or uncertain request, it can create another charge. If a hold remains unresolved, contact [email protected] with the job ID; never send your API key.

Limits and errors

The total quote/run JSON body is capped at 8,400,000 bytes; status bodies at 4,096 bytes. Uploads must arrive within 15 seconds. The provider call has a 120-second timeout, so allow more than two minutes for an end-to-end run response. Text output is capped at 4,096 model tokens; a confirmed incomplete result fails without a customer charge.

Rate limits, concurrent work limits, account spending caps and API-key funding permissions also apply. At most 30 distinct unexpired quotes can be held per owner across tools and keys; identical current price checks reuse a quote. Reduce concurrency when busy. A 429 response includes Retry-After in seconds. Back off before retrying, and preserve the request key if a prior attempt may have started.

{
  "error": {
    "code": "insufficient_funds",
    "message": "There is not enough permitted credit."
  }
}

Common HTTP errors
StatusMeaning
400 / 413 / 415Invalid fields, input or content type; or body too large. Correct the request before retrying.
401 / 403Missing, revoked or unauthorized key; unavailable account; or denied tool access. Check the key’s permissions.
402Insufficient permitted credit. Account credit may be unavailable to a restricted key.
409 / 422Expired or mismatched quote, reused request key with changed input, or conflicting state. Inspect error.code and recover the original request before starting a new job.
429Rate, quote, concurrency or spending limit. Respect Retry-After.
502 / 503Provider or service failure. Check status; an HTTP error alone does not establish whether a request was charged.

Responses containing account or request data use Cache-Control: no-store. Do not log authorization headers or uploaded content. Quotes send the input to Stylometrics; admitted runs submit it to the configured external AI provider. See Privacy and Terms.

← Back to tools