---
id: api
name: SupWriter Text API
version: 1.0.0
---

# SupWriter Text API

One REST API for four tools — **humanize**, **paraphrase**, **grammar check**, and **AI detection** — the same engines that power the SupWriter app. Hand this file to your AI coding agent and ask it to integrate SupWriter; everything it needs is below.

## Base URL

`https://supwriter.com/api/v1`

## Authentication

Every request needs a personal API key, created at **Settings → API & MCP** (requires a Pro or Ultra plan). Send it as a Bearer token over HTTPS:

```
Authorization: Bearer sw_live_xxxxxxxx
```

`x-api-key: sw_live_…` is also accepted. Keep the key server-side — never ship it to the browser.

## Response envelope

Success (HTTP 200):

```json
{ "success": true, "data": { /* endpoint-specific, see below */ } }
```

Error (with the matching HTTP status):

```json
{ "success": false, "error": { "code": "invalid_api_key", "message": "…" } }
```

## Endpoints

The four text endpoints are `POST` with a JSON body, and `text` is always required. `GET /me` takes no body. Where a request accepts `language`, pass the full English name of one of 50+ languages (for example `english` or `spanish`).

### POST /humanize

Rewrite AI-generated text so it reads as naturally human-written.

- Request: `{ "text": string, "intensity"?: "low"|"medium"|"high", "format"?: "plain"|"markdown", "language"?: string, "tone"?: string, "purpose"?: string }`
- `data`: `{ "humanized_text": string, "word_count": number, "credits_used": number, "credits_remaining": number, "processing_time_ms": number }`

### POST /paraphrase

Reword while preserving meaning, then humanize the result.

- Request: `{ "text": string, "mode"?: "standard"|"formal"|"casual"|"academic"|"creative", "language"?: string }`
- `data`: `{ "paraphrased_text": string, "word_count": number, "credits_used": number, "processing_time_ms": number }`

### POST /grammar

Fix grammar, spelling, and punctuation.

- Request: `{ "text": string, "mode"?: "basic"|"thorough"|"style", "language"?: string }`
- `data`: `{ "corrected_text": string, "word_count": number, "credits_used": number, "processing_time_ms": number }`

### POST /detect

Return one AI score plus a per-label guide and sentence highlights. The guide is from one scoring model, not live queries to other detectors.

- Request: `{ "text": string }`
- `data`: `{ "detection_score": number (0-100, higher = more likely AI), "detectors": { [name: string]: "right"|"warn"|"bad" }, "sentences": [{ "sentence": string, "score": number, "classification": "HUMAN_ONLY"|"MIXED"|"AI_GENERATED" }], "word_count": number, "credits_used": number, "credits_remaining": number }`

### GET /me

Who the key belongs to, and what it can do right now. Spends **no** word credits (it does consume one unit of the per-minute rate limit).

- Request: no body.
- `data`: `{ "user": { "id": string, "email": string|null, "name": string|null }, "plan": { "type": "pro"|"ultra", "billing_period": "monthly"|"annual"|null, "current_period_end": string|null, "cancel_at_period_end": boolean }, "credits": { "remaining": number, "total": number, "used": number, "plan_remaining": number, "extra_remaining": number }, "limits": { "words_per_request": number, "max_input_chars": number, "requests_per_minute": number|null }, "rate_limit": { "limit": number, "remaining": number, "reset": number|null } }`

Use it to show a balance before spending credits, and to read `limits.words_per_request` so long inputs can be split before they are rejected.

## Errors

| HTTP | `code`                   | Meaning                                          |
| ---- | ------------------------ | ------------------------------------------------ |
| 401  | `invalid_api_key`        | Missing or invalid API key.                      |
| 403  | `plan_required`          | API access requires a Pro or Ultra plan.         |
| 403  | `insufficient_credits`   | Not enough word credits on the plan.             |
| 403  | `plan_limit`             | Text exceeds the plan's per-request word cap.    |
| 403  | `language_not_available` | Requested language requires a higher plan.       |
| 400  | `invalid_input`          | Missing or malformed `text`.                     |
| 429  | `rate_limited`           | Over the per-minute limit; honor `Retry-After`.  |
| 500  | `provider_error`         | Upstream processing failed; safe to retry.       |

## Rate limits & billing

- **Pro:** 60 requests/minute, 2,000 words/request. **Ultra:** 120 requests/minute, 5,000 words/request — per key.
- Repeatedly presenting an invalid key is throttled per IP: after 30 rejected keys in a minute you get `429 rate_limited` instead of a `401`. A valid key never touches that budget.
- Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`; a 429 also sends `Retry-After`.
- Billing: humanize costs 1 credit per input word. Paraphrase and grammar cost 1 credit per output word. Detection is free for paid plans in the web app. Free web users, and every plan over the API or MCP, are charged 1 credit per input word.

## Example (cURL)

```bash
curl -s https://supwriter.com/api/v1/humanize \
  -H "Authorization: Bearer $SUPWRITER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"Your AI-generated text here.","intensity":"medium"}'
```

## Command line

The same API is packaged as a CLI, which is usually the fastest way to try it and the easiest way to wire it into CI:

```bash
npm install -g supwriter
sw login                                   # or set SUPWRITER_API_KEY
cat draft.md | sw humanize > final.md
sw detect 'content/**/*.md' --max-score 30 # exits 1 if anything reads as AI
```

It splits documents over the per-request word limit, retries on `429`, and exits with a distinct code per error class. See `sw --help`.

## Implementation checklist

1. Read `SUPWRITER_API_KEY` from a server-side env var; never expose it client-side. HTTPS only.
2. Write one typed client function per tool (or a single `call(tool, body)`), POSTing to the base URL with the Bearer header.
3. On success, unwrap `data`; on failure, map `error.code` to typed errors.
4. Retry on `429` up to 3 times, waiting `Retry-After` seconds.
5. Surface `credits_remaining` so users can see usage.

Full human-readable reference: https://supwriter.com/developers
