Program

API

The account key, three engines, errors, limits, and idempotency.

Base path /api/v1 on this site. Send JSON. Create a key on the Account page. That same key is what a program sends here, what an MCP client sends to /mcp, and what a download uses. The secret is shown once. We store a SHA-256 hash and a short prefix so you can tell keys apart. The Free plan allows 5 active keys. Revoke one before creating another. Revocation is immediate.

Authentication

X-API-Key: qed_live_… Authorization: Bearer qed_live_…

Either header. Keys do not belong in the query string. A missing key is 401 missing_api_key. An unknown or revoked key is 401 invalid_api_key. Neither is counted against the quota.

Solve

POST /api/v1/solve { "equations": [ "x + y = 3", "x - y = 1" ], "api": "rust" }

equations is 1 to 200 strings, each 1 to 20,000 characters after trimming. A blank line is rejected. api is rust (the interpreter, and the default), compiler, or legacy (the original engine). Those are the Interpreter, Compiler, and Old choices in the workspace. Other fields are ignored.

Engines

"api": "rust" interpreter, the default "api": "compiler" compiler "api": "legacy" the original engine

One account key covers all three. You do not mint a separate key per engine.

A program that runs returns HTTP 200, including a program that does not solve. Check Solved. A failed solve still counts toward the quota, because the work was done. request_id is added by this site. The rest of the body is the solver result the editor uses.

Response

{ "Solved": true, "api": "rust", "ExecutionTimeSeconds": "0.004", "request_id": "req_…", "newJSON": [ { "ExecutionInfo": { "Iterations": 1, "AverageResidual": "0", "LargestResidual": "0" }, "variables": [ { "name": "x", "value": "2" }, { "name": "y", "value": "1" } ], "statements": [], "functions": [], "charts": [] } ] }

When Solved is false, read ErrorMessage. Values are strings, in the same form the editor shows, including ʝ. charts holds plot data when the program plots. ExecutionInfo also includes Timestamp and UTCTimestamp.

Every metered response carries X-Request-Id, X-RateLimit-Limit-Minute, X-RateLimit-Remaining-Minute, X-RateLimit-Limit-Day, and X-RateLimit-Remaining-Day. A 429 also carries Retry-After, in seconds.

Errors

{ "Solved": false, "request_id": "req_…", "error": { "code": "rate_limited", "message": "…", "request_id": "req_…" } } 200 Solved false The call ran. Read ErrorMessage. 400 invalid_body Not {"equations":["…"]}. 400 too_many_equations More than 200 lines. 400 invalid_equation A line is empty or over 20,000 characters. 400 invalid_idempotency_key Idempotency-Key is longer than 200 characters. 401 missing_api_key No key header. Not metered. 401 invalid_api_key Unknown or revoked. Not metered. 405 method_not_allowed Not POST. Not metered. 409 idempotency_conflict Same Idempotency-Key, different body. 413 body_too_large Body over 256 KB. Not metered. 429 rate_limited Minute cap, Retry-After 60. Or day cap, Retry-After 3600. 502 upstream_error The solver could not be reached. 503 solver_unavailable That engine is not configured. Not metered.

401, 400, 405, 413, and 503 are not recorded. 200, 409, 429, and 502 are. A 429 counts, including the call that crossed the line, so a tight retry loop stays limited.

Limits

GET /api/v1/usage { "plan": { "name": "Free", "requestsPerMinute": 60, "requestsPerDay": 5000 }, "key": { "id": "…", "prefix": "qed_live_…" }, "minute": { "used": 2, "limit": 60, "remaining": 58 }, "day": { "used": 2, "limit": 5000, "remaining": 4998 } }

The Free plan costs $0. Limits exist so a loop, an agent, or a shared key cannot take the solver down. Windows are fixed, not sliding. The minute counter resets at the next UTC minute, and the day counter at the next UTC midnight. Both are stored in the database, so they hold across instances. This call does not spend a request. The same key header is required.

Idempotency

Idempotency-Key: solve-demo-1

Send this header on POST. The same key and the same body replay the stored response for 24 hours and do not spend another request. The replay carries Idempotent-Replayed: true. The same key with a different body, including a different api, is 409 idempotency_conflict, and that conflict does spend a request. Use this when an agent might retry after a dropped connection.

The lines are the same program you would put in the editor. A comment starts with //. = is an equation. := is a guess or a constant. ʝ is the imaginary unit. The other pages of these docs are the language. If a system does not converge, add a guess near the solution you want. Pass "api":"compiler" for the compiler, or "api":"legacy" for the original engine. Download is the next page.

curl

curl -s https://development.qedsolver.com/api/v1/solve \ -H "Authorization: Bearer $QED_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: solve-demo-1" \ -d '{"equations":["2a + 3b = 8","a - b = 1"],"api":"rust"}'

Keep the key in an environment variable, not in source.

Python

import os, requests r = requests.post( "https://development.qedsolver.com/api/v1/solve", headers={"Authorization": f"Bearer {os.environ['QED_API_KEY']}"}, json={"equations": ["phi = 1 + 1/phi"], "api": "rust"}, timeout=30, ) r.raise_for_status() body = r.json() if not body["Solved"]: raise SystemExit(body.get("ErrorMessage") or body["error"]["message"]) print({v["name"]: v["value"] for v in body["newJSON"][0]["variables"]}) print("remaining today", r.headers["x-ratelimit-remaining-day"])

raise_for_status catches 4xx and 5xx. A program that ran but did not solve is still HTTP 200, so check Solved.

Node

const res = await fetch("https://development.qedsolver.com/api/v1/solve", { method: "POST", headers: { Authorization: `Bearer ${process.env.QED_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ equations: ["(1 + ʝ) * z = 2"], api: "rust" }), }); if (res.status === 429) { const wait = Number(res.headers.get("retry-after") ?? "1"); await new Promise((r) => setTimeout(r, wait * 1000)); } const body = await res.json(); console.log(body.Solved ? body.newJSON[0].variables : body);

On 429, wait Retry-After seconds before trying again.

© 2026 QED Solverworks LLC