Plain Language Simplification API
The simplify endpoint rewrites a text in plain language at a reading level you choose — for plain-language compliance (e.g. agencies covered by the US Plain Writing Act), healthcare patient communications, legal notices, customer support and education. The rewrite keeps all of the text's content, structure and language: nothing is summarized, dropped or translated. To shorten a text instead, see summarize.
The improvement is verifiable: when the text's language supports readability scoring, the response includes a deterministic before/after readability block (Flesch-Kincaid grade level and reading ease, the same formulas as the statistics endpoint), so you can see — and show your users — how far the rewrite actually moved the score.
Terms that must survive the rewrite (product names, defined legal terms) can be
listed in preserve_terms; a rewrite that drops one is refused server-side rather than returned.
Sample Code
- cURL
- JavaScript
- Python
- Python (Sapling client)
curl -X POST https://api.sapling.ai/api/v1/simplify \
-H "Content-Type: application/json" \
-d '{"key":"<api-key>", "text":"The party of the first part shall remit payment within thirty (30) days of receipt of the invoice.", "reading_level": "plain", "preserve_terms": ["invoice"]}'
import axios from 'axios';
async function run(text) {
try {
const response = await axios.post(
'https://api.sapling.ai/api/v1/simplify',
{
key: '<api-key>',
text,
reading_level: 'plain',
preserve_terms: ['invoice'],
},
);
const {status, data} = response;
console.log({status});
console.log(JSON.stringify(data, null, 4));
} catch (err) {
const msg = err.response?.data?.msg || err.message;
console.log({err: msg});
}
}
run('The party of the first part shall remit payment within thirty (30) days of receipt of the invoice.');
import requests
from pprint import pprint
response = requests.post(
"https://api.sapling.ai/api/v1/simplify",
json={
"key": "<api-key>",
"text": ("The party of the first part shall remit payment within "
"thirty (30) days of receipt of the invoice."),
"reading_level": "plain",
"preserve_terms": ["invoice"],
}
)
if 200 <= response.status_code < 300:
pprint(response.json())
else:
print('Error: ', response.status_code, response.text)
from sapling import SaplingClient
api_key = '<api-key>'
client = SaplingClient(api_key=api_key)
result = client.simplify(
'The party of the first part shall remit payment within thirty (30) '
'days of receipt of the invoice.',
reading_level='plain',
preserve_terms=['invoice'],
)
print(result['simplified'])
readability = result.get('readability')
if readability:
print(f"Grade {readability['before']['grade']} -> "
f"{readability['after']['grade']}")
Sample Response
{
"simplified": "You must pay within 30 days of getting the invoice.",
"reading_level": "plain",
"lang": "en",
"readability": {
"before": {"grade": 12.3, "ease": 42.1},
"after": {"grade": 5.8, "ease": 78.4}
}
}
Batch Requests
To simplify many short texts — a set of notice paragraphs, a queue of support replies — in one
request, send texts (a list of 1–10 strings) instead of text. The same reading_level,
preserve_terms and lang (when provided) apply to every item, and the combined length of all items may be up to
5,000 characters (the same cap as a single text). Exactly one of text and texts must be
provided.
curl -X POST https://api.sapling.ai/api/v1/simplify \
-H "Content-Type: application/json" \
-d '{"key":"<api-key>", "texts": ["The party of the first part shall remit payment.", "Utilize the aforementioned portal heretofore described."]}'
The batch response is {"results": [...]} with one entry per input, in input order; each entry has
exactly the single-response shape above. With lang omitted, each item's language — and therefore
its readability scores — is detected per item, so a batch may legitimately mix languages:
{
"results": [
{
"simplified": "You must pay.",
"reading_level": "plain",
"lang": "en",
"readability": {
"before": {"grade": 11.2, "ease": 47.9},
"after": {"grade": 2.1, "ease": 95.7}
}
},
{
"simplified": "Use the portal described above.",
"reading_level": "plain",
"lang": "en",
"readability": {
"before": {"grade": 14.8, "ease": 12.3},
"after": {"grade": 4.4, "ease": 83.2}
}
}
]
}
Items are billed individually (a batch of N costs the same as N single requests), and each item is
cached the same way as a single request — batches and single calls share the cache. If any item fails
to process, the whole request returns a 502 and nothing is billed; the items that did succeed are
already cached, so a retry only re-runs the failures.
Request Parameters
POST to https://api.sapling.ai/api/v1/simplify
key: String
32-character API key. Can also be supplied via the Authorization header as a bearer token; if both are provided, the key parameter takes precedence.
text: String
The text to simplify, up to 5,000 characters. Sent to the model exactly as submitted — markup and
line structure are preserved, not stripped — and the rewrite gives you your document back with its
structure intact. Empty or whitespace-only text returns a 400. The text is treated as untrusted
data: instructions inside it are ignored and it is rewritten on its content alone. Provide exactly
one of text and texts.
texts: List[String]
Batch form (see Batch Requests): 1–10 texts to simplify in one request, each
treated exactly like text above, with a combined length of up to 5,000 characters. An empty or
whitespace-only item returns a 400 naming its index. The response becomes {"results": [...]},
one entry per item in input order.
reading_level: String, optional — defaults to plain
The target audience:
plain(default): plain-language style for a general audience, in the spirit of plainlanguage.gov — short sentences, everyday words, active voice.elementary: ~US grade 3–5.middle_school: ~US grade 6–8.high_school: ~US grade 9–10.
preserve_terms: List[String], optional
Up to 20 terms (each up to 100 characters) the rewrite must keep rather than paraphrase away —
product names, trademarks, defined legal terms. Enforced structurally: a rewrite that drops a term the
source contains is refused with a 502 rather than returned. Matching is case-insensitive with
whitespace collapsed, so line re-wrapping does not count as dropping a term — but by the same token a
change in casing alone (e.g. iPhone → Iphone) is not treated as a dropped term, so where the
exact capitalization of a term matters, verify it in the returned text.
lang: String, optional — defaults to auto-detection
ISO 639-1 code (e.g. en) selecting the readability formula for the before/after scores. Omit to
auto-detect (falls back to en); on the batch form, detection runs per item, so each result carries
its own lang. This never changes the rewrite itself —
the output always stays in the text's own language.
Response Parameters
simplified: String
The rewritten text: same content, same language, same document structure, at the target reading
level.
reading_level: String
The reading level that was applied.
lang: String
The language the readability scores were computed for — the value you passed, or the detected
language when omitted.
readability: Object, optional
{"before": {"grade": ..., "ease": ...}, "after": {"grade": ..., "ease": ...}} — deterministic
Flesch-Kincaid grade level and reading ease for the submitted text and the rewrite, computed over the
prose (markup excluded) with the same formulas as the statistics endpoint.
Omitted (never an error) when the language has no supported readability formula or the text has no
scoreable prose.
Errors
| Status | Meaning |
|---|---|
400 | Validation error — missing, empty or oversized text (or a texts item, named by index), both or neither of text/texts, an unknown reading_level, or invalid preserve_terms. The body is {"msg": "..."}. |
401 / 403 | Missing or invalid API key. |
429 | Rate limit exceeded or key over capacity. |
502 | {"msg": "Unexpected error simplifying text."} — the rewriting model failed or the rewrite dropped a preserved term; safe to retry. Not billed and not cached (on the batch form the whole request is not billed, but items that already succeeded stay cached — see Batch Requests). |
Tips
- Score first, rewrite second. The statistics endpoint returns the same readability scores on their own — use it to decide which documents need simplifying, then send only those here.
- Pick the level for your audience, not the lowest one.
plainis the right default for public, legal and healthcare communications;elementarytrades more nuance away than most adult-facing content wants. - List your defined terms. In legal and healthcare text, capitalized defined terms
(
"Subscriber","the Provider") and product names should go inpreserve_termsso the rewrite can't paraphrase them away. - Check the delta.
readability.after.gradetells you whether the text landed near your target; a document dense with unavoidable technical terms may need a glossary rather than a lower reading level. - Results are cached on Sapling's side for about three days, keyed on the text, reading level and preserve terms — re-running the same document returns the same rewrite; changing any of them re-generates.