Skip to content

Fileverity API docs

Text detections

Classify plain text as AI-written, mixed or human-written with POST /text/detections. Request and response fields, segment offsets, limits and billing.

View as MarkdownUpdated 2026-11-04

POST /text/detections runs one text check. It classifies plain text with fvdoc-3-text and returns the AI-written passages. It is synchronous: the result is in the response.

The examples read the text from a file, so save any passage of 100 words or more as essay.txt first. The Quickstart has a command that writes a sample.

curl https://api.fileverity.com/v1/text/detections \  -H "Authorization: Bearer $FILEVERITY_API_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: 6b9d1c1e-3b0f-4c0a-9a53-2d1c9d6b7e11" \  -d "$(jq -Rs '{text: ., language: "en"}' essay.txt)"

Request body

FieldTypeRequiredDescription
textstringYesPlain text of at least 100 words and at most 100,000 characters.
modelstringNoDefault fvdoc-3-text. Accepts the model ID or its snapshot ID fvdoc-3-text-2026-11-03.
languagestring or nullNoISO 639-1 code: en, fr, de, es, zh. Detected automatically when omitted.

Unknown fields are rejected with invalid_parameter. When the 100-word minimum is checked, Chinese characters count about two to a word.

Response

{  "id": "tdet_8Hk2pQ4vN9sL1xR7cW3mY6zB",  "object": "text.detection",  "created": 1793664000,  "model": "fvdoc-3-text-2026-11-03",  "livemode": true,  "language": "en",  "verdict": "mixed",  "score": 0.97,  "ai_fraction": 0.41,  "segments": [    {      "start": 212,      "end": 433,      "score": 0.97    },    {      "start": 579,      "end": 790,      "score": 0.93    }  ],  "usage": {    "characters": 1412,    "free_characters": 0,    "billed_units": 2  }}
FieldTypeDescription
idstringDetection ID, prefixed tdet_.
objectstringAlways text.detection.
createdintegerUnix timestamp.
modelstringSnapshot that produced the result.
livemodebooleanfalse for test keys.
languagestringThe language used: the one you sent, or the detected one.
verdictstringai: the text reads as AI-written throughout. mixed: AI-written passages within human text. human: no AI-written passage found.
scorenumberFrom 0 to 1. Model score for the most AI-like passage.
ai_fractionnumberFrom 0 to 1. Share of the evaluated text inside AI-written segments.
segmentsarrayThe AI-written passages. Each has start, end (exclusive) and score.
usageobjectcharacters evaluated, free_characters covered by your free start and billed_units actually charged after the free characters.

Text detection returns a statistical estimate, not proof of authorship. Segment boundaries are approximate. Use it as one input to a human review, and do not use it alone to make decisions that affect a person.

Segment offsets

start and end are offsets into the text you submitted, counted in Unicode code points, not bytes and not UTF-16 units. Segment boundaries are approximate and can include adjacent sentences. JavaScript strings index by UTF-16, so split into code points first:

const points = Array.from(text);for (const { start, end } of detection.segments) {  console.log(points.slice(start, end).join(""));}

Python strings already index by code point.

Billing

  • Text is billed per 1,000 characters at US$0.14, counted in Unicode code points. Each request is rounded up to whole units, with a minimum of one unit.
  • Your free-start text characters are used first and reported in usage.free_characters. usage.billed_units is the number of units charged after them, so the cost is billed_units times US$0.14. Example: 1,412 characters with no free characters left bill 2 units, US$0.28.
  • Requests rejected before evaluation are not charged: invalid parameters, text that is too short or too long, unsupported languages and similar. A check that fails inside the service (service_unavailable) is released and not charged.
  • Insufficient funds return insufficient_balance (402).

What is stored

The text is evaluated and discarded. It is never stored, so it cannot be retrieved later.

Fileverity keeps the result without the text (verdict, scores, passage offsets and usage) for 7 days, so the Console can show it and usage can be billed and audited. A retried request with the same Idempotency-Key gets the saved result for 24 hours. Text detections are synchronous: they do not appear in GET /analyses and do not send webhook events. Test keys record nothing. See Data retention and security.

Errors

CodeStatusCause
text_too_short400Fewer than 100 words.
text_too_long413More than 100,000 characters. Split the text into several requests.
unsupported_language400The language is not supported, or could not be detected.
unsupported_media_type415The body is not sent as application/json.
invalid_json400The body is not a JSON object.
missing_parameter400The text field is missing.
invalid_parameter400A field has the wrong type, an unknown field was sent, or the Idempotency-Key is invalid.
model_not_found404The model is unknown.
insufficient_balance402The balance cannot cover the request.
spend_limit_reached402The key reached its monthly spend limit.
idempotency_mismatch, idempotency_in_progress409The Idempotency-Key was used with a different body, or its first request is still running.
service_unavailable, analysis_failed503, 502The detection service is temporarily unavailable or busy. Not charged; retry. service_unavailable carries a retry-after header.

Send an Idempotency-Key so that retrying after a timeout does not bill twice. See Idempotency.