# Phrasly Business API > REST API for AI text humanization and AI content detection. Two POST endpoints, API key authentication, JSON request bodies. - Base URL: https://business.phrasly.ai - Human-readable docs: https://business.phrasly.ai/docs - API keys: https://business.phrasly.ai/dashboard/api-keys (requires an active subscription) ## Authentication Send these headers on every request: | Header | Value | | --- | --- | | `x-api-key` | Your API key | | `Content-Type` | `application/json` | Call the API from a server. Never put the key in client-side code, and read it from an environment variable. ## POST /api/v1/humanize Rewrites AI-generated text so it reads naturally while keeping the meaning. Request body (JSON): | Field | Type | Required | Notes | | --- | --- | --- | --- | | `text` | string | yes | 20 to 5,000 words | | `stream` | boolean | no | Default `false`. When `true` the text is streamed as it is generated | | `model` | string | no | Only for accounts with Phrasly Ultra enabled: they get Ultra by default, and `"latest"` selects the standard model instead. Everyone else can omit it | | `mode` | string | no | Phrasly Ultra mode, from fewest changes to strongest rewrite: `"gentle"`, `"light"`, `"balanced"` (default), `"strong"`, `"max"`. Any other value runs `"balanced"`. The standard model ignores it | Response: `200` with `Content-Type: text/plain`. The response body **is** the humanized text. It is not JSON, so read it with `response.text()`. With `stream: true` the body arrives as chunked plain UTF-8 text. Append the chunks in order. It is not SSE and not NDJSON. ```bash curl -X POST https://business.phrasly.ai/api/v1/humanize \ -H "x-api-key: $PHRASLY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Your AI-generated text here..."}' ``` ```javascript const response = await fetch("https://business.phrasly.ai/api/v1/humanize", { method: "POST", headers: { "x-api-key": process.env.PHRASLY_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ text }), }); if (!response.ok) { const { error } = await response.json(); throw new Error(`Phrasly ${response.status}: ${error}`); } const humanized = await response.text(); ``` ```python import os import requests response = requests.post( "https://business.phrasly.ai/api/v1/humanize", headers={"x-api-key": os.environ["PHRASLY_API_KEY"]}, json={"text": text}, timeout=300, ) response.raise_for_status() humanized = response.text ``` Streaming in JavaScript: ```javascript const response = await fetch("https://business.phrasly.ai/api/v1/humanize", { method: "POST", headers: { "x-api-key": process.env.PHRASLY_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ text, stream: true }), }); const decoder = new TextDecoder(); let humanized = ""; for await (const chunk of response.body) { humanized += decoder.decode(chunk, { stream: true }); } ``` ## POST /api/v1/detect Estimates how likely a text is to be AI-generated, overall and per sentence. Request body (JSON): | Field | Type | Required | Notes | | --- | --- | --- | --- | | `text` | string | yes | At least 50 words and at most 15,000 characters | Response: `200` with `Content-Type: application/json`. ```json { "data": { "Label": "AI", "AI": 85.9, "Human": 14.1, "Chunks": ["..."], "SentenceScores": [ { "text": "...", "score": 0.57 }, { "text": "...", "score": 0.22 } ], "original": "..." }, "message": "AI detection results are ready." } ``` | Field | Type | Meaning | | --- | --- | --- | | `data.Label` | string | `"AI"`, `"Might be AI"` or `"Human"` | | `data.AI` | number | AI confidence, 0 to 100 | | `data.Human` | number | Human confidence, 0 to 100 | | `data.Chunks` | string[] | The sentences flagged as AI-generated | | `data.SentenceScores` | `{ text, score }[]` | AI score from 0 to 1 for every sentence | | `data.original` | string | The input text | ```javascript const response = await fetch("https://business.phrasly.ai/api/v1/detect", { method: "POST", headers: { "x-api-key": process.env.PHRASLY_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ text }), }); const { data } = await response.json(); console.log(data.Label, data.AI); ``` ## Errors Every error is JSON in the shape `{ "error": "message" }`, including errors from the humanize endpoint. | Status | Meaning | Retry? | | --- | --- | --- | | 400 | Invalid JSON, missing `text`, or text outside the length limits | No, fix the request | | 401 | Missing or invalid API key | No | | 402 | API access suspended after failed payments | No, update the payment method | | 403 | No active subscription | No | | 429 | Rate limit exceeded (a `Retry-After` header gives the seconds to wait), or credits exhausted | Only for the rate limit | | 500 | Processing failed | Yes, with backoff | | 503 | Detection service temporarily unavailable | Yes, with backoff | For retries use exponential backoff: 2s, 4s, 8s, then give up and surface the error. ## Rate limits 5 requests per second (300 per minute) per API key, counted separately for each endpoint. ## Bulk and automated jobs - Send requests concurrently, but stay under 5 per second per key and honor `Retry-After` on a 429. - Split documents longer than 5,000 words at paragraph boundaries and humanize each part. - On the standard model, paragraphs under 15 words are passed through unchanged, so headings survive a rewrite. - Usage is billed on the word count of the input text. ## Pricing - $100 per month, which includes $100 in API credits. Credits reset each billing cycle. - Humanize: $0.14 per 1,000 words. Detect: $0.02 per 1,000 words. - Usage past the included credits is billed at the same per-word rates through autopay top-ups. With autopay off, requests return 429 until the next cycle. ## Models and guarantees - The API runs Phrasly's proprietary models. They can change over time and may differ from the models in the phrasly.ai app. - Phrasly Ultra is not included by default. Access is arranged with the sales team. - Detectors change and produce false positives, so no result on any specific AI detector is guaranteed. Detection scores are probabilistic estimates.