If your product accepts writing from users, someone is going to submit a first draft that a model wrote. TextSight exposes that judgement as a REST call: POST api.textsight.ai/v2/detect returns a verdict, an AI probability and a score for every sentence, as JSON, in one synchronous request. The same key also reaches a lightweight score endpoint and a rewrite endpoint, so a product can flag a draft and offer a fix in the same flow. It is the same engine behind the TextSight web app and the iOS and Android apps. API access starts on Pro.
Most detection APIs hand back a single percentage, which leaves you nothing to render except a number the user cannot argue with. Detect returns the breakdown as well as the verdict.
verdict is human, mixed or ai, which is the value most products actually branch on. ai_probability is a fraction between 0 and 1, so multiply by 100 before you render a percentage. humanization_score runs 0 to 100 in the opposite direction, where higher means more human, and it is the number TextSight surfaces as the Humanization Score. confidence runs 0 to 100 and tells you how much weight the verdict deserves: short inputs come back as a low-confidence mixed rather than a false certainty.
sentences is an array, and each entry carries its own text, a score from 0 to 1, and a label. That array is what lets you highlight the three sentences that triggered a flag instead of showing a bare 82%. In our own product it is the single feature that turns an argument about a score into a conversation about a paragraph. reasoning is a plain-language string you can surface verbatim, and model plus request_id give you something concrete to quote in a support ticket.
Detection is the entry point, but a flag on its own is a dead end for the user holding the draft. POST /v2/score is the cheaper path when you only need the number, for example when you are scoring a queue in the background and will not render a breakdown. POST /v2/rewrite returns a humanized version of the text with the output's own scores attached, which is what a "fix this" button calls. Same base URL, same key, different scope.
Full breakdown: verdict, AI probability, confidence, per-sentence scores and a reasoning string. What you call when a human is about to look at the result.
POST /v2/detect · scope: scanThe lightweight path. Returns the humanization score, the AI probability and a confidence value, with no sentence array. For background queues and bulk triage.
POST /v2/score · scope: scanReturns a humanized rewrite plus the scores of the output, so you can show the user what changed. Optional tone, strength 1 to 5, and a preserve list for citations and names.
POST /v2/rewrite · scope: humanizeThere is no SDK to install and no handshake to implement. Create a key in the dashboard, export it, post some JSON.
GET https://api.textsight.ai/v2 needs no key at all. It returns the current endpoint list and the language caveat, which makes it a useful reachability probe from a new environment or a locked-down CI runner.
Sign in and open API Keys in the dashboard at app.textsight.ai/api-keys. Keys are prefixed sk_live_ and the full value is shown once, so store it as an environment variable at the moment you create it. One note worth knowing before you commit anything: that prefix is shaped like a well-known payment provider's secret keys, so secret scanners on GitHub and elsewhere will flag a TextSight key as a payment credential. Keep it out of the repository the same way you would keep out any other secret.
A detection call is one POST with a JSON body containing text. There is no minimum length, though short inputs are reported honestly as low-confidence rather than guessed at.
curl -X POST https://api.textsight.ai/v2/detect \
-H "Authorization: Bearer $TEXTSIGHT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "The paragraph you want to check."}'const res = await fetch("https://api.textsight.ai/v2/detect", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TEXTSIGHT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ text: draft }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error.code + ": " + data.error.message);
console.log(data.verdict, Math.round(data.ai_probability * 100) + "% AI");import os, requests
res = requests.post(
"https://api.textsight.ai/v2/detect",
headers={"x-api-key": os.environ["TEXTSIGHT_API_KEY"]},
json={"text": draft},
timeout=60,
)
data = res.json()
if not res.ok:
raise RuntimeError(data["error"]["code"], data["error"]["message"])
print(data["verdict"], round(data["ai_probability"] * 100), "% AI")A successful response looks like this. Fields are trimmed for length; sentences carries one entry per sentence.
{
"humanization_score": 28,
"ai_probability": 0.72,
"verdict": "ai",
"confidence": 84,
"reasoning": "(plain-language explanation of the verdict)",
"sentences": [
{ "text": "The paragraph you want to check.", "score": 0.72, "label": "ai" }
],
"model": "ml",
"request_id": "req_8f21c0de-4b17-..."
}The parts of an API that cost integration time are usually auth and error handling. Both are deliberately small here.
Send the key either as Authorization: Bearer sk_live_... or as x-api-key. Both are accepted on every endpoint, so you can match whichever convention your HTTP client or gateway already uses rather than special-casing TextSight.
The scan scope covers detect and score. The humanize scope covers rewrite. Keys are created with scan and read by default, which means a brand new key returns 403 on the rewrite endpoint until you reissue it with the humanize scope. It catches people out, so it is worth checking first when a rewrite call fails and a detect call on the same key succeeds. The split is useful on purpose: a detection-only integration can hold a key that is incapable of spending rewrite quota.
Every error is the same shape, nested under a single key: {"error": {"code": "...", "message": "..."}}. Read error.code, not the HTTP status alone, and never parse the message string. 400 is a bad request body, 401 a missing or invalid key, 403 a scope you do not hold or a quota that is exhausted, 429 the per-minute rate limit, and 503 the detector being temporarily unavailable. A 503 is worth an explicit branch in your code: it means the score is unknown, which is not the same as a low score, and showing the user a zero at that moment would tell them the opposite of the truth.
A monthly call quota and a per-minute rate limit apply together, and both are counted per account rather than per key, so issuing more keys does not raise either ceiling. Metering only fires on a 2xx response: requests rejected with 400, 401, 403, 429 or 503 do not consume the monthly allowance.
| Plan | API calls / month | Requests / minute | Keys |
|---|---|---|---|
| Free | No API access | — | Cannot create keys |
| Starter | No API access | — | Cannot create keys |
| Pro | 5,000 | 60 | Multiple keys, shared quota |
| Business | 10,000 | 120 | Multiple keys, shared quota |
| Enterprise | 50,000 | 240 | Multiple keys, shared quota |
The API is included in the subscription rather than metered separately, so the cost of a busy month is the same as the cost of a quiet one. API access starts on Pro; Free and Starter accounts cannot create keys.
Billed $179.88/year — Save $60
Billed $359.88/year — Save $120
Billed $719.88/year — Save $240
Yearly billing saves 25% on every tier. Need more than 50,000 calls a month? Tell us the volume and we will quote it. View full pricing →
The endpoint is the same in every case. What changes is when you call it and what you do with the answer.
The call goes on the draft, not the publish button. Score in the background while the user writes, and surface the sentence array as inline highlights so the writer can see which lines read as machine-written before anyone else does. If your product already generates text, pairing generation with a detection pass is the honest version of the feature: the user is told how the output reads rather than being promised it is invisible. When a draft comes back flagged, POST /v2/rewrite is the endpoint behind a one-click revise, and it returns the new scores so you can show the movement instead of asserting it.
Submission triggers a detect call, and the result is stored against the submission rather than shown to the student as a pass or fail. The fields that matter here are confidence and the sentences array, because an instructor reviewing a flag needs to see which passages drove it. Two constraints belong in the design from the start: detection is probabilistic and is not proof, and writing by second-language English speakers is more likely to be scored as AI. Build the human review step first, then the API call.
An LMS usually wants the score on an assignment without adding a screen. The pattern is a webhook on submission in your own system that posts the text to /v2/score, stores the number, and only escalates to a full /v2/detect breakdown when a human opens the grading view. That keeps the per-minute rate limit well clear during a deadline spike, when several hundred submissions land in the same ten minutes, and keeps the expensive payload for the moment someone is actually reading it.
Content platforms and agency tooling tend to run this as a pipeline stage rather than a user-facing feature: detect on every draft in the queue, gate publication on a threshold you choose, and route anything above it to a rewrite pass and a re-check. Because quota is per account and pooled across keys, a multi-tenant platform can run one upstream subscription and meter its own customers however it likes. Note the per-minute ceiling when you size a nightly batch: 60 requests a minute on Pro and 120 on Business is the constraint to design the queue around, not the monthly number.
Shorter to read now than to discover in week three of an integration.
Text detection is English-language, and the unauthenticated discovery response says so too. Scores on non-English text are not reliable enough for us to present them as a supported feature, so do not build a multilingual flow on top of this endpoint.
No detector is 100% accurate, ours included, and we will not claim otherwise. A high AI probability is evidence worth a human look, never a finding on its own. If your product uses the output to penalise someone, the review step is your responsibility and it needs to exist.
The live v2 surface is three synchronous endpoints. There is no bulk endpoint, no asynchronous job queue, no streaming response and no official client library today, so high-volume callers loop over the synchronous endpoints inside their per-minute limit. Outbound webhooks are worth a separate note: TextSight does have them on Pro and above, configured in the dashboard, but they fire for scans run through the app and a v2 call does not emit an event. A v2 call returns its result in its own response, which is the only delivery mechanism to design around. If a batch endpoint or a v2 webhook would change your decision, say so in an email and it moves up the list.
The rewrite endpoint runs one synchronous pass so the call returns inside normal request timeouts, which means it can leave some sentences unchanged. The web app iterates further. The response also carries score_reliable: when it is false, the rewrite is still valid but the scores attached to it were produced while the detector was degraded, so do not render them as a confirmed result.
Endpoint-by-endpoint request and response documentation, the full error-code table, and client examples. The contract, in detail.
/api-docs.htmlEvery surface TextSight ships: the REST API, the iOS and Android apps, the web app, and an honest status on what is still being built.
/integrations/A closer look at the rewrite endpoint on its own: tone, strength, the preserve list, and the tradeoff behind the single synchronous pass.
/ai-humanizer-api/Key handling, fair use, prohibited uses, how request data is treated, and the availability and termination terms that apply to the API.
/api-terms/Create a key from the dashboard and make the first call with cURL before you write a line of integration code. API access starts on Pro.