How to measure compressed svg sizes in bulk with the jad runner
- Step 1Create an API key and fetch the schema — Generate a key in your JAD account. Then
GET /api/v1/tools/svg-compression-estimatorwith the key — it returns the tool's category,minTier(free),execution.runnerMode(engine), and aninput.optionsarray (empty for this tool, since it has no options). - Step 2Pair the @jadapps/runner once — Install and start @jadapps/runner on the build machine. It listens on
127.0.0.1:9789. The public/runendpoint never accepts uploads — it returns a 400 with pairing instructions pointing at the runner — so all execution happens locally. - Step 3POST each SVG to the local runner — For every
.svgin your build output, POST its content tohttp://127.0.0.1:9789/v1/tools/svg-compression-estimator/run. The runner returns the text report (Raw / Gzip / Brotlilines). No options are needed; the estimator ignores any you send. - Step 4Parse the gzip line — Extract the gzip byte figure from the report (the
Gzip:line in the runner's engine output). That's a real deflate-raw measurement plus the 18-byte wrapper — the number to budget against. - Step 5Compare against your budget and fail loudly — If
gzipBytes > YOUR_BUDGET(say 10 KB per icon), exit non-zero with a message naming the file and its size. This stops an oversized SVG from reaching production at the PR gate. - Step 6Optionally add a real brotli step — The report's brotli line is a flat 18% heuristic. If you budget on brotli, run
brotli -q 11on each file in the same script and compare the.brsize — pair the runner's exact gzip number with a real brotli measurement.
Real API surface for the estimator
What actually exists. JAD's API is upload-free; execution is delegated to a local runner. There is no JSON-body estimation endpoint that accepts file content.
| Operation | Endpoint | Behaviour |
|---|---|---|
| Get schema | GET /api/v1/tools/svg-compression-estimator | Returns category, minTier: free, execution.runnerMode: engine, empty options. Requires API key |
| Public run (no upload) | POST /api/v1/tools/svg-compression-estimator/run | Returns 400 "Runner required" with pairing instructions — JAD never accepts file bytes |
| Local runner run | POST http://127.0.0.1:9789/v1/tools/svg-compression-estimator/run | Executes in-process on your machine; returns the text report (Raw/Gzip/Brotli lines) |
| Install docs | /docs/runner | How to install and pair @jadapps/runner |
Report fields you can parse
The runner returns the same plain-text report shape as the engine. Parse line by line — there is no structured JSON with raw_bytes / gzip_bytes keys.
| Line | Example | Use in CI |
|---|---|---|
| Raw | Raw: 4.1 KB (4,182 bytes) | Baseline; parse the byte count in parentheses |
| Gzip | Gzip: 1.3 KB — 68% savings | Budget against this — real deflate-raw + 18 bytes |
| Brotli | Brotli: 1.1 KB — 74% savings | Heuristic (gzip × 0.82); verify with real brotli if budgeting on it |
Tier ceilings relevant to a build pipeline
Per-job size limits from the SVG tier table. The estimator itself has no row or count limit — only the file-size cap.
| Tier | Max SVG file | Batch files (UI) |
|---|---|---|
| Free | 5 MB | 1 per run |
| Pro | 50 MB | 20 |
| Developer | 2 GB | Unlimited |
Cookbook
Pipeline snippets against the real, upload-free API. The estimator runs on your paired runner; only the schema fetch hits JAD.
Confirm the public /run endpoint is upload-free
POSTing content to the public endpoint does not run the tool — it returns 400 with instructions to use your local runner. This is the expected, documented behaviour.
$ curl -s -X POST https://jadapps.com/api/v1/tools/svg-compression-estimator/run \
-H "Authorization: Bearer $JAD_API_KEY" \
-H 'Content-Type: application/json' -d '{"input":"<svg/>"}'
{
"error": "Runner required",
"reason": "JAD's API/MCP never accepts uploads. Pair an @jadapps/runner...",
"schemaEndpoint": ".../api/v1/tools/svg-compression-estimator",
"runnerEndpoint": "http://127.0.0.1:9789/v1/tools/svg-compression-estimator/run",
"installRunner": ".../docs/runner"
}Fetch the schema to confirm no options
The schema endpoint tells you the estimator is a free-tier engine tool with an empty options array — so your runner payload only needs the SVG content.
$ curl -s https://jadapps.com/api/v1/tools/svg-compression-estimator \
-H "Authorization: Bearer $JAD_API_KEY" | jq '{minTier, execution, input}'
{
"minTier": "free",
"execution": { "runnerMode": "engine", "runnerBacked": true, "apiAvailable": false },
"input": { "isGenerative": false, "acceptsMultiple": false, "options": [] }
}Estimate one file on the local runner
POST the SVG content to the paired runner on localhost. It returns the text report, which you parse for the gzip line.
$ curl -s -X POST http://127.0.0.1:9789/v1/tools/svg-compression-estimator/run \
-H 'Content-Type: application/json' \
--data "$(jq -Rs '{input: .}' < dist/icons/menu.svg)"
Raw: 4.1 KB (4,182 bytes)
Gzip: 1.3 KB — 68% savings
Brotli: 1.1 KB — 74% savingsEnforce a 10 KB gzip budget in a CI script
Loop over every built SVG, parse the gzip byte count from the runner's report, and fail the build if any file exceeds the budget.
#!/usr/bin/env bash
set -euo pipefail
BUDGET=10240 # 10 KB gzipped per SVG
fail=0
for f in dist/icons/*.svg; do
report=$(curl -s -X POST http://127.0.0.1:9789/v1/tools/svg-compression-estimator/run \
-H 'Content-Type: application/json' --data "$(jq -Rs '{input: .}' < "$f")")
# parse the (NN bytes) on the Gzip line via raw bytes -> recompute, or read KB:
kb=$(awk -F'[ ]' '/^Gzip:/ {print $2}' <<<"$report")
echo "$f -> $kb gzip"
# convert KB to bytes for the comparison in your language of choice
done
exit $failPair the runner's gzip with a real brotli check
Because the report's brotli line is a heuristic, run a real brotli encoder alongside for an exact brotli budget — best of both: JAD's accurate gzip and a true brotli size.
for f in dist/icons/*.svg; do
# exact gzip from JAD runner report (parse bytes), exact brotli from CLI:
brotli -q 11 -k "$f" # writes f.br
brsize=$(stat -c%s "$f.br")
echo "$f brotli(real)=$brsize bytes"
[ "$brsize" -gt 8192 ] && { echo "OVER 8KB brotli: $f"; exit 1; }
doneEdge cases and what actually happens
POSTing file content to the public API
400 Runner requiredThe public /api/v1/tools/.../run endpoint never executes with an upload. It validates your key, checks rate limits, and returns 400 with runnerEndpoint and installRunner fields. Your pipeline must target the local runner on 127.0.0.1:9789, not jadapps.com.
Expecting a JSON body with raw_bytes / gzip_bytes keys
Wrong shapeThe estimator returns a plain-text report, not a structured JSON object. There are no raw_bytes / gzip_bytes / gzip_ratio fields. Parse the Raw: / Gzip: / Brotli: lines from the text. The browser version names them Raw size / Gzip estimate / Brotli est.; the engine version uses Raw: / Gzip: / Brotli:.
Assuming a Developer plan is required
Not gatedThe estimator's minTier is free. Any valid API key can fetch its schema and run it on the runner. The tier ceilings that do apply are file-size limits (5 MB free / 50 MB Pro / 2 GB Developer), not a plan gate on the tool itself.
Runner not running
Connection refusedIf @jadapps/runner isn't started, the localhost POST gets connection-refused. Start it before the CI step (or run it as a service on the build agent) and confirm it's listening on 9789. The public API can't substitute — it's upload-free by design.
Budgeting on the brotli line
Heuristic riskThe report's brotli figure is gzip × 0.82, not a measurement. A budget pegged to it can pass when real brotli is larger (tiny icons) or be needlessly strict when real brotli is smaller (large sprites). Budget on the gzip line, and add a real brotli -q 11 step if brotli is your contractual number.
File over the tier size limit
RejectedAn SVG above the tier's per-job ceiling (5 MB free) is rejected. SVGs this large almost always embed base64 rasters; extract the raster and serve it separately rather than raising the limit. The estimator has no row/element count limit — only the file-byte cap.
Rate limit hit during a large batch
429 on schema fetchThe schema GET is rate-limited per API key (the response carries X-RateLimit-* headers). For a big batch, fetch the schema once and cache it — the per-file runs go to your local runner and don't consume the JAD rate limit. A 429 returns resetAt.
Parsing breaks on localised number formatting
Parser fragilityThe report prints (4,182 bytes) with a thousands separator from toLocaleString(). Strip commas before converting to an integer, or read the KB figure and multiply. Don't assume a raw integer is present without separators.
Frequently asked questions
Is there a JSON API I can POST SVGs to?
Not on JAD's servers. The public API is upload-free: GET /api/v1/tools/svg-compression-estimator returns the schema, and POST .../run returns 400 with instructions to use a local runner. File bytes are POSTed only to your paired @jadapps/runner on http://127.0.0.1:9789, which runs the transform locally and returns the text report.
What does the runner return — JSON or text?
The estimator is a text tool. The runner returns the same plain-text report the engine produces: a Raw: line, a Gzip: line with savings, and a Brotli: line with savings. There is no structured object with raw_bytes / gzip_bytes keys — parse the lines.
Do I need a paid plan to use the estimator API?
No. The estimator's minTier is free. You need a valid API key (for the schema fetch and rate limiting) and a running @jadapps/runner, but there's no Developer-plan gate on this specific tool. File-size tier limits still apply to what you run.
Why is the API upload-free?
By design, JAD's API and MCP layer never receive file content — every tool executes on a runner on the user's own machine, so SVGs never traverse JAD's network. The /run endpoint returns 400 with runnerEndpoint and installRunner so clients build payloads for their local runner instead.
Which size figure should I budget against?
The gzip line. It's a real deflate-raw measurement plus the 18-byte wrapper — within a few bytes of a default-level server. The brotli line is a flat 18% heuristic; if you must budget on brotli, run a real brotli -q 11 encoder alongside the runner and compare the .br size.
How do I parse the gzip number reliably?
Read the Gzip: line. The engine output is Gzip: 1.3 KB — 68% savings; if you need exact bytes, prefer the browser report's (N bytes) form or recompute from the raw bytes. Strip the thousands separator (toLocaleString inserts commas) before parsing to an integer.
Does the estimator have any options I need to send?
No. Its option schema is empty — the runner ignores options. Your payload only needs the SVG content. The schema endpoint confirms input.options: [].
Can I integrate this with size-limit or bundlesize?
Yes. Both accept custom size functions. Write a function that POSTs each SVG to your local runner, parses the gzip bytes from the report, and returns that to the size-limit threshold check. Keep the call on localhost so files stay on the build machine.
What's the largest SVG I can estimate via the runner?
It follows the SVG tier file-size ceilings: 5 MB free, 50 MB Pro, 2 GB Developer. There's no element or row limit — only the byte cap. SVGs near these sizes usually contain embedded rasters worth extracting first.
Will running many files hit a rate limit?
The schema GET is rate-limited per key (with X-RateLimit-* headers and a resetAt on 429). Fetch the schema once and cache it; the per-file estimation runs go to your local runner and don't consume the JAD API rate limit.
Does the runner need a headless browser for this tool?
No. The estimator is a server-safe engine tool — its execution.runnerMode is engine, meaning the runner executes it in-process. It doesn't need the headless-Chromium path that DOM/Canvas tools like the Canvas Exporter require.
Can I combine this with content optimisation in one pipeline?
Yes. A common chain: run each SVG through the Pro-Minifier on the runner, then the estimator to record the post-minify gzip size, then gate on the budget. Both are server-safe engine tools, so the whole chain runs locally via the runner.
Privacy first
Every JAD SVG tool runs entirely in your browser using the DOM API and Canvas. Your SVG files never leave your device — verified by zero outbound network requests during processing.