Skip to main content

AI Detector API

Sapling's AI detector API computes the probability that a piece of text is AI-generated (sometimes called "AI slop"), as well as the probability that each constituent sentence and token is AI-generated. Use this AI detection API to flag machine-generated content in user submissions, marketplace listings, reviews, job applications, student work, and content pipelines — or as an AI slop detector API for identifying low-quality, machine-generated content.

The system is trained to be able to handle LLMs from different vendors, such as OpenAI's GPT family of models, Google's Gemini models, Anthropic's Claude models, and the open-release Llama and Mistral models. It is also somewhat robust to small changes and noisy text.

AI detection APIAt a glance
EndpointPOST https://api.sapling.ai/api/v1/aidetect
ReturnsDocument score from 0 to 1, the fraction of the text that reads as AI-generated, per-sentence scores with offsets, per-token probabilities, and an optional HTML heatmap
Input limitsUp to 200,000 characters per request; at least 300 characters recommended
LanguagesEnglish, Spanish, Portuguese, French, German, Italian, Dutch and Japanese, detected automatically (see Languages)
SDKsPython, JavaScript
PricingFrom $0.005 per 1,000 characters, with volume discounts — see API Pricing

Run Detector

Accuracy and Trade-offs

All AI detection systems have false positives and false negatives. In some cases, small modifications to AI-generated text can cause that text to no longer be flagged as AI-generated. In other cases, human-written (but perhaps rote) text can be misclassified as AI-generated. Please do not interpret this as conclusive belief that your text is AI slop. Depending on the application, false positives or false negatives may be less desirable. Contact us for ways to adjust for your use case.

Quickstart​

  1. Register for a Sapling account and generate a key from your API settings dashboard. See API Access for the full walkthrough.
  2. POST your text to https://api.sapling.ai/api/v1/aidetect with your key, using one of the samples below.
  3. Read score from the response — closer to 1 means more confidence the text is AI-generated. Read ai_fraction to see how much of a document reads as AI-generated (useful when only part of it is), and use segments, sentence_scores or token_probs to show where in the text that comes from. verdict sums the result up as human, mixed or ai, and low_confidence tells you when to hedge it.

Sample Code​

curl -X POST https://api.sapling.ai/api/v1/aidetect \
-H "Content-Type: application/json" \
-d '{"key":"<api-key>", "text":"This is sample text."}'
AI Detector UI integration

Sapling's Javascript SDK provides a complete end-to-end UI integration for AI content detection capabilities. Head over to our AI Detect JavaScript Quickstart for more details.

AI Detector POST​

Request Parameters​

https://api.sapling.ai/api/v1/aidetect

HTTP method: POST

The AI Detector API POST endpoint takes JSON parameters documented below:

key: String
32-character API key. Can also be supplied via the Authorization header as a bearer token; if both are provided, the key parameter takes precedence.

text: String
Text to run detection on. The limit is currently 200,000 characters. If latency is high or requests time out, we recommend adapting this script. Please contact us if you need to run the system on longer inputs. We can also provide suggestions on how to chunk your text into smaller pieces and then combine detection results.

Provide exactly one of text and texts.

texts: List[String]
Batch form (see Batch Requests): 1–10 texts to run detection on in one request, each scored independently under the same options.

sent_scores: Boolean
Whether to return sentence scores. Defaults to true. Sentence scores are computed from the same model pass as the document score, so they add almost no latency.

score_string: Boolean
Whether to return string highlighting token-level scores. Defaults to false. This allows you to visualize which portions of the text are likely AI-generated similar to on Sapling's AI detector page.

lang: String
Optional language of the text: en, es, pt, fr, de, it, nl or ja (region subtags such as pt-BR are accepted). Omit it, or pass auto, to detect the language automatically (see Languages). Setting it picks that language's model instead of relying on detection, which helps with short texts. Unsupported languages return a 400 error. If version is also set, version wins.

version: String
There are currently 4 versions of the detector available:

  1. 20240606 (only available upon request)
  2. 20251027 (previous default; available by specifying this version)
  3. 20260820 (current default)
  4. ml20261009 (multilingual model for English, Spanish, Portuguese, French, German, Italian, Dutch and Japanese)

Requests without version are routed by language: text detected as one of the supported non-English languages is scored by ml20261009, everything else by the default model. Pass lang to specify the language explicitly. Specifying a version turns this routing off, and every text uses that model.

zero_retention: Boolean
Optional. Set it to true to keep no copy of the text. See Data retention. Defaults to false; it is always on for teams with data retention disabled. A non-boolean value returns a 400 error. In a batch request it applies to every item.

create_report: Boolean
Optional. Set it to true to also save the result as a shareable report and get its link in report. See Shareable reports. Single text requests only; it can't be combined with zero_retention or texts (400). It needs your private API key: OAuth access tokens and public API keys get a 400.

September 17, 2026 default-model upgrade​

The default AI Detector model was upgraded to version 20260820 on September 17, 2026.

The upgrade brings higher precision on modern text and better recall on humanized AI text. In our internal held-out evaluations, human-text false positives fell by 60% on the modern-text set, while detection of humanized AI text improved by 3.9% absolute. We've also refreshed training data from recent ChatGPT, Claude, Gemini, DeepSeek, and Qwen models, and added Kimi coverage.

Requests that omit the version parameter and aren't routed to the multilingual model use the new model. To keep using the previous model, include "version": "20251027" in each request.

October 2026 update: ai_fraction and model-based sentence scores​

Two changes to the response, with no change to the request or to score:

  • New ai_fraction field on every response: the share of the text (from 0 to 1) in sentences scoring 0.5 or above. The document score measures how AI-like the text is as a whole, so a document that is half human-written and half AI-generated gets a low score. ai_fraction reports roughly 0.5 for it, so partially AI-written documents are no longer hidden.
  • sentence_scores now come from the detector model itself, on the same scale and with the same 0.5 threshold as score. Each entry also includes start and end character offsets into text for highlighting. Previously, sentence scores were computed by a separate method and could disagree with the overall score. Sentence boundaries may also differ slightly from before.

Requests with default options are also faster, since sentence scoring no longer needs a second model pass.

Response Parameters​

The AI Detector POST endpoint returns JSON of the following format:

{
"score": 0.8016229165451867,
"ai_fraction": 1.0,
"verdict": "ai",
"segments": [
{
"start": 0,
"end": 19,
"score": 0.8016
}
],
"low_confidence": true,
"low_confidence_reasons": ["short_text"],
"sentence_scores": [
{
"score": 0.8016229165451867,
"sentence": "Here is a sentence.",
"start": 0,
"end": 19
}
],
"text": "Here is a sentence.",
"token_probs": [
0.8062431365251541,
0.8068526238203049,
0.8062431365251541,
0.8080672174692154,
0.8062431365251541
],
"tokens": [
"Here",
" is",
" a",
" sentence",
"."
],
"version": "..."
}

A score from 0 to 1 will be returned, with 0 indicating the maximum confidence that the text is human-written, and 1 indicating the maximum confidence that the text is AI-generated.

ai_fraction: The fraction of the text, from 0 to 1, in sentences whose score is 0.5 or above (weighted by characters, ignoring whitespace). Use it alongside score for documents that may be only partly AI-generated: a document with one AI-generated paragraph out of two has a low score but an ai_fraction near 0.5. It is null in the rare case that no sentence could be scored.

verdict: A plain-language summary of score and ai_fraction, one of:

  • ai: score is 0.5 or above and at least 80% of the text (ai_fraction ≥ 0.8) scores as AI-generated.
  • mixed: otherwise, score is 0.5 or above or at least 20% of the text (ai_fraction ≥ 0.2) scores as AI-generated. This catches partly AI-written documents whose overall score stays low.
  • human: everything else.

When ai_fraction is null, the verdict is ai or human by score alone. verdict is omitted if the text could not be scored.

segments: The AI-generated passages: each run of consecutive sentences scoring 0.5 or above, as an object such as {"start": 0, "end": 183, "score": 0.985}, in text order. start/end are character offsets into text counted like the sentence_scores offsets below, and score is the run's sentence scores averaged by length. It is an empty list when no sentence scores 0.5 or above. Segments are returned even with sent_scores set to false, so you can highlight AI-generated passages without the per-sentence list. They are omitted when sentence offsets are unavailable.

low_confidence: true when the input is of a kind where human-written text is more often misread as AI-generated, so the result should be shown with a caveat rather than as a bare score. low_confidence_reasons lists why (empty when low_confidence is false):

  • short_text: the text is under 500 characters.
  • list_format: most of the text is bullet or numbered lines.

If score_string is set to true, a score_string field will be provided. The field contains an HTML string with a heatmap of the portions of the text that are predicted to be AI-generated. If the default score string is not what you desire, you can generate your own using tokens and token_probs.

If sent_scores is true (the default), a field sentence_scores containing scores for each sentence will also be returned. Each entry contains the sentence, its score (which uses the same model, scale, and 0.5 threshold as the overall score), and the start/end character offsets of the sentence in text, so text[start:end] equals sentence (end is exclusive). Offsets count Unicode code points (Python str indexing), not UTF-16 code units or bytes. In JavaScript, slice with Array.from(text) if the input may contain characters outside the Basic Multilingual Plane (e.g. emoji), whose UTF-16 string indices would otherwise misalign. The overall score is computed over the whole text at once, so it is not the plain average of the sentence scores.

tokens: List of tokens from backend tokenizer that can be used to token_probs to visualize the output prediction per token.

token_probs: List of probabilities that each token is AI-generated. This can be used with tokens to visualize the output prediction per token.

version: Identifies the detector that scored the text: the version you requested or, if you didn't pin one, the model the text was routed to (the current default model, or ml20261009 for supported non-English text). Treat it as an opaque string, and log it if you need to know which model produced a stored result.

language (optional): When the text's language is identified with high confidence, an object such as {"code": "es", "name": "Spanish", "confidence": 0.98}. Use it with version to see which model scored the text, or to flag results for unsupported languages. It is omitted when the language can't be identified confidently, so don't rely on it being present.

Batch Requests​

To score many texts — a queue of submissions, a page of reviews — in one request, send texts (a list of 1–10 strings) instead of text. The same sent_scores, score_string, and version options, as well as lang (if set), apply to every item. Exactly one of text and texts must be provided, and the combined length of all items may be up to 200,000 characters (the same cap as a single text).

curl -X POST https://api.sapling.ai/api/v1/aidetect \
-H "Content-Type: application/json" \
-d '{"key":"<api-key>", "texts": ["First submission to check.", "Second submission to check."], "sent_scores": false}'

The batch response is {"results": [...]} with one entry per input, in input order; each entry has exactly the single-response shape above (sentence_scores, tokens, and token_probs are relative to that item's own text):

{
"results": [
{"score": 0.93, "ai_fraction": 1.0, "verdict": "ai", "segments": [{"start": 0, "end": 26, "score": 0.93}], "low_confidence": true, "low_confidence_reasons": ["short_text"], "text": "First submission to check.", "tokens": ["..."], "token_probs": [0.9], "version": "..."},
{"score": 0.04, "ai_fraction": 0.0, "verdict": "human", "segments": [], "low_confidence": true, "low_confidence_reasons": ["short_text"], "text": "Second submission to check.", "tokens": ["..."], "token_probs": [0.1], "version": "..."}
]
}

Each item is scored independently and billed individually (a batch of N costs the same as N single requests, and identical items are billed once). If any item fails, the whole request returns an error and nothing is billed; items that did resolve are cached, so a retry only re-scores the rest. The batch form requires an API key.

Interpreting the score​

score is a probability estimate, not a decision. How you turn it into one depends on which kind of mistake is more costly in your application:

  • A high threshold (for example, only act above 0.9) minimizes false accusations, at the cost of letting more AI-generated text through. This is usually the right default when a flag has consequences for a person — moderation, admissions, hiring.
  • A lower threshold surfaces more candidates for human review. This suits content pipelines where a flag just means "route to an editor".
  • ai_fraction and sentence scores are useful for review workflows: a document that is mostly human-written with a few AI-generated paragraphs has a low overall score, but its ai_fraction and the flagged entries in sentence_scores show how much of it was AI-generated, and where. Consider flagging on ai_fraction (for example, above 0.3) when partially AI-written submissions matter to you. segments gives the same passages ready to highlight.
  • verdict is a ready-made label for display, using a 0.5 threshold on score and ai_fraction cut-offs of 0.2 and 0.8. Use your own threshold instead when you need a stricter or looser trade-off than that.
  • low_confidence marks inputs where false positives are more likely: short text and bullet-list text. When it is true, show the result with a caveat or ask for more text rather than acting on the score alone.

Contact us if you need help calibrating thresholds against your own labelled data.

Languages​

The detector supports English, Spanish, Portuguese, French, German, Italian, Dutch and Japanese.

Sapling identifies the language of each text (from its first 20,000 characters) and, when it is confident (above 0.9) and no version is pinned, scores Spanish, Portuguese, French, German, Italian, Dutch and Japanese text with a multilingual model trained on human-written and AI-generated text. Italian and Dutch are newer: the model has no Italian or Dutch training data yet, so in Sapling's evaluation it flags about as little human-written text as in the other languages but catches somewhat less AI-generated text. The detected language is returned in the language field and the model in version. Without a pinned version, a document that mixes languages is routed by its dominant language. If you already know the language, pass it as lang to use that language's model directly.

If you don't pin a version, text in other languages, or text whose language can't be identified confidently, is scored by the English model. Those scores are less reliable, so treat them with more caution.

To try the detector in a browser, use the English, Spanish, Portuguese, French, German, Italian or Japanese detector page.

Limits, quotas, and pricing​

  • Input length: up to 200,000 characters per request. At least 300 characters is recommended — it is possible to classify shorter text, but there is much less signal to work with. Results on text under 500 characters have low_confidence set to true.
  • Free quota: new API keys get 50,000 characters/day and 250,000 characters/month. See Usage and Pricing.
  • Rate limits: each API key has its own character allowance for this endpoint, refilled continuously: 50,000 characters every 24 hours for free-trial keys and, by default, 10 million characters every 24 hours for subscribed keys. The monthly quota applies on top. A single request longer than the key's whole allowance gets a 429 without retry_after and can't succeed as sent. Use the X-RateLimit-* response headers to pace batch jobs. See Rate Limits.
  • Caching: AI detection results are cached on the entire text for 3 days, so re-sending an identical document does not incur additional usage. See API Pricing. Requests with zero_retention are not cached.
  • Billing: usage-based, per character, starting at $0.005 per 1,000 characters with volume discounts. See API Pricing.

Data retention​

Send "zero_retention": true to score a text without Sapling keeping a copy of it. The request is scored and billed as usual, but:

  • Not cached: the result is not written to the 3-day result cache. If someone else's request already cached the identical text, that cached result can still be returned.
  • No stored text in usage records: usage is logged without the text. A hash of the text is kept, so sending it again within the deduplication window is still not billed twice.
  • No samples or history: the request is never sampled for quality review or saved to detector history.

It works on every AI detection endpoint: /api/v1/aidetect (single and batch), /api/v1/url-aidetect and /api/v1/file-aidetect, which takes it in jsonParams. Teams with data retention disabled get this behavior on every request, including checks from the Sapling extensions and the Word add-in, without setting the flag. For retention policies across all of Sapling, see Data Processing.

curl -X POST https://api.sapling.ai/api/v1/aidetect \
-H "Content-Type: application/json" \
-d '{"key": "<api-key>", "text": "<text>", "zero_retention": true}'

Shareable reports​

Send "create_report": true to save a result as a report you can share, like the links the AI detector dashboard creates. Alongside the usual fields, the response then carries:

{
"report": {
"id": "3f0c...",
"url": "https://sapling.ai/ai-detector/report/<token>",
"shared_at": "2026-10-27T12:00:00Z",
"expires_at": "2027-01-25T12:00:00Z"
}
}

Anyone with url can view the scored text, its overall score, verdict and highlighted passages, without signing in. The page never shows your name, email or API key.

  • Where it lives: the report is saved to your AI detector history (source "API"), where you can revoke the link or delete the entry. It counts toward your history limits; when history is full, the oldest entries and their links are removed first.
  • How long it lasts: until expires_at, 90 days after it was created, unless you revoke the link, delete the entry, or turn off text retention for your history sooner. Cancelling a plan doesn't revoke links, but history above the free plan's limit (100 entries) is removed, oldest first, the next time a check is saved.
  • When no report is made: the request is still scored and billed, report is null, and report_skip_reason says why: nonpersist (your team keeps no data), disabled (AI detector history is turned off), text_not_retained (history keeps no text), unconfirmed_email, quota, unscorable or error.

It works on /api/v1/aidetect (single text), /api/v1/url-aidetect and /api/v1/file-aidetect, which takes it in jsonParams. Because a report keeps the text, create_report can't be combined with zero_retention. Because it publishes a link from your history, it needs your private API key, sent from a server. OAuth access tokens (such as an MCP client's) and public API keys get a 400 and nothing is scored.

curl -X POST https://api.sapling.ai/api/v1/aidetect \
-H "Content-Type: application/json" \
-d '{"key": "<api-key>", "text": "<text>", "create_report": true}'

Tips​

Checking Files​

To score a document without extracting its text yourself, upload it to the file endpoint. It reads the document's text and scores it as if you had sent that text to /api/v1/aidetect with sent_scores and score_string enabled.

POST to https://api.sapling.ai/api/v1/file-aidetect as multipart/form-data.

  • file (required): the document. The format is chosen by file extension: .pdf, .docx, .pptx, .odt, .txt, .md, .html or .htm. Uploads are limited to 30MB; a larger file returns 413.
  • jsonParams: a JSON object holding your key, {"key": "<api-key>"}, sent as a file part or as a plain form field. You can send the key as a bearer token in the Authorization header instead. It can also hold "zero_retention": true (see Data retention) or "create_report": true (see Shareable reports).
curl -X POST https://api.sapling.ai/api/v1/file-aidetect \
-F "file=@essay.pdf" \
-F 'jsonParams={"key":"<api-key>"}'
import json
import requests

with open("essay.pdf", "rb") as f:
response = requests.post(
"https://api.sapling.ai/api/v1/file-aidetect",
files={"file": f},
data={"jsonParams": json.dumps({"key": "<api-key>"})},
)
print(response.status_code, response.json())

The response has the same fields as a /api/v1/aidetect response, including score, verdict, segments, low_confidence, sentence_scores, score_string and language. Some things to know:

  • Text read: only the first 200,000 characters of extracted text are scored.

  • What is extracted:

    • PPTX: slides are read in order; speaker notes are skipped.
    • ODT: footnotes, comments and tracked deletions are dropped.
    • HTML: only the visible text is read.
  • Language: the document's language is detected and routed exactly as for text requests (see Languages). This endpoint does not take lang or version.

  • Billing: billed like /api/v1/aidetect on the extracted text.

  • Rate limit: 60 requests per minute per account. The limit is shared with the other upload endpoints.

  • Errors: file problems come back as 400 {"error": "<reason>"}:

    • the file part is missing
    • the extension is unsupported
    • the document is empty, encrypted, corrupt or has no extractable text
    • zero_retention in jsonParams is not a boolean

    A request with no key at all also returns 400 {"error": "Unauthenticated"}.

    A transient scoring failure is also a 400, with an "Unexpected error" message in error or msg. You can retry it after a short delay.

The older /api/v1/pdf-aidetect and /api/v1/docx-aidetect routes still work, but new integrations should use /api/v1/file-aidetect. To pull typed fields out of a document instead of scoring it, see Data Extraction.

Checking Web Pages​

To score a public web page, send its URL. Sapling fetches the page, keeps its main readable text and scores that text like a /api/v1/aidetect request. Navigation, headers, footers, forms and hidden elements are dropped, and the largest <article> (else <main>) is preferred, so the cookie banners and footer links you would get by scraping the raw HTML yourself are neither scored nor billed.

POST to https://api.sapling.ai/api/v1/url-aidetect with a JSON body:

  • key: your API key (or send it as a bearer token in the Authorization header).
  • url (required): the page to check, up to 2,048 characters. A bare host and path such as example.com/blog/post gets https:// added.
  • sent_scores, score_string, version, lang, zero_retention, create_report: the same options as /api/v1/aidetect.
curl -X POST https://api.sapling.ai/api/v1/url-aidetect \
-H "Content-Type: application/json" \
-d '{"key": "<api-key>", "url": "https://example.com/blog/post", "sent_scores": false}'
import requests

response = requests.post(
"https://api.sapling.ai/api/v1/url-aidetect",
json={"key": "<api-key>", "url": "https://example.com/blog/post", "sent_scores": False},
timeout=60,
)
print(response.status_code, response.json())

The response has the same fields as a /api/v1/aidetect response, plus a source object describing what was scored:

{
"score": 0.93,
"ai_fraction": 0.88,
"verdict": "ai",
"segments": [{"start": 0, "end": 4810, "score": 0.97}],
"low_confidence": false,
"low_confidence_reasons": [],
"text": "...",
"version": "...",
"source": {
"url": "https://example.com/blog/post",
"title": "Example post",
"content_type": "text/html",
"characters": 5120,
"truncated": false
}
}
  • source.url: the final URL after redirects.
  • source.title: the page's og:title, <title> or first <h1>. For a linked document, this is its file name.
  • source.content_type: the media type from the server's Content-Type header, such as text/html or application/pdf. A document served with a generic type is still read by its URL extension, so this can be application/octet-stream.
  • source.characters: the characters scored, which is what you are billed for.
  • source.truncated: true when only a prefix was scored.

Some things to know:

  • Linked documents: a URL that serves a .pdf, .docx, .pptx, .odt, .txt or .md file is read like an upload to /api/v1/file-aidetect. The format is chosen by Content-Type, or by the URL's extension when the server sends a generic type.

  • Size limits: the first 2MB of an HTML page are read, a document over 15MB is rejected, and only the first 200,000 characters of text are scored.

  • What can be fetched: only http and https URLs on ports 80, 443, 8080 or 8443 that resolve to public addresses. Up to 5 redirects are followed, and each one is checked the same way. Pages behind a login or paywall can't be read: send their text to /api/v1/aidetect instead.

  • Billing: billed like /api/v1/aidetect on the characters scored. Results are cached on the scored text, so checking an unchanged page again does not use more quota.

  • Rate limits: 30 URL checks per minute per account, on top of the /api/v1/aidetect limits. URL checks share /api/v1/aidetect's character allowance, and each check counts at least 5,000 characters against it.

  • Errors (all {"msg": "<reason>"}):

    • 400: the URL is malformed, isn't allowed (wrong scheme or port, or a host that doesn't resolve to a public address), or redirects too many times; or lang, version or zero_retention is invalid.
    • 422: the page was fetched but can't be scored. It answered with a non-2xx status (many sites answer 403 to automated requests), has an unsupported content type, is an oversized or unreadable document, or has no readable text.
    • 502: the page couldn't be fetched (a timeout or connection failure). You can retry it later.

    As with text requests, a transient scoring failure is a 400 with an "Unexpected error" message; retry it after a short delay.

From an AI assistant, the sapling_ai_detect_url tool on the Sapling MCP server makes the same request and returns the same fields. A page that can't be fetched or scored comes back as a tool error carrying the reason, not as a score.

Other ways to use Sapling's AI detection​

Beyond calling the HTTP API directly, the same detector is available through:

  • JavaScript SDK — a drop-in UI that adds an AI detection button to any textarea or contenteditable in your web app.
  • Python SDK — the sapling-py package wraps the endpoint as client.aidetect(...).
  • Sapling MCP Server — exposes AI detection as tools to Claude and other MCP-compatible assistants: sapling_ai_detect for text and sapling_ai_detect_url for a web page.
  • Sapling's AI content detector — the hosted web app, useful for spot checks and for comparing against your own integration.

If you are still evaluating vendors, Sapling maintains a comparison of the top AI detection APIs.

Frequently asked questions​

What is an AI detection API?​

An AI detection API is an HTTP endpoint that takes text as input and returns a score estimating how likely that text was generated by a large language model rather than written by a person. Sapling's AI detector API returns a document-level score from 0 to 1, plus optional per-sentence and per-token scores so you can see which passages drive the result.

Which AI models can the detector identify?​

The detector is trained across LLMs from different vendors, including OpenAI's GPT family, Google's Gemini models, Anthropic's Claude models, and the open-release Llama and Mistral models. It does not name which model produced the text; it returns the probability that the text is AI-generated.

How much text does the AI detector API need?​

At least 300 characters is recommended. Shorter inputs carry much less signal, so scores on them are less reliable, and results on text under 500 characters come back with low_confidence set to true. A single request accepts up to 200,000 characters.

How accurate is AI detection?​

Every AI detection system has false positives and false negatives. Small edits to AI-generated text can keep it from being flagged, and human-written but rote text is sometimes misclassified as AI-generated. Treat the score as a signal to review rather than as proof, and contact us to tune thresholds for your use case.

Does the AI detector API support languages other than English?​

Yes: English, Spanish, Portuguese, French, German, Italian, Dutch and Japanese. The language is detected automatically and, unless you pin a version, text in the supported non-English languages is scored by a multilingual model (it has no Italian or Dutch training data yet, so it catches somewhat less AI-generated text in those two). Unless you pin a version, text in other languages is scored by the English model and is less reliable; the response's language field tells you which language was detected. See Languages.

Can I run AI detection on PDF or DOCX files?​

Yes. Upload the file to /api/v1/file-aidetect. It accepts PDF, DOCX, PPTX, ODT, TXT, Markdown and HTML documents up to 30MB and scores the extracted text like a text request.

Can I check a web page by URL?​

Yes. Send the URL to /api/v1/url-aidetect. Sapling fetches the page, drops navigation, headers, footers and other boilerplate, and scores the main text like a text request. The response's source field reports the final URL, page title and characters scored.

Does Sapling store the text I send to the AI detector?​

Not if you ask it not to. Add "zero_retention": true to a request and the text is scored and billed but not cached, usage is logged without the text, and nothing is saved to detector history. Teams with data retention disabled get this on every request. See Data retention.

Is there a free tier for the AI detection API?​

Yes. A new API key comes with a free quota of 50,000 characters per day and 250,000 characters per month, which is enough to evaluate the detector before subscribing. This quota is for trial keys only: subscribing raises a key's limits to 10 million characters per day and 25 million per month by default (see Rate Limits). Paid usage is billed per character with volume discounts — see API Pricing.

Yes, if you ask for one. Set "create_report": true on a single-text request sent with your private API key, and the response's report field holds a link to a shareable report saved to your AI detector history. Without create_report, API responses are returned as JSON with no shareable link. See Shareable reports.

Does the website AI detector use the same model as the API?​

Yes. The sapling.ai AI detector always uses the current default model: the same one an API request gets when it omits version, including the routing of supported non-English text to the multilingual model. Results differ only if an API request pins a different version. The website's per-check length limit depends on your plan; see Pricing.