Developer API · v1

Stealthwriter API Documentation

Stealthwriter API Documentation

Introduction

The Stealth Writer API lets you humanize AI-generated text and detect AI-written content from your own applications. Every endpoint accepts a JSON request body and returns a JSON response. All requests are made over HTTPS.

Base URL
https://stealthwriter.io/api/v1
Content-Type application/json
Methods All endpoints use POST.

Authentication

Authenticate every request with your API token, sent as a Bearer token in the Authorization header. The token is tied to your subscription and looks like sk_live_…. Requests with a missing, invalid, or disabled token — or a token that is not attached to an active API subscription — are rejected with 401 Unauthorized.

Authorization header
Authorization: Bearer sk_live_your_api_token
Content-Type: application/json
Need a token? API tokens are issued with a paid API plan. Purchase a plan or contact support to receive your sk_live_… token. Keep it secret — treat it like a password and never expose it in client-side code.

Plan limits & quotas

Your API plan can define several usage limits, each checked on every request. When a limit is reached you receive a 429 Too Many Requests with code: "limit_exceeded"; the reason field tells you exactly which limit was hit so you can handle it programmatically.

LimitApplies toreasonDescription
Words per requestAll endpointswords_per_inputMaximum number of words allowed in a single request's text. Humanize and the detectors each have their own configured value.
Humanizations per day/humanizehumanizations_per_dayMaximum successful humanize calls per calendar day.
AI scans per day/detector/passage + /detector/sentenceai_scans_per_dayMaximum successful detector calls per calendar day, shared across both detector endpoints.
Words per monthAll endpointswords_per_monthMaximum words consumed per calendar month. Humanize keeps its own monthly pool; the two detector endpoints share one.

Daily counters reset at midnight and the monthly word total resets on the first of each month (server time). All counters only include successful (200 OK) requests — calls that fail validation, hit a limit, or error out do not consume your quota.

Limits are opt-in per plan. Only the limits your plan actually configures are enforced. Any limit your plan leaves unset is simply not applied — those requests pass through without that check. When a configured limit is reached you get a 429 with code: "limit_exceeded". Contact support to adjust your plan.
POST /api/v1/humanize

Rewrites AI-generated text so it reads as natural, human-written content. Returns one or more humanized variations of the supplied text.

Limits: subject to words_per_input, humanizations_per_day and words_per_month. Exceeding any configured limit returns 429 limit_exceeded; limits your plan doesn't configure are not enforced. See Plan limits and Errors.

Body parameters

ParameterTypeRequiredDescription
textstringRequiredThe text to humanize.
nintegerRequiredNumber of humanized variations to generate.
levelintegerOptionalHumanization intensity. Higher values rewrite more aggressively.
stylestringOptionalWriting style / tone to apply to the output.

Request

curl -X POST https://stealthwriter.io/api/v1/humanize \
  -H "Authorization: Bearer sk_live_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "The mitochondria is the powerhouse of the cell.",
    "n": 1,
    "level": 2,
    "style": "casual"
  }'
const res = await fetch("https://stealthwriter.io/api/v1/humanize", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_your_api_token",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "The mitochondria is the powerhouse of the cell.",
    n: 1,
    level: 2,
    style: "casual",
  }),
});

const data = await res.json();
$client = new \GuzzleHttp\Client();

$response = $client->post('https://stealthwriter.io/api/v1/humanize', [
    'headers' => [
        'Authorization' => 'Bearer sk_live_your_api_token',
        'Content-Type'  => 'application/json',
    ],
    'json' => [
        'text'  => 'The mitochondria is the powerhouse of the cell.',
        'n'     => 1,
        'level' => 2,
        'style' => 'casual',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Response 200 OK

application/json
{
  "backend": "local",
  "sentences": [
    {
      "index": 0,
      "input_sentence": "The mitochondria is the powerhouse of the cell.",
      "outputs": [
        {
          "text": "Honestly, the mitochondria is what keeps the cell powered up.",
          "readability": 45.29,
          "label": 0
        }
      ]
    }
  ]
}

Response fields are passed through from the Stealth Writer engine and may include additional metadata.

POST /api/v1/detector/passage

Analyzes a passage as a whole and returns the likelihood that it was written by AI.

Limits: subject to words_per_input, words_per_month and the shared ai_scans_per_day quota (counts toward the same daily total as /detector/sentence). Exceeding any configured limit returns 429 limit_exceeded; limits your plan doesn't configure are not enforced. See Plan limits and Errors.

Body parameters

ParameterTypeRequiredDescription
textstringRequiredThe passage to analyze.
detectorstringOptionalDetection sensitivity. One of easy, normal, or strict. Defaults to normal when omitted.

Request

curl -X POST https://stealthwriter.io/api/v1/detector/passage \
  -H "Authorization: Bearer sk_live_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Paste the passage you want to analyze here.",
    "detector": "normal"
  }'
const res = await fetch("https://stealthwriter.io/api/v1/detector/passage", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_your_api_token",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "Paste the passage you want to analyze here.",
    detector: "normal",
  }),
});

const data = await res.json();
$client = new \GuzzleHttp\Client();

$response = $client->post('https://stealthwriter.io/api/v1/detector/passage', [
    'headers' => [
        'Authorization' => 'Bearer sk_live_your_api_token',
        'Content-Type'  => 'application/json',
    ],
    'json' => [
        'text'     => 'Paste the passage you want to analyze here.',
        'detector' => 'normal',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Response 200 OK

application/json
{
  "ai_prob": 0.9881510734558105,
  "human_prob": 0.0118489945307374,
  "label": "AI"
}

ai_prob and human_prob are floats between 0 and 1 (not percentages) and sum to 1. label is the engine's verdict for the passage as a whole.

POST /api/v1/detector/sentence

Breaks the text into sentences and returns a per-sentence AI/human probability and label — useful for highlighting exactly which parts read as AI-generated.

Limits: subject to words_per_input, words_per_month and the shared ai_scans_per_day quota (counts toward the same daily total as /detector/passage). Exceeding any configured limit returns 429 limit_exceeded; limits your plan doesn't configure are not enforced. See Plan limits and Errors.

Body parameters

ParameterTypeRequiredDescription
textstringRequiredThe text to analyze sentence by sentence.
detectorstringOptionalDetection sensitivity. One of easy, normal, or strict.

Request

curl -X POST https://stealthwriter.io/api/v1/detector/sentence \
  -H "Authorization: Bearer sk_live_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "First sentence. Second sentence to check.",
    "detector": "normal"
  }'
const res = await fetch("https://stealthwriter.io/api/v1/detector/sentence", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_your_api_token",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "First sentence. Second sentence to check.",
    detector: "normal",
  }),
});

const data = await res.json();
$client = new \GuzzleHttp\Client();

$response = $client->post('https://stealthwriter.io/api/v1/detector/sentence', [
    'headers' => [
        'Authorization' => 'Bearer sk_live_your_api_token',
        'Content-Type'  => 'application/json',
    ],
    'json' => [
        'text'     => 'First sentence. Second sentence to check.',
        'detector' => 'normal',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Response 200 OK

application/json
[
  { "label": "Human" },
  { "label": "AI" }
]

The response is a bare JSON array of per-sentence verdicts, in the order the sentences appear in text. Each item carries only a label; the sentence text itself is not echoed back.

Sentences shorter than 5 words are skipped — the engine will not score them. The array is therefore often shorter than your sentence count, and is [] when every sentence is too short (a 200 OK with an empty array is a valid result, not an error). Do not assume item n corresponds to your n-th sentence.

The detector value (easy / normal / strict) controls how aggressively sentences are flagged.

Errors

Errors come back with the matching HTTP status code in one of two JSON shapes. Most errors use a consistent envelope with success, code, reason and message. Body-validation errors (422) use Laravel's standard message + errors shape instead.

Standard error envelope

Error shape
{
  "success": false,
  "code": "limit_exceeded",
  "reason": "words_per_input",
  "message": "This request has 5200 words, which exceeds your plan limit of 5000 words per request."
}

Examples

401 Unauthorized — missing, invalid, or disabled token:

application/json
{
  "success": false,
  "code": "unauthorized",
  "reason": "invalid-api-token",
  "message": "The API token is invalid or has been disabled."
}

429 Too Many Requests — the request would exceed your monthly word allowance:

application/json
{
  "success": false,
  "code": "limit_exceeded",
  "reason": "words_per_month",
  "message": "This request's 800 words would exceed your monthly limit of 50000 words (you have used 49500 this month)."
}

429 Too Many Requests — a configured plan limit was reached:

application/json
{
  "success": false,
  "code": "limit_exceeded",
  "reason": "ai_scans_per_day",
  "message": "You have reached your daily limit of 500 AI scans. Try again tomorrow."
}

422 Unprocessable — body validation failed (note the different shape):

application/json
{
  "message": "The text parameter is required.",
  "errors": {
    "text": ["The text parameter is required."],
    "n": ["The n parameter is required."]
  }
}

Status reference

StatuscodereasonMeaning
401unauthorizedinvalid-api-tokenMissing, invalid, or disabled token, or a token not attached to an active API subscription.
422n/asee errorsRequest body failed validation. Uses the message + errors shape.
429limit_exceededwords_per_input · words_per_month · humanizations_per_day · ai_scans_per_dayA configured plan limit was reached (per-request words, monthly words, or a per-day quota).
429upstream_error429The engine is receiving too many requests — slow down and retry shortly.
502upstream_error / invalid_upstream_responserequest-failed / non-jsonThe Stealth Writer engine returned an error or an invalid response.
503service_not_configuredmissing-api-configThe service is not configured. Contact support.
504upstream_timeouttimeoutThe engine did not respond in time. Retry the request.