How to script brand color swaps across svgs with the runner-backed api
- Step 1Provision an API key on a paid tier — Free tier has no API access (rate limit 0). Create a key on Pro, Pro-media, or Developer. The tool itself is Pro-gated, so the key tier must be Pro or higher.
- Step 2Fetch the tool schema —
GET /api/v1/tools/svg-hex-swapperwith yourAuthorizationheader returns the option schema, includingcolorPairs(typecolor-pairs, default[{ from: "#000000", to: "#6366f1" }]) and the execution mode (engine, runner-backed). - Step 3Pair the @jadapps/runner locally — Install and pair the runner from
/docs/runner. It listens onhttp://127.0.0.1:9789. The hosted/runendpoint will refuse content and point you here; the runner is where execution actually happens. - Step 4Build the colorPairs payload — Construct
{ options: { colorPairs: [{ from: "#003082", to: "#1a56db" }, …] } }plus the SVG content. Use full hex; the engine expands 3-digit hex itself but matches literal strings, sofrommust be the hex actually in your files. - Step 5POST each file to the runner — Send one SVG per call to
127.0.0.1:9789/v1/tools/svg-hex-swapper/run. There is no multi-file or ZIP endpoint — iterate your directory in code. Capture the returned recoloured SVG and the 'Colors swapped' count. - Step 6Verify and commit — Diff the output against the source (a clean diff shows exactly which colour strings changed), run a visual regression pass, then write the files back and commit. Build your own summary from the per-file swap counts.
API surface for svg-hex-swapper
What the hosted API does versus what the local runner does. The hosted /run never executes — it returns pairing instructions.
| Endpoint | Method | Purpose |
|---|---|---|
/api/v1/tools/svg-hex-swapper | GET | Returns option schema, minTier, execution mode (engine) |
/api/v1/tools/svg-hex-swapper/run | POST | Returns 400 + runner pairing instructions (never accepts uploads) |
127.0.0.1:9789/v1/tools/svg-hex-swapper/run | POST | Local runner — executes the swap on your machine |
Per-key rate limits
Rate limit is per API key, by account tier, surfaced in X-RateLimit-Limit / -Remaining / -Reset headers. There is no per-call file-count limit because each call is one file.
| Tier | Requests / hour | API access |
|---|---|---|
| Free | 0 | No (tool is Pro-gated anyway) |
| Pro | 100 | Yes |
| Pro-media | 500 | Yes |
| Developer | Unlimited | Yes |
colorPairs option shape
The single option the swapper accepts. Each entry is a from/to mapping applied left-to-right.
| Field | Type | Notes |
|---|---|---|
colorPairs | array of { from, to } | Default [{ from: "#000000", to: "#6366f1" }]; applied in array order |
from | string | Lowercased + trimmed + short-hex-expanded, then literal match |
to | string | Written verbatim into the output |
Cookbook
Patterns for scripting the swap through a paired runner. Adapt the pseudo-code to your language; the payload shape is what matters.
Discover the schema before building payloads
Always read the live schema rather than hard-coding option names. It confirms colorPairs and the runner-backed execution mode.
GET /api/v1/tools/svg-hex-swapper
Authorization: Bearer <API_KEY>
200 OK (excerpt):
{
"minTier": "pro",
"execution": { "runnerMode": "engine", "runnerBacked": true,
"apiAvailable": false },
"input": { "acceptsMultiple": false,
"options": [ { "name": "colorPairs",
"type": "color-pairs",
"default": [{"from":"#000000","to":"#6366f1"}] } ] }
}The hosted /run rejects uploads on purpose
Sending content to the cloud endpoint returns a 400 with the runner address. This is the intended flow, not an error to retry.
POST /api/v1/tools/svg-hex-swapper/run
Authorization: Bearer <API_KEY>
{ "options": { "colorPairs": [...] }, "file": "<svg…>" }
400 Bad Request:
{
"error": "Runner required",
"runnerEndpoint": "http://127.0.0.1:9789/v1/tools/svg-hex-swapper/run",
"schemaEndpoint": ".../api/v1/tools/svg-hex-swapper",
"installRunner": ".../docs/runner"
}Dispatch one file to the local runner
The runner accepts the SVG plus options and returns the recoloured text and metrics. One file per call.
POST http://127.0.0.1:9789/v1/tools/svg-hex-swapper/run
{
"file": "<svg viewBox='0 0 24 24'><path fill='#003082'.../></svg>",
"options": { "colorPairs": [
{ "from": "#003082", "to": "#1a56db" },
{ "from": "#e02d3c", "to": "#e3342f" }
] }
}
Result:
recoloured SVG text + metrics { "Colors swapped": 5 }Loop a directory in your own code
There is no batch endpoint. Iterate files, call the runner per file, and collect the swap counts into your own report.
pairs = load('colors.json') # [{from,to}, ...]
report = []
for f in glob('icons/**/*.svg'):
svg = read(f)
res = post(RUNNER, { file: svg, options: { colorPairs: pairs } })
write(f, res.svg)
report.append({ file: f, swapped: res.metrics['Colors swapped'] })
save('swap-report.json', report) # your own report, not the API'sNormalise to hex first when files mix formats
The engine matches literal strings and won't catch rgb()/hsl() forms. If your exports vary, pre-normalise to hex so the same colors.json catches every instance.
Problem: some files use fill="rgb(0,48,130)" Pair #003082 -> #1a56db misses them (literal match). Fix in your pipeline before the swap: - re-export icons with hex colours, OR - run a regex pass converting known rgb() values to hex, then dispatch the swap so every instance matches.
Edge cases and what actually happens
POST content to the hosted /run
400By design, /api/v1/tools/svg-hex-swapper/run never accepts uploads. It returns 400 { error: "Runner required" } with the local runner endpoint, schema endpoint, and install link. Send your payload to the runner instead.
Free-tier API key
BlockedFree tier's rate limit is 0 — no API calls succeed. The tool is Pro-gated as well, so you need a Pro, Pro-media, or Developer key to use it at all.
Rate limit exceeded
429Exceeding your tier's hourly quota returns 429 { error: "Rate limit exceeded", resetAt } with X-RateLimit-* headers. Pro allows 100/hr, Pro-media 500/hr, Developer is unlimited. Back off until resetAt or batch your loop within quota.
Expecting a multi-file or ZIP endpoint
Not supportedacceptsMultiple is false for the Hex Swapper — there is no batch or ZIP API. Iterate files in your own code and call the runner once per file.
Looking for an exclude_files flag
Not a featureThere is no server-side exclude list. To skip files during a rebrand, filter them out in your loop before dispatching — the tool only knows about the single SVG you send it.
Expecting a change-report.json from the API
Build your ownThe engine returns a per-file 'Colors swapped' count, not a multi-file report. Aggregate the counts yourself across your loop if you want a summary artifact.
from value is rgb()/named, files are hex
No matchThe engine compares literal strings. A non-hex from won't match hex content (and vice versa). Keep your colors.json in the exact format your files use, or normalise both to hex.
Pairs cascade unexpectedly
By designPairs apply in array order. If an earlier pair's to equals a later pair's from, the later pair will also rewrite the colours the earlier one produced. Order the array to avoid an unintended chain (A→B then B→C makes A→C).
File over the tier size limit
RejectedThe SVG family per-job ceiling is 50 MB on Pro and 2 GB on Developer. Oversized files are rejected before processing. Split or optimise huge SVGs (try the Pro-Minifier) first.
Frequently asked questions
Does JAD's API receive my SVG files?
No. The hosted /run endpoint refuses uploads with a 400 and points you to a local @jadapps/runner. The runner accepts the same payload on 127.0.0.1:9789 and executes the swap on your machine, so SVG content never leaves your network. The hosted API only handles auth, rate limiting, and the schema.
How do I get the exact option names to send?
GET /api/v1/tools/svg-hex-swapper returns the schema. For this tool the only option is colorPairs — an array of { from, to } with default [{ from: "#000000", to: "#6366f1" }]. The response also reports runnerMode: engine and apiAvailable: false (runner-backed).
Is there a batch endpoint for a whole folder?
No. acceptsMultiple is false — one SVG per call. Loop your directory in your own script and POST each file to the local runner. There's no ZIP input or multi-file response.
What are the rate limits?
Per API key, by tier: Free 0 (no access), Pro 100/hr, Pro-media 500/hr, Developer unlimited. Limits are reported in X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers; exceeding yours returns 429.
Can I exclude certain files from the swap?
Not via the API — there's no exclude flag. Filter the files in your own loop before dispatching. The tool only ever sees the single SVG you send, so any selection logic lives in your code.
Does the API give me a report of what changed?
Per file, the runner result includes a 'Colors swapped' count. There's no multi-file change-report.json — assemble one yourself by collecting the counts (and diffs) across your loop.
How does case-insensitive matching work?
The engine lowercases and trims the from value, expands 3-digit hex to 6-digit across the document, then matches case-insensitively. So #FF0000, #ff0000, and #Ff0000 are all the same from. The to value is written exactly as you supply it.
Will a hex from value catch rgb() colours?
No. Matching is literal-string, with no colour-format conversion. If your files mix hex and rgb()/hsl(), normalise them to hex before the swap, or include both string forms as separate pairs.
Why did one file change colours I didn't intend?
Two likely causes: a literal from string also appears inside an id, comment, or text node; or your pairs cascaded (an early pair's target matched a later pair's source). Diff the output and reorder or tighten your pairs.
Can I run this in CI?
Yes. A typical flow: a CI job runs the paired runner, loops the icon directory, applies your colors.json, runs visual regression (e.g. Storybook + a snapshot diff), and commits. Keep the source files in version control so a swap is always revertible.
What tier do I need?
Pro or higher. Free has no API access and the tool is Pro-gated. Developer adds unlimited rate and the 2 GB per-job ceiling for very large SVGs.
Is the API result identical to the web tool?
Yes. The runner calls the same server-safe engine (hexSwap) the website uses for this tool, so a given input and colorPairs produce the same recoloured output and the same swap count in both places.
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.