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 API | At a glance |
|---|---|
| Endpoint | POST https://api.sapling.ai/api/v1/aidetect |
| Returns | Document 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 limits | Up to 200,000 characters per request; at least 300 characters recommended |
| Languages | English, Spanish, Portuguese, French, German, Italian, Dutch and Japanese, detected automatically (see Languages) |
| SDKs | Python, JavaScript |
| Pricing | From $0.005 per 1,000 characters, with volume discounts — see API Pricing |
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
- Register for a Sapling account and generate a key from your API settings dashboard. See API Access for the full walkthrough.
POSTyour text tohttps://api.sapling.ai/api/v1/aidetectwith your key, using one of the samples below.- Read
scorefrom the response — closer to1means more confidence the text is AI-generated. Readai_fractionto see how much of a document reads as AI-generated (useful when only part of it is), and usesegments,sentence_scoresortoken_probsto show where in the text that comes from.verdictsums the result up ashuman,mixedorai, andlow_confidencetells you when to hedge it.
Sample Code
- cURL
- JavaScript
- Python
- Python SDK
curl -X POST https://api.sapling.ai/api/v1/aidetect \
-H "Content-Type: application/json" \
-d '{"key":"<api-key>", "text":"This is sample text."}'
import axios from 'axios';
async function run(text) {
try {
const response = await axios.post(
'https://api.sapling.ai/api/v1/aidetect',
{
key: '<api-key>',
text,
},
);
const {status, data} = response;
console.log({status});
console.log(JSON.stringify(data, null, 4));
} catch (err) {
const { msg } = err.response.data;
console.log({err: msg});
}
}
run('This is sample text'); // replace with the text you want to analyze
import requests
from pprint import pprint
response = requests.post(
"https://api.sapling.ai/api/v1/aidetect",
json={
"key": "<api-key>",
"text": "This is sample text."
}
)
if 200 <= response.status_code < 300:
pprint(response.json())
else:
print('Error: ', response.status_code, response.text)
# pip: python -m pip install sapling-py
# uv: uv add sapling-py
from sapling import SaplingClient
from pprint import pprint
api_key ='<api-key>'
client = SaplingClient(api_key=api_key)
detection_scores = client.aidetect('This is sample text.', sent_scores=True)
pprint(detection_scores)
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:
20240606(only available upon request)20251027(previous default; available by specifying this version)20260820(current default)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_fractionfield on every response: the share of the text (from 0 to 1) in sentences scoring0.5or above. The documentscoremeasures how AI-like the text is as a whole, so a document that is half human-written and half AI-generated gets a lowscore.ai_fractionreports roughly0.5for it, so partially AI-written documents are no longer hidden. sentence_scoresnow come from the detector model itself, on the same scale and with the same0.5threshold asscore. Each entry also includesstartandendcharacter offsets intotextfor 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:scoreis0.5or above and at least 80% of the text (ai_fraction≥0.8) scores as AI-generated.mixed: otherwise,scoreis0.5or above or at least 20% of the text (ai_fraction≥0.2) scores as AI-generated. This catches partly AI-written documents whose overallscorestays 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_fractionand sentence scores are useful for review workflows: a document that is mostly human-written with a few AI-generated paragraphs has a low overallscore, but itsai_fractionand the flagged entries insentence_scoresshow how much of it was AI-generated, and where. Consider flagging onai_fraction(for example, above0.3) when partially AI-written submissions matter to you.segmentsgives the same passages ready to highlight.verdictis a ready-made label for display, using a0.5threshold onscoreandai_fractioncut-offs of0.2and0.8. Use your own threshold instead when you need a stricter or looser trade-off than that.low_confidencemarks inputs where false positives are more likely: short text and bullet-list text. When it istrue, 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_confidenceset totrue. - 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
429withoutretry_afterand can't succeed as sent. Use theX-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_retentionare 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,
reportisnull, andreport_skip_reasonsays why:nonpersist(your team keeps no data),disabled(AI detector history is turned off),text_not_retained(history keeps no text),unconfirmed_email,quota,unscorableorerror.
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
- Check the status code of the result and any error logs.
- Unless you're sending very large requests, the requests should rarely fail or time out, but you can follow these instructions to implement a retry mechanism.
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,.htmlor.htm. Uploads are limited to 30MB; a larger file returns413.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 theAuthorizationheader 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
langorversion. -
Billing: billed like
/api/v1/aidetecton 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
filepart is missing - the extension is unsupported
- the document is empty, encrypted, corrupt or has no extractable text
zero_retentioninjsonParamsis 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 inerrorormsg. You can retry it after a short delay. - the
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 theAuthorizationheader).url(required): the page to check, up to 2,048 characters. A bare host and path such asexample.com/blog/postgetshttps://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'sog:title,<title>or first<h1>. For a linked document, this is its file name.source.content_type: the media type from the server'sContent-Typeheader, such astext/htmlorapplication/pdf. A document served with a generic type is still read by its URL extension, so this can beapplication/octet-stream.source.characters: the characters scored, which is what you are billed for.source.truncated:truewhen only a prefix was scored.
Some things to know:
-
Linked documents: a URL that serves a
.pdf,.docx,.pptx,.odt,.txtor.mdfile is read like an upload to/api/v1/file-aidetect. The format is chosen byContent-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
httpandhttpsURLs 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/aidetectinstead. -
Billing: billed like
/api/v1/aidetecton 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/aidetectlimits. 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; orlang,versionorzero_retentionis invalid.422: the page was fetched but can't be scored. It answered with a non-2xx status (many sites answer403to 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
400with 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
textareaorcontenteditablein your web app. - Python SDK — the
sapling-pypackage wraps the endpoint asclient.aidetect(...). - Sapling MCP Server — exposes AI detection as tools to Claude and other
MCP-compatible assistants:
sapling_ai_detectfor text andsapling_ai_detect_urlfor 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.
Does the API return a shareable report link?
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.