Read endpoints are CORS-open and unauthenticated. Writes (posting a test, webhook config, crypto redemption) require a Bearer key. All payloads are JSON; all timestamps are ISO-8601 UTC; all hashes are SHA-256 prefixed.
https://api.benchlist.ai/v1
Static JSON mirrors of registry data are served under https://benchlist.ai/api/*.json, no auth, edge-cached (s-maxage 300s). Use them for dashboards; use the versioned API under api.benchlist.ai/v1 for writes and anything real-time.
All versioned endpoints return application/json with UTF-8. Breaking changes land behind /v2; /v1 is supported for ≥ 18 months after /v2 launches.
Reads need nothing. Writes require a Bearer key, which is free to obtain, email verification only. Pay-as-you-go · credit packs from $25 (6 tests) · volume discounts up to 33%.
Authorization: Bearer bl_live_...
Getting a key:
Rotate with POST /v1/keys/rotate. Keys scope to a single publisher; you can issue sub-keys per environment.
400 invalid payload shape401 missing / bad Bearer key402 insufficient credits403 publisher suspended (dispute upheld) 404 unknown service / benchmark / run id409 duplicate submission (by transcriptMerkleRoot)422 on-chain verification failed (bad tx, wrong amount, reverted)429 rate-limited; retry after Retry-After header5xx transient, automatic retry with idempotency keyAll errors return { "error": "...", "detail": "...", "request_id": "req_..." }. Include the request_id when contacting support.
Enterprise tier lifts all limits; email dev@remlabs.ai.
Submits a (service, model, benchmark) tuple for attestation. The runner queues the job on an available attestor, executes the benchmark, builds the Merkle tree, generates the proof, submits to Aligned Layer, and settles on Ethereum L1.
POST https://api.benchlist.ai/v1/run
Authorization: Bearer bl_live_...
Content-Type: application/json
{
"service": "anthropic-claude", // service id (see /v1/services)
"model": "claude-opus-4-7", // your model identifier
"benchmark": "mbpp", // benchmark id
"runs": 3, // number of runs to average; 1-10
"proof_system": "sp1", // optional: sp1 | risc0 | halo2 | groth16 | plonk
"attestor": "auto", // optional: attestor id or "auto"
"webhook": "https://me.com/hk", // optional: override default webhook
"idempotency_key": "2026-04-23-a" // optional
}
{
"run_id": "run-8f3a...",
"status": "queued",
"est_seconds": 180,
"charge": { "credits": 1, "usd": 5.00 },
"verify_url": "https://benchlist.ai/verify/run-8f3a..."
}
Poll GET /v1/run/:id or subscribe to run.verified webhook.
curl -X POST https://api.benchlist.ai/v1/run \
-H "Authorization: Bearer $BENCHLIST_KEY" \
-H "Content-Type: application/json" \
-d '{"service":"anthropic-claude","model":"claude-opus-4-7","benchmark":"mbpp","runs":3}'import os, httpx
r = httpx.post(
"https://api.benchlist.ai/v1/run",
headers={"Authorization": f"Bearer {os.environ['BENCHLIST_KEY']}"},
json={"service": "anthropic-claude", "model": "claude-opus-4-7",
"benchmark": "mbpp", "runs": 3},
timeout=30.0,
)
print(r.json()["verify_url"])const r = await fetch("https://api.benchlist.ai/v1/run", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.BENCHLIST_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
service: "anthropic-claude",
model: "claude-opus-4-7",
benchmark: "mbpp",
runs: 3
})
});
const { verify_url } = await r.json();
console.log(verify_url);import "net/http"; import "bytes"
body := bytes.NewBufferString(`{"service":"anthropic-claude","model":"claude-opus-4-7","benchmark":"mbpp","runs":3}`)
req, _ := http.NewRequest("POST", "https://api.benchlist.ai/v1/run", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("BENCHLIST_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)Polls the current state of a submitted run. Status transitions: queued → running → committed → proving → submitted → verified (or failed).
GET https://api.benchlist.ai/v1/run/run-8f3a...
{
"run_id": "run-8f3a...",
"status": "verified",
"score": 87.3,
"verification": {
"mode": "aligned",
"alignedBatchId": "0x7b3c...",
"alignedVerifierContract": "0xeF2A435e5EE44B2041100EF8cbC8ae035166606c",
"onchainBlock": 22184921,
"onchainTx": "0x1a2b...",
"verifiedAt": "2026-04-23T18:04:12Z"
},
"transcriptMerkleRoot": "sha256:e7f9...",
"datasetHash": "sha256:a1b3...",
"methodologyHash": "sha256:c3d5..."
}
Lists recent attested runs across the registry. Supports query params ?service=, ?benchmark=, ?publisher=, ?status=, ?since=ISO8601, ?limit=1..500.
curl "https://api.benchlist.ai/v1/runs?benchmark=longmemeval&status=verified&limit=10"
For a permanent, cache-friendly mirror of the whole corpus, use /api/runs.json.
:slugReturns {services: [...]}. Each service has id, name, vendor, category, oneliner, homepage, publisher, openSource, license, tags. Static mirror: /api/services.json.
:slugEach benchmark has id, name, category, metric (accuracy | score | latency-ms | tokens-per-second | wer), maxScore, datasetHash, methodologyHash, runnerRepo. Mirror: /api/benchmarks.json.
Static mirror: /api/publishers.json.
The registered attestor set with public keys, staked ETH, and reputation scores. Mirror: /api/attestors.json.
The 16 service categories. Mirror: /api/categories.json.
Free signup, drop an email, we mint an HMAC-signed API key, mail it to you, mirror a notification to ops. No card required. Load credits when you're ready to sign.
POST https://benchlist.ai/api/v1/submit
Content-Type: application/json
{
"kind": "signup",
"contact": "you@company.com"
}
// → 200 OK
// {
// "ok": true,
// "id": "sub_a1b2c3",
// "issued": true,
// "key_delivered_to": "you@company.com",
// "forwarded": [{ "channel": "resend:user", "ok": true, "status": 200 }, ...],
// "request_id": "req_..."
// }
The same endpoint handles list, run, waitlist, dispute, and contact kinds. For signup specifically, when RESEND_API_KEY is configured, the key is delivered by email and not included in the response body; when Resend is not configured, the response body includes key so dev environments still work.
Two actions for lifecycle: verify (check a key is real + unexpired) and rotate (mint a new key for the same publisher; old key keeps working until a revocation list ships).
POST https://benchlist.ai/api/v1/keys
Content-Type: application/json
{ "action": "verify", "key": "bl_live_…" }
// → { "valid": true, "payload": { "email": "you@company.com", "iat": 1713000000000, "plan": "free_first_test", "nonce": "…" } }
POST https://benchlist.ai/api/v1/keys
Authorization: Bearer bl_live_<current>
Content-Type: application/json
{ "action": "rotate" }
// → { "rotated": true, "key": "bl_live_…", "payload": { ... } }
Anonymous free probe — no API key required, n capped at 3, rate-limited to 1 successful probe per IP per hour and 5 per day. Returns a real signed receipt at /verify/<run_id> just like a paid run.
POST https://benchlist.ai/api/v1/probe
Content-Type: application/json
{ "benchmark": "gsm8k", "model": "anthropic/claude-haiku-4-5", "n": 3 }
// → {
// "run_id": "run-probe-abc123",
// "verify_url": "https://benchlist.ai/verify/run-probe-abc123",
// "score": 100,
// "score_ci": { "lower": 38.4, "upper": 100, "half": 30.8 },
// "n": 3, "model": "...", "attestor": "benchlist-probe",
// "algo": "hmac-sha256", "duration_ms": 6334
// }
Same probe, full UX, no curl needed. /try has a benchmark dropdown, model field, and copy-paste curl snippet that updates as you change the form.
Returns the canonical 32-byte digest + Ethereum tx parameters needed to anchor any registry run on Ethereum L1. The caller signs the tx with their own wallet — no private key on our server.
GET /api/v1/aligned-attest?run_id=run-local-0b2611c0de7a&network=ethereum&from=0xYOUR_WALLET
// → {
// "ok": true,
// "digest_hex": "dae9017883eb1ed8...",
// "tx": {
// "to": "0x000000000000000000000000000000000000dEaD",
// "data": "0xdae9017883eb...",
// "gas": "0x59d8", // 23,000 units
// "maxFeePerGas": "0x...", "maxPriorityFeePerGas": "0x...",
// "chainId": 1, "type": "0x2"
// },
// "estimated_cost_eth": 0.00065,
// "ui": "https://benchlist.ai/anchor?run=run-local-0b2611c0de7a"
// }
After broadcasting the signed tx, post the resulting hash here. We pull the tx from chain, verify the calldata matches the expected digest, and record the anchor on the run.
POST /api/v1/aligned-finalize
Content-Type: application/json
{ "run_id": "run-local-0b2611c0de7a", "tx_hash": "0x...", "network": "ethereum" }
// → {
// "ok": true,
// "anchor": {
// "txHash": "0x...", "blockNumber": 21234567,
// "attestor": "0xYOUR_WALLET", "digest_hex": "...",
// "explorer_tx_url": "https://etherscan.io/tx/0x...",
// "explorer_block_url": "https://etherscan.io/block/21234567"
// }
// }
Live BatcherPaymentService contract balance + your deposit balance (if you supply ?address=…). Used to size the next deposit when running the full ZK + Aligned anchor route.
GET /api/v1/aligned-fund-status?address=0xYOUR_DEPOSIT_ADDR
// → {
// "contract": "0xb0567184A52cB40956df6333510d6eF35B89C8de",
// "contract_balance_eth": 2.58, "contract_balance_usd": 6057,
// "user_deposit_eth": 0.05, "user_deposit_usd": 117,
// "eth_price_usd": 2346.41,
// "verify_contract": "https://etherscan.io/address/0xb056..."
// }
One-page UI: pick a run, click your wallet (EIP-6963 picker — MetaMask, OKX, Rabby, Coinbase, Trust, Frame, Brave, Phantom all supported), sign + broadcast. /anchor. Mobile fallback uses deep-links to open the wallet's in-app browser.
Live stream of recent attested runs with provenance digests + trust-tier rollups. Useful for dashboards and "what's happening on Benchlist right now" tickers.
GET /api/v1/activity.json?limit=50&trust=attested&since=2026-04-26T00:00:00Z
// → {
// "ok": true, "count": 50, "total_runs": 314,
// "totals_by_trust": { "verified": 0, "attested": 38, "local": 89, "self-reported": 109, "pending": 0 },
// "stream": [
// { "id": "run-...", "ts": "...", "model": "claude-opus-4-7", "benchmark": "gsm8k",
// "score": 100, "n": 8, "trust": "attested", "verify_url": "...",
// "runner_provenance_digest": "sha256:...", "aligned_batch": null }, …
// ]
// }
Creates a Stripe Checkout session. Use this for the card flow; the response redirects to Stripe’s hosted page.
POST https://benchlist.ai/api/v1/checkout
Content-Type: application/json
// 10 plans live: test_1 · credits_25 · credits_100 · credits_500 · credits_2000
// launch_certificate · provider_verified · certified_annual · publisher_pro · contamination_index
{ "plan": "launch_certificate" }
// → { "id": "cs_...", "url": "https://checkout.stripe.com/c/pay/...", "plan": "launch_certificate", "label": "..." }
Opens the Stripe Customer Portal so subscribers can update card, cancel subs, or download invoices. Auth by Bearer key (preferred) or billing email.
POST /api/v1/customer-portal
Authorization: Bearer bl_live_...
{ "return_url": "https://benchlist.ai/account" }
// → { "url": "https://billing.stripe.com/session/...", "customer": "cus_..." }
Two-mode endpoint for the native-ETH crypto flow. Omit txHash to get the receiving address + the live ETH amount (rate pulled at request time); include it to verify a sent transaction on-chain. ERC-20 tokens are not accepted, native ETH only.
POST https://benchlist.ai/api/v1/crypto
{ "plan": "test_1" }
// → {
// "mode": "init",
// "amountUsd": 5,
// "amountEth": 0.001389,
// "ethPriceUsd": 3599.87,
// "credits": 1,
// "asset": "ETH",
// "receiver": "0xb7d4d49da62bc3af186de2ee78a59fd3002fdaad",
// "chains": [
// { "chain":"base", "chainId":8453, "address":"0xb7d4…", "explorer":"https://basescan.org/..." },
// { "chain":"ethereum", "chainId":1, "address":"0xb7d4…", "explorer":"https://etherscan.io/..." },
// { "chain":"arbitrum", "chainId":42161, "address":"0xb7d4…", "explorer":"https://arbiscan.io/..." }
// ]
// }
POST https://benchlist.ai/api/v1/crypto
{
"plan": "test_1",
"chain": "base",
"txHash": "0x123abc..."
}
// → {
// "mode": "verify",
// "verified": true,
// "chain": "base",
// "txHash": "0x...",
// "sentEth": 0.001402,
// "ethPriceUsd": 3599.87,
// "blockNumber": 18234567,
// "confirmations": 12,
// "from": "0xsender...",
// "to": "0xb7d4d49da62bc3af186de2ee78a59fd3002fdaad",
// "plan": "test_1",
// "credits": 1
// }
Verification logic (server-side): fetch eth_getTransactionByHash + eth_getTransactionReceipt → confirm tx.to == receiver and tx.value ≥ expectedEth × 0.98 (2% slippage tolerance for mempool drift) and receipt.status == 0x1. Returns 422 with a specific error string on mismatch. No contract-call parsing, native ETH only.
POST https://api.benchlist.ai/v1/webhooks
Authorization: Bearer bl_live_...
{
"url": "https://your-app.com/hooks/benchlist",
"secret": "whsec_...", // 32+ bytes; used for HMAC
"events": ["run.verified", "run.failed", "run.disputed"]
}
run.submitted, accepted into the queuerun.running, attestor picked it uprun.committed, Merkle root builtrun.proving, ZK proof generation startedrun.verified, Aligned batch confirmed on Ethereum L1run.failed, runner / proof failed (refunded)run.disputed, dispute filed by another partyrun.dispute_upheld, dispute upheld; score annulledcredits.low, < 5 credits remainingEvery webhook POST carries Benchlist-Signature: t=<ts>,v1=<hmac>. Compute HMAC-SHA256 over ts + "." + raw_body using your secret; reject if mismatch or if ts is older than 5 minutes (replay protection).
import hmac, hashlib
def verify(body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=") for p in header.split(","))
ts, sig = parts["t"], parts["v1"]
expected = hmac.new(secret.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(sig, expected)