The AI humanizer API
Humanize AI-generated text from your own code. REST over HTTPS, one credit per word, and an OpenAPI spec you can point a client generator at. The same pipeline that powers the ReverseGPT editor.
Quickstart
Two calls: start a run, then poll it until the rewrite comes back.
Start a run
POST /api/v1/humanize curl -X POST https://www.reversegpt.ai/api/v1/humanize \ -H "Authorization: Bearer rg_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Leveraging synergies facilitates optimal outcomes."}'202 AcceptedHTTP/1.1 202 Accepted Location: https://www.reversegpt.ai/api/v1/jobs/clx8k2p9a0001 Retry-After: 3The job URL is in
Location. Poll it — nothing else in the body matters yet.Poll that URL until it finishes
GET /api/v1/jobs/{id} curl https://www.reversegpt.ai/api/v1/jobs/clx8k2p9a0001 \ -H "Authorization: Bearer rg_live_YOUR_KEY"200 OK{ "job": { "status": "completed", "wordCount": 5 }, "result": { "humanizedText": "Working together gets better results." } }Abridged. Keep polling while
statusispending; readresult.humanizedTextonce it iscompleted. Full shape in the OpenAPI spec.
Authenticate with one header — Authorization: Bearer rg_live_… or x-api-key, whichever your client makes easier. Going to production? Add an Idempotency-Key header to the POST so a retry cannot start — and charge for — a second run.
Endpoints
Base URL https://www.reversegpt.ai/api/v1. Every response carries an X-Request-Id — quote it if you ever need support.
| Endpoint | Scope | Cost | What it does |
|---|---|---|---|
| POST /v1/humanize | humanize:write | 1 credit per word | Start a run. Answers 202 with a Location header. At most 1000 words per request. |
| GET /v1/jobs/{id} | humanize:read | Free | Read a run back. Poll until status leaves pending, then read result.humanizedText. |
| GET /v1/openapi | None | Free | The OpenAPI 3.1 document, generated from the same schemas the endpoints validate with. |
In your language
There is no SDK to install and nothing to keep up to date — it is one POST and one GET. Both examples below are complete.
const API = "https://www.reversegpt.ai/api/v1";
const KEY = process.env.REVERSEGPT_API_KEY;
async function humanize(text) {
const started = await fetch(`${API}/humanize`, {
method: "POST",
headers: {
Authorization: `Bearer ${KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({ text }),
});
if (!started.ok) throw new Error((await started.json()).error.message);
let { job } = await started.json();
// Poll until the run leaves `pending`. Polling is free.
while (job.status === "pending") {
await new Promise((r) => setTimeout(r, 2000));
const polled = await fetch(job.url, {
headers: { Authorization: `Bearer ${KEY}` },
});
const body = await polled.json();
job = body.job;
if (job.status === "completed") return body.result.humanizedText;
if (job.status === "failed") throw new Error(body.error.message);
}
}import os, time, uuid, requests
API = "https://www.reversegpt.ai/api/v1"
KEY = os.environ["REVERSEGPT_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}
def humanize(text: str) -> str:
started = requests.post(
f"{API}/humanize",
headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
json={"text": text},
timeout=30,
)
started.raise_for_status()
job = started.json()["job"]
# Polling is free — the run was paid for when it started.
while job["status"] == "pending":
time.sleep(2)
body = requests.get(job["url"], headers=HEADERS, timeout=30).json()
job = body["job"]
if job["status"] == "completed":
return body["result"]["humanizedText"]
if job["status"] == "failed":
raise RuntimeError(body["error"]["message"])Errors
Every failure returns the same envelope. Branch on error.type, which is stable, and show error.message — it is written as an instruction, so it is safe to pass straight to a user or to an agent.
| Status | Type | Meaning |
|---|---|---|
| 400 | invalid_request_error | The body is not valid JSON. |
| 401 | authentication_error | The key is missing, revoked or expired. |
| 402 | billing_error | Not enough credits. Nothing ran and nothing was charged. |
| 403 | permission_error | The key lacks the scope this call needs. |
| 404 | not_found_error | No job with that id belongs to your account. |
| 422 | invalid_request_error | Empty text, over 1000 words, or an idempotency key reused with different text. |
| 429 | rate_limit_error | Key rate limit spent. See Retry-After. |
| 503 | api_error | The humanizing queue is unreachable. Retry shortly. |
{
"error": {
"type": "billing_error",
"code": "insufficient_credits",
"message": "This request needs 640 credits (one per word) but the account has 120 available. Add credits, upgrade the plan, or send shorter text, then retry.",
"details": { "creditsRequired": 640, "creditsAvailable": 120 },
"request_id": "req_9f2c1ab74e0d4c8e9b1a7f6d3c2e5a80"
}
}Rate limits, retries and scopes
Rate limits
60 requests per minute per key. Every response reports X-RateLimit-Remaining and the IETF RateLimit field. A 429 carries Retry-After. This is abuse protection, not billing — billing is credits.
Safe retries
Send Idempotency-Key on every POST. Replaying it with the same text returns the original job instead of starting a second, separately charged run. Reusing it with different text is rejected with 422 rather than silently returning the wrong output.
Scopes
humanize:write starts runs and spends credits. humanize:read only reads them back. Create a read-only key for anything unattended and it can collect results without ever being able to bill you.
Frequently asked questions
- How much does the ReverseGPT API cost?
- One credit per word of input, the same rate as the web editor — a 500-word draft costs 500 credits. Credits come from your plan, so the API adds no separate bill. Polling a job is free, and a run that fails is not charged.
- How do I get an API key?
- Sign in, open Settings, and create one under API keys. The key is shown once at creation and stored only as a hash, so copy it then. You can create a read-only key for unattended processes that should collect results but never start a run.
- Is the API synchronous or asynchronous?
- Asynchronous. POST /v1/humanize answers 202 with a Location header pointing at the job; poll that URL until status is completed and read result.humanizedText. Most runs finish in seconds and the pipeline gives up after five minutes.
- How long can the text be?
- Up to 1000 words per request, matching the editor's limit. Split anything longer into parts and humanize each one — the API is happy to run them in parallel, subject to your key's rate limit.
- Can I retry a request safely?
- Yes. Send an Idempotency-Key header on every POST. Replaying the same key with the same text returns the original job rather than starting and charging for a second run, for at least 24 hours.
- Is there an OpenAPI spec or an SDK?
- The OpenAPI 3.1 document is served at https://www.reversegpt.ai/api/v1/openapi and is generated from the same schemas the endpoints validate with, so it cannot drift. Point any OpenAPI code generator at it to get a typed client in your language.
- Can I use this from Claude or ChatGPT?
- Yes — ReverseGPT also ships an MCP server, which is the better fit for an AI assistant. Connect it once and the assistant can humanize text as a tool, with OAuth sign-in instead of a pasted key.
Start humanizing from your own code
Create an API key in Settings and make your first call in under a minute. Credits come from your existing plan — there is no separate API bill.