REST / JSON
API docs
JSON API: prepaid key balance, per-request billing, the same engine as the web UI.
Quick start in three calls
- Create a key — you get a csscl_… with a trial balance
curl -X POST https://css-cleaner.ru/api/v1/key -H "Content-Type: application/json" -d '{"name":"my-app"}' - Top up the balance: card, SBP, crypto or the sandbox
curl -X POST https://css-cleaner.ru/api/v1/topup -d 'key=csscl_XXXX&amount=500&provider=yookassa' - Send CSS — get clean CSS back plus a report
curl -X POST https://css-cleaner.ru/api/v1/dedup -H "X-API-Key: csscl_XXXX" -d '{"css":".a{color:red}.a{color:red}"}'
There is no account: the key is the wallet. Lose the key, lose the balance.
Authentication
Pass the key in any of these three ways.
X-API-Key: csscl_XXXXXXXXXXXXXXXX
Authorization: Bearer csscl_XXXXXXXXXXXXXXXX
POST /api/v1/dedup?key=csscl_XXXXXXXXXXXXXXXX
Without a key you get 20 free requests per day per IP — fine for tests, get a key for production.
Endpoints
| Method and path | What it does | Price |
|---|---|---|
POST /api/v1/dedup | Removes duplicates and empty rules, safely merges repeated selectors. | $0.0011 |
POST /api/v1/filter | Keeps only the rules that actually match the HTML you send. | $0.0022 |
POST /api/v1/block | Extracts one block's styles, including inherited html/body rules and every var() it needs. | $0.0033 |
POST /api/v1/key | Create a key with a trial balance | free |
GET /api/v1/key | Balance, spend, request count, current prices | free |
POST /api/v1/topup | Top-up invoice — returns the checkout URL | free |
POST /api/dedup · /api/filter · /api/block | The same three jobs, key-less and unbilled | free |
Request parameters
POST /api/v1/dedup
| Field | Type | Required | Meaning |
|---|---|---|---|
css | string | yes | the whole stylesheet, exactly as in the file |
strip_comments | bool | no | strip comments (off by default) |
merge_overrides | bool | no | merge identical selectors into one rule — only when properties do not conflict |
POST /api/v1/filter
| Field | Type | Required | Meaning |
|---|---|---|---|
css | string | yes | the whole stylesheet, exactly as in the file |
html | string | yes | the page HTML the CSS is cleaned against |
drop_keyframes | bool | no | drop @keyframes nothing animates |
drop_fonts | bool | no | drop unused @font-face |
POST /api/v1/block
| Field | Type | Required | Meaning |
|---|---|---|---|
css | string | yes | the whole stylesheet, exactly as in the file |
html | string | yes | the page HTML the CSS is cleaned against |
target | string | yes | block selector: .card, #hero, main .btn |
prune | bool | no | drop declarations the cascade overrides later anyway |
drop_keyframes | bool | no | drop @keyframes nothing animates |
drop_fonts | bool | no | drop unused @font-face |
POST /api/v1/topup
| Field | Type | Required | Meaning |
|---|---|---|---|
key | string | yes | the API key to top up |
amount | number | yes | amount in rubles: 100 to 100000 |
provider | string | no | yookassa · robokassa · cryptocloud · demo (empty = first available) |
Response
A successful call returns JSON with the result and a report. Money is in kopecks.
{
"ok": true,
"endpoint": "block",
"charged": 30,
"price": 30,
"balance": 970,
"output": ":root { --brand: #3b82f6; } body { color: #222; } .card { padding: 24px; }",
"report": {
"rules_total": 14, "rules_kept": 9, "rules_dropped": 5,
"elements_matched": 1, "inherited_kept": ["html", "body", ".content"],
"var_names": ["--brand", "--gap", "--text"],
"saved_bytes": 412, "reduction": 38.4
}
}
| Field | Meaning |
|---|---|
output | the cleaned CSS as a single string |
charged · price · balance | what was charged, the price, the remaining balance |
report.saved_bytes · report.reduction | bytes saved and the percentage |
report.rules_before · rules_after · duplicates · empty · merged_groups | dedup stats: rules before/after, duplicates, empty rules, merges |
report.rules_kept · rules_dropped · elements_matched · keyframes_dropped · fonts_dropped | filter stats: kept, dropped, how many elements matched |
report.inherited_kept · var_names · decls_pruned · target | for a block: inherited selectors, variable names, pruned declarations |
Errors
Every error is JSON of the shape {"error": "…"}.
| HTTP | Meaning |
|---|---|
400 | missing css/html or malformed JSON |
401 | key unknown or not supplied |
402 | insufficient balance |
403 | key disabled |
404 | unknown endpoint, e.g. /api/v1/pretty |
413 | input larger than 2 MB |
429 | daily free limit reached |
Limits
- up to 2 MB of CSS per request — more than any real site needs
- up to 60 seconds per request
- 20 free requests per day per IP without a key
- you pay per request, not per byte: 2 MB and 2 KB cost the same
Code samples
JavaScript (fetch)
const r = await fetch("https://css-cleaner.ru/api/v1/filter", {
method: "POST",
headers: { "Content-Type": "application/json", "X-API-Key": "csscl_XXXX" },
body: JSON.stringify({ html: document.documentElement.outerHTML, css, drop_keyframes: true }),
});
const { output, report } = await r.json();
console.log(`−${report.saved_bytes} б, −${report.rules_dropped} правил`);
Python (requests)
import requests
r = requests.post("https://css-cleaner.ru/api/v1/block",
headers={"X-API-Key": "csscl_XXXX"},
json={"html": html, "css": css, "target": ".card", "prune": True},
timeout=60)
r.raise_for_status()
print(r.json()["output"], r.json()["balance"])