# Winston AI API documentation

> Add Winston AI AI detection, plagiarism checks and AI image detection to your own product: a small set of JSON endpoints over HTTPS.

Canonical page: https://winstonai.ai/api-docs

Every request creates a scan, the same kind you see in your dashboard. Authenticate with a secret API key, send text, and read `human_score` for the overall call and `sentences` for highlights.

## Quick facts

- Base URL: `https://winstonai.com/api/v1`
- Authentication: `Authorization: Bearer <key>`. Keys start with `wai_live_`; create them on the Developers page of the dashboard and keep them on your server (for example in `WINSTONAI_API_KEY`).
- Format: JSON request and response bodies in UTF-8, snake_case field names, ISO 8601 UTC timestamps.
- Versioning: the version is in the path; within v1 fields are only added, never removed or renamed.
- Safe retries: send an `Idempotency-Key` header with any POST; a repeat within 24 hours replays the first response.
- Text per request: 40 to 25,000 words. Images: up to 10 MB.
- Rate limit: 60 requests per minute per key; 10 plagiarism scans running at once per workspace.
- Credits: detection 1 credit per word, plagiarism 2 credits per word, images 300 credits each. GET requests are free.

## Endpoints

| Method | Path | What it does |
| --- | --- | --- |
| POST | `/api/v1/detect` | Detect AI text |
| POST | `/api/v1/plagiarism` | Check plagiarism |
| GET | `/api/v1/scans/{id}` | Retrieve a scan |
| GET | `/api/v1/usage` | Check usage |
| POST | `/api/v1/image` | Detect AI images (coming soon) |

## POST /api/v1/detect

Scores the text and each sentence for how likely it is to be machine-written. Returns a completed scan.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `text` | string | Yes | The text to check, from 40 to 25,000 words. |
| `language` | string | No | ISO 639-1 code such as `en` or `fr`. Detected automatically when left out. |
| `title` | string | No | A name for the scan, shown in your dashboard. Up to 200 characters. |
| `author` | string | No | Who wrote the text. Up to 120 characters. |
| `folder_id` | string | No | Save the scan into one of your dashboard folders. An unknown id fails with `invalid_body`. |
| `sentences` | boolean | No | Include the per-sentence results. Defaults to `true`; set `false` to leave `sentences` out of the result. |
| `metadata` | object | No | Up to 20 string key-value pairs, such as your own submission id. Returned with the scan and in webhooks. |

Example request:

```sh
curl https://winstonai.com/api/v1/detect \
  -H "Authorization: Bearer $WINSTONAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Remote work has become a cornerstone of modern business, offering employees unparalleled flexibility and a better balance between work and home. Moreover, it enables organizations to tap into a diverse, global talent pool. I still miss the office coffee, though, and the chats that only happen by accident."
  }'
```

Example response:

```json
{
  "id": "scn_6f1c2a9e4b7d4c1e9a3f5b8d2e7c4a10",
  "object": "scan",
  "type": "ai_text",
  "status": "completed",
  "credits_used": 48,
  "metadata": {},
  "created_at": "2026-10-18T09:24:11Z",
  "completed_at": "2026-10-18T09:24:12Z",
  "result": {
    "human_score": 47,
    "verdict": "mixed",
    "word_count": 48,
    "sentence_count": 3,
    "readability": 66,
    "language": "en",
    "sentences": [
      {
        "text": "Remote work has become a cornerstone of modern business, offering employees unparalleled flexibility and a better balance between work and home.",
        "ai_probability": 0.755,
        "verdict": "ai"
      },
      {
        "text": "Moreover, it enables organizations to tap into a diverse, global talent pool.",
        "ai_probability": 0.524,
        "verdict": "mixed"
      },
      {
        "text": "I still miss the office coffee, though, and the chats that only happen by accident.",
        "ai_probability": 0.218,
        "verdict": "human"
      }
    ]
  },
  "error": null
}
```

## POST /api/v1/plagiarism

Compares the text against web pages and your library. Runs in the background: the response is a queued scan; fetch it later or use a webhook.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `text` | string | Yes | The text to compare against web pages and your library, from 40 to 25,000 words. |
| `title` | string | No | A name for the scan, shown in your dashboard. Up to 200 characters. |
| `author` | string | No | Who wrote the text. Up to 120 characters. |
| `folder_id` | string | No | Save the scan into one of your dashboard folders. An unknown id fails with `invalid_body`. |
| `exclude_urls` | string[] | No | Sources to ignore, such as the writer's own site. Up to 50 URLs. |
| `exclude_quotes` | boolean | No | Skip text inside quotation marks. Defaults to `true`. |
| `metadata` | object | No | Up to 20 string key-value pairs, such as your own submission id. Returned with the scan and in webhooks. |

Example request:

```sh
curl https://winstonai.com/api/v1/plagiarism \
  -H "Authorization: Bearer $WINSTONAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Remote work has become a cornerstone of modern business, offering employees unparalleled flexibility and a better balance between work and home. Moreover, it enables organizations to tap into a diverse, global talent pool. I still miss the office coffee, though, and the chats that only happen by accident."
  }'
```

Response (queued):

```json
{
  "id": "scn_b3e07d5a1c9f4e2d8a6b0c4f7e1d9a35",
  "object": "scan",
  "type": "plagiarism",
  "status": "queued",
  "credits_used": 96,
  "metadata": {
    "submission_id": "essay-1042"
  },
  "created_at": "2026-10-18T09:26:40Z",
  "completed_at": null,
  "result": null,
  "error": null
}
```

The scan once completed:

```json
{
  "id": "scn_b3e07d5a1c9f4e2d8a6b0c4f7e1d9a35",
  "object": "scan",
  "type": "plagiarism",
  "status": "completed",
  "credits_used": 96,
  "metadata": {
    "submission_id": "essay-1042"
  },
  "created_at": "2026-10-18T09:26:40Z",
  "completed_at": "2026-10-18T09:27:05Z",
  "result": {
    "similarity": 22.9,
    "word_count": 48,
    "matched_words": 11,
    "sources": [
      {
        "url": "https://example.com/blog/remote-hiring-guide",
        "title": "A practical guide to hiring remotely",
        "type": "web",
        "matched_words": 11,
        "percent": 22.9,
        "excerpt": "it enables organizations to tap into a diverse, global talent pool"
      }
    ]
  },
  "error": null
}
```

## GET /api/v1/scans/{id}

Retrieves any scan by id. Free.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes | The scan id returned when you created the scan: `scn_` followed by 32 hex characters. |

```sh
curl https://winstonai.com/api/v1/scans/scn_6f1c2a9e4b7d4c1e9a3f5b8d2e7c4a10 \
  -H "Authorization: Bearer $WINSTONAI_API_KEY"
```

## GET /api/v1/usage

Credit balance, plan and rate limit for the key's workspace. Free.

```sh
curl https://winstonai.com/api/v1/usage \
  -H "Authorization: Bearer $WINSTONAI_API_KEY"
```

```json
{
  "object": "usage",
  "workspace_id": "6f1c2a9e-4b7d-4c1e-9a3f-5b8d2e7c4a10",
  "plan": "advanced",
  "credits": {
    "balance": 154230,
    "monthly_allowance": 200000
  },
  "rate_limit": {
    "limit": 60,
    "remaining": 59,
    "reset": 42
  }
}
```

## POST /api/v1/image (coming soon)

Scores an image for how likely it is to be AI-generated.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | No | A public HTTPS link to a JPEG, PNG, WebP or HEIC image up to 10 MB. Send this or `file`. |
| `file` | file | No | The image itself, sent as `multipart/form-data`. Send this or `url`. |
| `metadata` | object | No | Up to 20 string key-value pairs, such as your own submission id. Returned with the scan and in webhooks. |

```json
{
  "id": "scn_0d8e4f2a7c1b4e9d93a5f6c8b2e0d471",
  "object": "scan",
  "type": "ai_image",
  "status": "completed",
  "credits_used": 300,
  "metadata": {},
  "created_at": "2026-10-18T09:31:02Z",
  "completed_at": "2026-10-18T09:31:04Z",
  "result": {
    "human_score": 4,
    "verdict": "ai",
    "ai_probability": 0.96,
    "format": "png",
    "width": 1024,
    "height": 1024
  },
  "error": null
}
```

## The scan object

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | No | Unique id: `scn_` followed by 32 hex characters. |
| `type` | string | No | `ai_text`, `plagiarism` or `ai_image`. |
| `status` | string | No | `queued`, `processing`, `completed` or `failed`. |
| `credits_used` | integer | No | Credits charged for this scan. Refunded if it fails. |
| `result` | object \| null | No | The findings, once `status` is `completed`. Its shape depends on `type`. |
| `error` | object \| null | No | A `code` and `message`, when `status` is `failed`. |
| `metadata` | object | No | Whatever you sent when creating the scan. |
| `created_at, completed_at` | string | No | ISO 8601 timestamps in UTC. |

## Errors

| Type | HTTP status | Meaning |
| --- | --- | --- |
| `invalid_request_error` | 400, 404, 409 | Something about the request needs to change. |
| `authentication_error` | 401 | The API key is missing or no longer valid. |
| `billing_error` | 402 | The workspace needs more credits. |
| `permission_error` | 403 | The plan doesn't include this check. |
| `rate_limit_error` | 429 | Too many requests in the current minute. |
| `api_error` | 5xx | A problem on our side, or a feature that isn't live yet. |

| Code | Status | Meaning |
| --- | --- | --- |
| `invalid_body` | 400 | The body isn't valid JSON or a field is wrong. `issues` lists each problem with its `path`. |
| `too_short` | 400 | The text has fewer than 40 words. |
| `too_long` | 400 | The text has more than 25,000 words. Split it into parts. |
| `invalid_image` | 400 | The image couldn't be read, or is too large or an unsupported type. |
| `unauthorized` | 401 | The API key is missing, revoked or mistyped. |
| `insufficient_credits` | 402 | Not enough credits. `cost` and `balance` are included. |
| `plan_required` | 403 | Your plan doesn't include this check. Plagiarism and images need Advanced or Team. |
| `not_found` | 404 | No scan with that id exists in your workspace. |
| `idempotency_key_reused` | 409 | This `Idempotency-Key` was already used with a different request body or endpoint. |
| `request_in_progress` | 409 | A request with this `Idempotency-Key` is still running. Retry shortly. |
| `rate_limited` | 429 | Too many requests, or too many plagiarism checks running at once. Wait `retry_after` seconds, also sent as the `Retry-After` header. |
| `server_error` | 500 | Something failed on our side. Safe to retry with the same `Idempotency-Key`. |
| `not_available` | 501 | The endpoint isn't live yet. Today that only applies to `POST /image`. |
| `unavailable` | 503 | Detection is briefly unavailable. Retry with exponential backoff. |

Example error body:

```json
{
  "error": {
    "type": "billing_error",
    "code": "insufficient_credits",
    "message": "This scan needs 2,360 credits and your balance is 420.",
    "cost": 2360,
    "balance": 420,
    "request_id": "req_5bT1kz0Qe2"
  }
}
```

## Webhooks

Webhooks tell your server when a scan finishes or fails, so you don't have to poll. Add an endpoint on the Developers page of the dashboard. Each delivery is signed in the `WinstonAI-Signature` header.

```json
{
  "id": "evt_9sK1mP4rTbX2cQ7vN3wL8yZd",
  "object": "event",
  "type": "scan.completed",
  "created_at": "2026-10-18T09:27:05Z",
  "data": {
    "id": "scn_b3e07d5a1c9f4e2d8a6b0c4f7e1d9a35",
    "object": "scan",
    "type": "plagiarism",
    "status": "completed",
    "credits_used": 96,
    "metadata": {
      "submission_id": "essay-1042"
    },
    "created_at": "2026-10-18T09:26:40Z",
    "completed_at": "2026-10-18T09:27:05Z",
    "result": {
      "similarity": 22.9,
      "word_count": 48,
      "matched_words": 11,
      "sources": [
        {
          "url": "https://example.com/blog/remote-hiring-guide",
          "title": "A practical guide to hiring remotely",
          "type": "web",
          "matched_words": 11,
          "percent": 22.9,
          "excerpt": "it enables organizations to tap into a diverse, global talent pool"
        }
      ]
    },
    "error": null
  }
}
```

## SDKs

Official SDKs are coming. Until then, any HTTP client works; the examples above use only cURL.
