> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.resemble.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.resemble.ai/_mcp/server.

# Detect AI-generated text

POST https://app.resemble.ai/api/v2/text_detect
Content-Type: application/json

Submit a passage of text and classify it as AI-generated or human-written.

Processing is asynchronous by default: the response returns immediately with `status: processing`,
and you can poll `GET /text_detect/{uuid}` or supply a `callback_url` to be notified. Send the
`Prefer: wait` header to block until the verdict is available and receive the completed item in the
same response. Allow a generous client timeout when waiting, since the first request after a period
of inactivity can take several minutes while the model warms up.

Requests are rejected with a 400 when `text` is missing, exceeds 100,000 characters, or contains
fewer than 25 words. Each successfully scored request bills one Text Detection unit; abstained
(`uncertain`) results are not billed.

Reference: https://docs.resemble.ai/api-reference/text-detection/create-text-detection

## Authentication

- `Authorization` header (bearer token, required) — API key from https://app.resemble.ai/hub/api

## Request

### Headers

- `Prefer` (enum, optional) — When set to "wait", the API processes synchronously and returns the completed result.
  - Allowed values: `wait`

### Body (application/json)

This endpoint expects a TextDetectRequest.

- `text` (string, required) — The text to analyze. Must contain at least 25 words (whitespace-delimited) and at most 100,000 characters. Shorter passages are rejected with a 400 because no threshold reliably separates casual human writing from AI-generated text below that length.
- `thinking` (enum, optional, default: low) — How much reasoning effort the detector spends on the passage. Higher levels can improve accuracy on ambiguous text at the cost of latency.
  - Allowed values: `low`, `medium`, `high`
- `threshold` (double, optional) — Probability cutoff used to turn the AI-likelihood score into the `ai` or `human` prediction. Scores at or above the threshold are reported as `ai`.
- `callback_url` (string, optional) — POST destination for the result once processing finishes. The body matches the response of `GET /text_detect/{uuid}` with an additional top-level `error` field when analysis failed.

## Response

### 200

Text detection created (or completed when `Prefer: wait` was sent)

- `success` (boolean, required)
- `item` (TextDetectItem, required)

## Errors

### 400 Bad Request Error

Invalid request: missing text, text over 100,000 characters, fewer than 25 words, invalid `thinking` or `threshold`, or malformed `callback_url`.

- `success` (boolean, optional)
- `error` (string, optional)
- `errors` (map from string to list of string, optional)
- `message` (string, optional)

### 401 Unauthorized Error

Unauthorized

- `success` (boolean, optional)
- `error` (string, optional)
- `errors` (map from string to list of string, optional)
- `message` (string, optional)

### 422 Unprocessable Entity Error

Validation failed

- `success` (boolean, optional)
- `error` (string, optional)
- `errors` (map from string to list of string, optional)
- `message` (string, optional)

## Types

### TextDetectItem

- `uuid` (string, required)
- `status` (enum, required) — `processing` until inference returns, `completed` once `prediction` and `confidence` are populated, or `failed` if the analysis errored.
  - Allowed values: `processing`, `completed`, `failed`
- `created_at` (datetime, required)
- `updated_at` (datetime, required)
- `prediction` (enum, optional, nullable) — Verdict for the passage. `uncertain` means the detector abstained rather than guessing (for example, too few words after normalization); abstained requests are not billed. Null while processing.
  - Allowed values: `ai`, `human`, `uncertain`
- `confidence` (double, optional, nullable) — Confidence in the reported `prediction`, from 0 to 1. Null while processing.
- `text_content` (string, optional, nullable) — The submitted text.

## Examples

**Request**

```json
{
  "text": "The quarterly report suggests that our distribution strategy has matured considerably over the past eighteen months, though several regional markets continue to underperform relative to the projections we shared with the board last spring."
}
```

**Response**

```json
{
  "success": true,
  "item": {
    "uuid": "7f3c1b6e2a9d4f0e8c5b1a2d3e4f5061",
    "status": "processing",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z",
    "prediction": "ai",
    "confidence": 0.9973,
    "text_content": "string"
  }
}
```

**SDK Code**

```python
import requests

url = "https://app.resemble.ai/api/v2/text_detect"

payload = { "text": "The quarterly report suggests that our distribution strategy has matured considerably over the past eighteen months, though several regional markets continue to underperform relative to the projections we shared with the board last spring." }
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://app.resemble.ai/api/v2/text_detect';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"text":"The quarterly report suggests that our distribution strategy has matured considerably over the past eighteen months, though several regional markets continue to underperform relative to the projections we shared with the board last spring."}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://app.resemble.ai/api/v2/text_detect"

	payload := strings.NewReader("{\n  \"text\": \"The quarterly report suggests that our distribution strategy has matured considerably over the past eighteen months, though several regional markets continue to underperform relative to the projections we shared with the board last spring.\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://app.resemble.ai/api/v2/text_detect")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"text\": \"The quarterly report suggests that our distribution strategy has matured considerably over the past eighteen months, though several regional markets continue to underperform relative to the projections we shared with the board last spring.\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://app.resemble.ai/api/v2/text_detect")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"text\": \"The quarterly report suggests that our distribution strategy has matured considerably over the past eighteen months, though several regional markets continue to underperform relative to the projections we shared with the board last spring.\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://app.resemble.ai/api/v2/text_detect', [
  'body' => '{
  "text": "The quarterly report suggests that our distribution strategy has matured considerably over the past eighteen months, though several regional markets continue to underperform relative to the projections we shared with the board last spring."
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://app.resemble.ai/api/v2/text_detect");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"text\": \"The quarterly report suggests that our distribution strategy has matured considerably over the past eighteen months, though several regional markets continue to underperform relative to the projections we shared with the board last spring.\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["text": "The quarterly report suggests that our distribution strategy has matured considerably over the past eighteen months, though several regional markets continue to underperform relative to the projections we shared with the board last spring."] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://app.resemble.ai/api/v2/text_detect")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```