API Reference

Documentation

Everything you need to integrate the HumanizeThisAI API. Authentication, endpoints, error handling, rate limits, and working code examples.

Quick Start

Three steps to your first humanized output.

1

Get your API key

Sign up, subscribe to a plan, and generate a key from your dashboard.

2

Send a request

POST to /v1/humanize with your text, mode, and tone.

3

Handle the response

Parse the JSON response and use the humanized text.

Request

bash
curl -X POST https://api.humanizethisai.com/v1/humanize \
  -H "Authorization: Bearer htai_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -d '{
    "text": "Artificial intelligence has revolutionized the way we interact with technology.",
    "mode": "standard",
    "tone": "casual"
  }'

Response

json
{
  "object": "humanization",
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "humanized_text": "AI has completely changed how we deal with tech day to day.",
  "input_words": 12,
  "output_words": 11,
  "mode": "standard",
  "tone": "casual",
  "language": "en",
  "best_of_used": 1,
  "is_custom_tone": false,
  "words_charged": 12,
  "words_remaining": 124988
}

Authentication

All authenticated endpoints require a Bearer token in the Authorization header.

HTTP Header
Authorization: Bearer htai_live_...

Key management

  • Generation: Create API keys from your dashboard at any time. Each key starts with htai_live_.
  • Security: Keys are shown only once at creation. We store a SHA-256 hash — even we can’t recover the original. Store it securely.
  • Rotation: When you rotate a key, the old key enters a grace period (configurable) before revocation, so you can update your integrations without downtime.
  • Limits: Growth plans get 5 keys, Scale gets 15.

Endpoints

POSThttps://api.humanizethisai.com/v1/humanize

Humanize AI-generated text with a specific mode and tone. This is the primary endpoint.

Request Body

json
{
  "text": "string (required) — The AI-generated text to humanize",
  "mode": "standard | academic | enhanced | ultra | aggressive",
  "tone": "casual | formal | professional | academic | creative",
  "readability": "high-school | college | graduate | doctorate",
  "freeze_words": ["BrandName", "TechnicalTerm"],
  "best_of": 1,
  "language": "en",
  "stream": false
}
FieldTypeRequiredDescription
textstringYesThe AI-generated text to humanize
modestringNoDefault: standard. One of: standard, academic, enhanced, ultra, aggressive
tonestringNoDefault: casual. One of: casual, formal, professional, academic, creative
readabilitystringNoWriting level of the output. One of: high-school, college, graduate, doctorate. Default: college
freeze_wordsstring[]NoWords/phrases to preserve unchanged during humanization
best_ofintegerNoFan-out 1-5. Runs N generations, keeps the best. Multiplies word cost by N. Default: 1
languagestringNoOutput language (ISO 639-1): en, es, fr, de, it, pt, ru, zh, ja, ko, tr, ar. Default: en
streambooleanNoEnable SSE streaming. Default: false

Billing & best_of

You are charged words_charged = input_words × multiplier. The multiplier is best_of (1–5, default 1): higher values run more parallel generations and keep the best result, at a proportionally higher word cost. Two modes set their own floor: mode: "ultra" always runs a 3-call self-audit chain, so it forces a 3× multiplier and ignores best_of;mode: "aggressive" defaults to a 3× multiplier when best_of is omitted (an explicit best_of overrides it). The multiplier applies to input_words only — output length never affects the charge. best_of above 3 requires the Business plan.

Response (200 OK)

json
{
  "object": "humanization",
  "id": "8f2a1c6e-3b4d-4f0a-9c12-7e5d8a1b2c3d",
  "humanized_text": "Your humanized output...",
  "input_words": 150,
  "output_words": 148,
  "mode": "standard",
  "tone": "casual",
  "language": "en",
  "best_of_used": 1,
  "is_custom_tone": false,
  "words_charged": 150,
  "words_remaining": 124850
}

Streaming Response (SSE events)

text/event-stream
event: meta
data: {"id":"8f2a1c6e-3b4d-4f0a-9c12-7e5d8a1b2c3d","mode":"standard","tone":"casual","language":"en","input_words":12}

event: chunk
data: {"text":"AI has completely "}

event: chunk
data: {"text":"changed how we "}

event: chunk
data: {"text":"deal with tech."}

event: done
data: {"object":"humanization","id":"8f2a1c6e-3b4d-4f0a-9c12-7e5d8a1b2c3d","input_words":12,"output_words":11,"words_charged":12,"words_remaining":124988,"credits_low":false,"language":"en","best_of_used":1,"is_custom_tone":false,"latency_ms":1342}

# If generation fails mid-stream, a single error event is emitted instead of done:
event: error
data: {"code":"AI_GENERATION_FAILED","message":"Failed to generate humanized text. You were not charged.","refunded":true}

meta — Request metadata: id (the canonical request id, equal to the X-Request-Id header), mode, tone, language, input_words.

chunk — Partial humanized text fragment: { text }. Repeats until the output is complete.

done — Terminal success event with object, id, word counts, words_charged/words_remaining, best_of_used, is_custom_tone, and latency_ms.

error — Terminal failure event: { code, message, refunded }. refunded: true means you were not charged. Emitted instead of done.

POSThttps://api.humanizethisai.com/v1/detectAll Plans

Run AI detection scoring on text. Returns AI probability, human probability, and detailed signals.

Request Body

json
{
  "text": "string (required) — The text to run AI detection on"
}

Response (200 OK)

json
{
  "object": "detection",
  "id": "c4d5e6f7-8a9b-4c0d-1e2f-3a4b5c6d7e8f",
  "ai_score": 72,
  "human_score": 28,
  "confidence": "high",
  "verdict": "likely_ai",
  "label": "Likely AI",
  "signals": [
    { "signal": "Low lexical variance", "severity": "high", "detail": "Repetitive phrasing across sentences" }
  ],
  "sentences": [],
  "summary": "Text shows strong markers of AI generation.",
  "input_words": 150,
  "words_charged": 150,
  "words_remaining": 124700
}
GEThttps://api.humanizethisai.com/v1/usage

Returns your current plan details, usage stats, and key information.

Response (200 OK)

json
{
  "plan": {
    "key": "growth",
    "name": "Growth",
    "words_per_month": 1000000,
    "max_words_per_request": 15000,
    "requests_per_minute": 60,
    "allowed_modes": ["standard", "academic", "enhanced", "ultra", "aggressive"],
    "streaming_enabled": true,
    "detection_enabled": true
  },
  "usage": {
    "words_used": 42150,
    "words_remaining": 957850,
    "words_limit": 1000000,
    "topup_words_remaining": 0,
    "requests_this_period": 1203,
    "period_end": "2026-05-01T00:00:00Z"
  },
  "stats": {
    "summary": {
      "total_requests": 1203,
      "total_words_charged": 42150,
      "total_input_words": 41000,
      "total_output_words": 40200,
      "avg_latency_ms": 1380,
      "error_count": 4,
      "success_rate": 99.67
    },
    "daily": [
      { "date": "2026-04-07", "requests": 47, "words_charged": 1650, "errors": 0 }
    ]
  },
  "key": {
    "id": "0b9c8d7e-6f5a-4b3c-2d1e-0f9a8b7c6d5e",
    "name": "Production Key",
    "scopes": ["humanize", "detect", "tones"]
  }
}
GEThttps://api.humanizethisai.com/v1/health

Health check endpoint. No authentication required.

Response (200 OK)

json
{
  "status": "healthy",
  "timestamp": "2026-04-07T14:30:00Z",
  "version": "1.0.0"
}
Custom Tonestones scope

Create and manage reusable custom tones, then pass a tone’s UUID as the tone field on /v1/humanize. Every endpoint requires an API key with the tones (or *) scope. Write operations (POST/PUT/DELETE) are capped at 10 requests/minute. These calls never consume words — words_charged is always 0.

MethodPathDescription
POST/v1/tonesCreate a custom tone
GET/v1/tonesList your custom tones
GET/v1/tones/:idFetch one tone by UUID
PUT/v1/tones/:idUpdate a tone (any subset of fields)
DELETE/v1/tones/:idDelete a tone
FieldTypeRequiredDescription
namestringYes1-50 characters. Display name for the tone.
instructionsstringYes1-500 characters. How the model should rewrite the text.
descriptionstringNoUp to 500 characters. Optional human note.
examplestringNoUp to 500 characters. Optional sample of the target voice.

Create — Request

bash
curl -X POST https://api.humanizethisai.com/v1/tones \
  -H "Authorization: Bearer htai_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Newsletter voice",
    "instructions": "Warm, concise, second-person. Short sentences.",
    "description": "Used for our weekly digest",
    "example": "Hey there — here is what you missed this week."
  }'

Create — Response (200 OK)

json
{
  "object": "tone",
  "id": "0b9c8d7e-6f5a-4b3c-2d1e-0f9a8b7c6d5e",
  "tone": {
    "id": "9d7c1b2a-4e5f-4a6b-8c9d-0e1f2a3b4c5d",
    "name": "Newsletter voice",
    "instructions": "Warm, concise, second-person. Short sentences.",
    "description": "Used for our weekly digest",
    "example": "Hey there — here is what you missed this week.",
    "created_at": "2026-04-07T14:30:00Z",
    "updated_at": "2026-04-07T14:30:00Z"
  },
  "words_charged": 0,
  "words_remaining": 957850
}

List — Response (200 OK)

json
{
  "object": "list",
  "id": "0b9c8d7e-6f5a-4b3c-2d1e-0f9a8b7c6d5e",
  "tones": [
    {
      "id": "9d7c1b2a-4e5f-4a6b-8c9d-0e1f2a3b4c5d",
      "name": "Newsletter voice",
      "instructions": "Warm, concise, second-person. Short sentences.",
      "description": "Used for our weekly digest",
      "example": "Hey there — here is what you missed this week.",
      "created_at": "2026-04-07T14:30:00Z",
      "updated_at": "2026-04-07T14:30:00Z"
    }
  ],
  "words_charged": 0,
  "words_remaining": 957850
}

Delete — Response (200 OK)

json
{
  "object": "tone",
  "id": "0b9c8d7e-6f5a-4b3c-2d1e-0f9a8b7c6d5e",
  "deleted": true,
  "words_charged": 0,
  "words_remaining": 957850
}

Idempotency

/v1/humanize and /v1/detect accept an optional Idempotency-Key request header. Send the same key to safely retry a request — on a flaky network or a timeout — without being charged twice. The key also becomes the response id and the X-Request-Id header.

First use — the request is processed normally and the result is stored against the key.
Replay — reusing the key returns the stored result without re-running (or re-charging) the request. The response carries X-Idempotent-Replay: true and a replayed: true field. Replays are always returned as JSON, even if the original request used stream: true.
Conflict — reusing a key with a different payload returns 409 IDEMPOTENCY_KEY_CONFLICT. Only reuse a key to retry the identical request.
Malformed — a key outside 1-128 characters of A-Z a-z 0-9 _ - : . returns 400 INVALID_IDEMPOTENCY_KEY. It is never silently replaced.
Absent — without the header the request is not deduplicated; each call is independent.
bash
# Retry the exact same request safely — reuse the key.
curl -X POST https://api.humanizethisai.com/v1/humanize \
  -H "Authorization: Bearer htai_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4815-humanize-01" \
  -d '{ "text": "Text to humanize", "mode": "standard", "tone": "casual" }'

# Reusing the key with the SAME payload replays the stored result:
HTTP/1.1 200 OK
X-Idempotent-Replay: true
X-Request-Id: order-4815-humanize-01

Error Codes

Every error is an RFC 7807 application/problem+json document. Match on the code field (a stable UPPER_SNAKE string) or the full type URL — never parse the human-readabledetail. request_id equals the X-Request-Id response header. Some codes add structured extension members — INSUFFICIENT_WORDS carries words_required and words_remaining.

json
HTTP/1.1 402 Payment Required
Content-Type: application/problem+json

{
  "type": "https://docs.humanizethis.ai/errors/insufficient-words",
  "title": "Insufficient word balance",
  "status": 402,
  "detail": "Insufficient word balance. Need 600, have 50. This request uses mode="ultra", which multiplies word cost by 3. Purchase top-ups or upgrade your plan.",
  "instance": "/v1/humanize",
  "code": "INSUFFICIENT_WORDS",
  "request_id": "8f2a1c6e-3b4d-4f0a-9c12-7e5d8a1b2c3d",
  "words_required": 600,
  "words_remaining": 50
}
CodeHTTPDescription
MISSING_API_KEY401No Authorization header provided
INVALID_API_KEY_FORMAT401Key doesn't start with htai_live_
INVALID_KEY401Key not found or has been revoked
KEY_EXPIRED401Key has expired (rotated)
NO_ACTIVE_SUBSCRIPTION403No active API plan on the account
SCOPE_DENIED403Key doesn't have the required scope for this operation
MODE_NOT_AVAILABLE403Requested mode not included in current plan
ENDPOINT_NOT_AVAILABLE403Endpoint not included in current plan
STREAMING_NOT_AVAILABLE403SSE streaming not included in current plan
DETECTION_NOT_AVAILABLE403AI detection not included in current plan
IP_NOT_ALLOWED403Request IP not in key's allowlist
RATE_LIMITED429Rate limit exceeded — slow down and retry
MISSING_TEXT400No text field in request body
INVALID_TEXT400Text failed validation (empty or invalid characters)
INVALID_JSON400Request body is not valid JSON
TEXT_TOO_LONG400Text exceeds plan's maximum words per request
INVALID_MODE400Unknown humanization mode
INVALID_TONE400Unknown tone preset
INVALID_BEST_OF400best_of is not an integer between 1 and 5
BEST_OF_NOT_AVAILABLE403best_of above 3 requires the Business plan
LEGACY_PROMPT_REMOVED400Legacy "prompt" field is no longer supported — use "text"
INVALID_IDEMPOTENCY_KEY400Idempotency-Key header is malformed (must be 1-128 chars of A-Z a-z 0-9 _ - : .)
IDEMPOTENCY_KEY_CONFLICT409Idempotency-Key was already used for a different request payload
INSUFFICIENT_WORDS402Not enough word balance — carries words_required and words_remaining
TONE_NOT_FOUND404Referenced custom tone does not exist
AI_GENERATION_FAILED500AI processing failed — not charged
DETECTION_FAILED500Detection processing failed — not charged

Rate Limits

Every API response includes rate limit headers so you can build adaptive throttling into your client.

PlanRequests / minConcurrentKeys
Growth60105
Scale2003015

Response headers

X-RateLimit-LimitYour plan's maximum requests per minute.
X-RateLimit-RemainingRequests remaining in the current 60-second window.
X-RateLimit-ResetSeconds until the current window resets (a delta, not a Unix timestamp). 0 while you are within the limit.
Retry-AfterSeconds to wait before retrying. Present only on 429 responses.
HTTP
# Example rate-limited response
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 30
Retry-After: 30

{
  "type": "https://docs.humanizethis.ai/errors/rate-limited",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "Rate limit exceeded. Slow down.",
  "instance": "/v1/humanize",
  "code": "RATE_LIMITED",
  "request_id": "8f2a1c6e-3b4d-4f0a-9c12-7e5d8a1b2c3d"
}

Code Examples

Complete, runnable examples with authentication and error handling.

cURL
curl -X POST https://api.humanizethisai.com/v1/humanize \
  -H "Authorization: Bearer htai_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "text": "AI-generated text to humanize",
    "mode": "standard",
    "tone": "casual",
    "freeze_words": ["HumanizeThisAI", "GPTZero"],
    "best_of": 1,
    "language": "en",
    "stream": false
  }'