Style Guide Compliance API
The styleguide endpoint checks a text for compliance with style rules you define in the request — house style, brand voice, editorial guidelines, terminology policy. Send the text together with 1–20 rule names (optionally with a short description of each), and the endpoint returns every violation it finds: the verbatim offending passage with its character offsets, the violated rule, a brief note, and a rewrite suggestion that complies with your rules.
Only the rules you list are enforced — the endpoint never volunteers grammar, factuality or style opinions outside them. For general grammar and spelling, see the edits endpoint; for a generic writing-quality rubric, see quality.
Every reported passage is grounded: it is quoted verbatim from your text and located in it, so you
can highlight or auto-fix violations directly from the offsets. The text is checked exactly as
submitted (markup is not stripped — your rules may legitimately govern formatting), which means
start/end always index the string you sent.
Sample Code
- cURL
- JavaScript
- Python
- Python (Sapling client)
curl -X POST https://api.sapling.ai/api/v1/styleguide \
-H "Content-Type: application/json" \
-d '{"key":"<api-key>", "text":"Our synergy-driven solution was leveraged by the team. It is really great!!", "rules": ["no corporate jargon", "active voice", {"name": "no exclamation marks", "description": "Never use exclamation marks, even single ones."}]}'
import axios from 'axios';
async function run(text) {
try {
const response = await axios.post(
'https://api.sapling.ai/api/v1/styleguide',
{
key: '<api-key>',
text,
rules: [
'no corporate jargon',
'active voice',
{name: 'no exclamation marks',
description: 'Never use exclamation marks, even single ones.'},
],
},
);
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('Our synergy-driven solution was leveraged by the team. It is really great!!');
import requests
from pprint import pprint
response = requests.post(
"https://api.sapling.ai/api/v1/styleguide",
json={
"key": "<api-key>",
"text": "Our synergy-driven solution was leveraged by the team. It is really great!!",
"rules": [
"no corporate jargon",
"active voice",
{"name": "no exclamation marks",
"description": "Never use exclamation marks, even single ones."},
],
}
)
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)
rules = [
'no corporate jargon',
'active voice',
{'name': 'no exclamation marks',
'description': 'Never use exclamation marks, even single ones.'},
]
result = client.styleguide(
'Our synergy-driven solution was leveraged by the team. It is really great!!',
rules=rules,
)
if result['compliant']:
print('Text complies with the style guide.')
else:
for violation in result['violations']:
print(f"[{violation['rule']}] {violation['text']!r} -> {violation['suggestion']!r}")
print(f" {violation['note']} (chars {violation['start']}-{violation['end']})")
Sample Response
{
"violations": [
{
"rule": "no corporate jargon",
"text": "synergy-driven solution",
"start": 4,
"end": 27,
"note": "\"Synergy-driven\" is a corporate buzzword.",
"suggestion": "effective product"
},
{
"rule": "active voice",
"text": "was leveraged by the team",
"start": 28,
"end": 53,
"note": "Passive construction (and \"leveraged\" is itself jargon).",
"suggestion": "the team used"
},
{
"rule": "no exclamation marks",
"text": "It is really great!!",
"start": 55,
"end": 75,
"note": "Uses exclamation marks.",
"suggestion": "It is really great."
}
],
"rules": ["no corporate jargon", "active voice", "no exclamation marks"],
"compliant": false
}
A text with no violations returns "violations": [] and "compliant": true — that is a successful,
billable check, not an error.
Request Parameters
POST to https://api.sapling.ai/api/v1/styleguide
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 check, up to 10,000 characters. Checked exactly as submitted — markup is not stripped
(style rules may govern formatting), so the returned offsets always index this string. Empty or
whitespace-only text returns a 400. The text is treated as untrusted data: instructions inside it are
ignored and it is judged on its content alone.
rules: List[String | Object]
The style rules to enforce: between 1 and 20 entries. Each entry is either a string (the rule name) or an
object {"name": "...", "description": "..."} where description is optional. Names are trimmed, internal
whitespace is collapsed, and they must be at most 80 characters and case-insensitively unique; descriptions
are at most 400 characters and carry the nuance the model follows (what the rule requires or forbids, and
any exceptions). Names are returned in this normalized form in the response, so match your application
logic on the normalized names.
Response Parameters
violations: List[Object]
Up to 20 violations, most important first. May be empty (the text complies). Each has:
rule: the violated rule's name — always one of the submitted rule names (as normalized).text: the offending passage, quoted verbatim from the submitted text (at most 200 characters).start/end: the passage's character offsets in the submitted text (textequals the exact slicesubmitted_text[start:end]). Offsets count Unicode code points (Pythonstrindexing) — not UTF-16 code units or bytes — so in JavaScript slice withArray.from(text)if the input may contain characters outside the Basic Multilingual Plane (e.g. emoji), whose UTF-16 string indices would otherwise misalign. Both arenullin the rare case the passage cannot be located in the submitted text; the violation is still real andtextstill shows the passage.note: one short sentence explaining how the passage violates the rule.suggestion: a rewrite of the passage that complies with all the rules. May be an empty string when no rewrite applies (e.g. the fix is a deletion).
When the same problem repeats many times, the first few occurrences are reported and the note says so.
rules: List[String]
The checked rule names, normalized (trimmed, internal whitespace collapsed), in the order submitted.
compliant: Boolean
true exactly when violations is empty.
Errors
| Status | Meaning |
|---|---|
400 | Validation error — missing, empty or oversized text, or invalid rules (e.g. {"msg": "Invalid rules: Needs between 1 and 20 rules."}). The body is {"msg": "..."}. |
401 / 403 | Missing or invalid API key. |
429 | Rate limit exceeded or key over capacity. |
502 | {"msg": "Unexpected error checking text against the style guide."} — the checking model failed; safe to retry. Not billed and not cached. |
Tips
- Describe your rules. Plain names work for common conventions (
active voice,no exclamation marks), but adescriptionis what carries YOUR house style: what counts as jargon for you, which spellings you standardize on, the exceptions ("Use sentence case in headings, except for product names"). - One rule per concern. Violations name the rule they break, so separate rules (
no jargon,serial comma,spell out numbers under ten) give you per-rule reporting and let editors filter — a single mega-rule collapses everything into one bucket. - Auto-fix from the offsets.
textis always the exactstart:endslice of what you sent, so you can splicesuggestionover[start, end)directly (apply from the last violation to the first so earlier offsets stay valid). Offsets are Unicode code-point indices, so in JavaScript index overArray.from(text)rather than the raw string when the input may contain non-BMP characters like emoji. Skip violations whose offsets arenull. - A terminology policy works well as rules — e.g.
{"name": "product naming", "description": "The product is 'Acme Studio', never 'the studio' or 'AcmeStudio'"}. For mechanical find-and-replace pairs, custom mappings on the edits endpoint are cheaper. - Results are cached on Sapling's side for about three days, keyed on the text and the full rule set (names and descriptions) — re-running the same document against the same rules is fast and returns the same violations; changing any rule re-checks.