← Readability Check / API
Get a token

Drive Readability Check from your own code

The scores (Flesch, Flesch-Kincaid, Fog, SMOG, Coleman-Liau, ARI, the CEFR word profile) are computed in the browser and are not an API call. The rewrite is. Base URL:

https://api.skillsafe.ai/v1/app-api

Every request carries Authorization: Bearer <token> and Content-Type: application/json. Every response is a JSON envelope: {"ok":true,"data":{…}} on success and {"ok":false,"error":{"code":"…","message":"…"}} on failure. Read error.code, not only the HTTP status.

The web app re-scores every rewrite, checks that each number and name from the original is still there, and lists words left above the target. Code calling the API gets the model's reply only and should check it the same way.

Errors

CodeMeaningWhat to do
UNAUTHORIZEDMissing, expired or wrong-app token.Mint a guest token or sign in again.
INSUFFICIENT_CREDITSBalance below min_credits.Top up. /estimate is free and tells you the hold.
VALIDATION_ERRORThe input object failed validation.Check error.details. The body is the input object itself — never wrap it in {"input": …}.
RATE_LIMITEDToo many requests.Back off and retry.
JOB_FAILEDThe run started and did not complete.Retry with the same Idempotency-Key.

The task field comes first

There is one task: "rewrite". Send it on every call.

Input

One flat object of strings. task, text and target are what matter; the rest may be empty.

FieldTypeMeaning
taskstring, required"rewrite".
textstringThe text to rewrite. The web app sends at most 1,500 words, cut between paragraphs.
targetstringA2, B1, B2, C1 (CEFR) or G3–G10, G12 (US grade).
target_labelstringThe target in words, e.g. CEFR B1 (intermediate).
keep_termsstringComma-separated terms to keep exactly (400 characters).
audiencestringWho will read it (300 characters).
prescanstring (JSON)The web app's free check: words, sentences, avg_sentence_words, grade, flesch, cefr, vocabulary_level, sentence_cap, above_target[], facts[], hardest[]. Send {} if you have none.
retry_notestringSent only on the one automatic reformat retry.

Output

The output (output.output) is one JSON object: task, status (ok · already_at_level · not_prose), message, target, level_read, rewrite (the whole text; paragraphs separated by a blank line), changes[] of {from, to, why}, kept_terms[] of {term, why} and check_notes[].

status: "not_prose" means the input was code, a table or too short: rewrite is empty and message says why. The model may wrap the object in a code fence; strip it before parsing.

1. Get a token

A guest token is enough for /me and /estimate. Running is metered and needs a personal token from signing in. The tokens page shows yours and copies it. Replace YOUR_TOKEN below with it.

2. Check the session and the balance

/me returns subject_type, subject_id and credits. Signed in means subject_type === "user".

3. Price it before you run it

/estimate is free and starts no job. hold_credits is a reservation priced against the longest possible reply, not the price. A tenancy notice to CEFR A2:

A science text to US grade 5, keeping two terms:

4. Run and poll

/run returns a job. Always send an Idempotency-Key — the same key is the same run, so a retry cannot bill twice. The web app derives it from a hash of the input plus an attempt number for its one automatic reformat retry.

5. Stream it

/run-stream answers with server-sent events and ends with a done frame carrying the whole output, charged_credits and truncated. If truncated is true the balance ran low mid-answer: the rewrite stops early; top up and run again.