Diceplots API private beta
Three tiers, the same Rust engine. Free unauthenticated for
casual integration, free registered for serious development,
paid for production traffic. The first endpoint cohort is
live — Strike, Compare, Strikes-to-kill, Cascades, plus
Helpers discovery — with an OpenAPI 3 spec and Swagger UI
at /api/v1/docs.
Tiers
| Tier | Auth | Rate limit | Suitable for |
|---|---|---|---|
| Free unreg | None — anonymous | 30 requests / minute / IP | Try-it-out, scripts, low-volume integrations. |
| Free reg | Account + API key | 300 requests / minute / key | Personal projects, dev work, low-traffic apps. |
| Paid | API key on a paid account | 3,000 requests / minute / key | Production traffic, embedded engine calls, latency-sensitive use. |
Per-key limits are tracked by sliding-window in ETS; the
free-unreg bucket is keyed by remote IP (with
X-Forwarded-For handled at Caddy). On limit
exceeded, the API returns 429 with a JSON body
naming the resolved tier and the per-window cap. Paid-tier
pricing and bulk-purchase contact are below.
Endpoints
Six routes under /api/v1/*. All POSTs take JSON
bodies; all 2xx responses share an envelope
{ meta: { tier, engine_version, request_id }, result: ... }.
Probabilities are returned as both a f64 approximation
and a reduced-rational *_exact string ("p/q" or "p")
— exactness is the product, don't round-trip through floats.
-
POST /api/v1/strike — distribution + kill probability
for one expression. Same grammar as
/strike/:slug; optional
hpand per-damage-typeresistances. -
POST /api/v1/compare — two expressions at one HP,
side-by-side distributions plus the signed
kill_probability_delta. Sweephpclient-side to plot the crossover curve. -
POST /api/v1/strikes-to-kill — expected strikes
+ cumulative kill curve up to
max_strikes. Thesourcefield distinguishes exact-rational results from the f64 fallback path. -
POST /api/v1/cascades — GWM-on-kill, PAM glaive,
any cascade clause. Depth + queue HP are encoded in the
expression itself (
cascade Norcascade [HP1, HP2, …]). The same machinery that powers the cascade pillar. - GET /api/v1/helpers + /helpers/:name — discovery for every MCP-callable engine helper (Strike, Lancer attack, Daggerheart duality, Year Zero pool, …) with full arg schemas. Drives the MCP server too.
Examples
Anonymous (free_unreg, 30/min/IP):
$ curl -s https://diceplots.com/api/v1/strike \
-H "content-type: application/json" \
-d '{"expression":"2d6+5","hp":11}'
{
"meta": {
"tier": "free_unreg",
"engine_version": "0.1.0+sha.91b3979",
"request_id": "GUv8h4iY...XK"
},
"result": {
"expression": "2d6+5",
"min": 7, "max": 17,
"mean": 12.0, "mean_exact": "12",
"kill_probability": 0.7222,
"kill_probability_exact": "13/18",
"outcomes": [{"value":7,"probability":0.0277,"probability_exact":"1/36"}, ...]
}
}
Authenticated (free_reg, 300/min/key):
$ curl -s https://diceplots.com/api/v1/compare \
-H "Authorization: Bearer dp_live_..." \
-H "content-type: application/json" \
-d '{"a":"2d6+5","b":"1d12+5","hp":11}' \
| jq '.result.kill_probability_delta_exact, .result.a.kill_probability_exact, .result.b.kill_probability_exact'
Helper discovery:
$ curl -s https://diceplots.com/api/v1/helpers | jq '.result.count, .result.helpers[0]'
$ curl -s https://diceplots.com/api/v1/helpers/strike | jq '.result.args'
Rate-limit body when exhausted:
HTTP/2 429
content-type: application/json
{
"error": "rate_limited",
"tier": "free_unreg",
"limit_per_window": 30,
"window_ms": 60000,
"see": "https://diceplots.com/platforms/api"
}
Spec + Swagger UI
The full OpenAPI 3 document lives at
/api/v1/openapi.json
— point your generator at it for typed clients in any language.
Interactive "Try it" lives at
/api/v1/docs; the
Authorize button accepts a bearer key so you can exercise the
authenticated path against your account.
Accounts and keys
Sign up at /sign-up with email and
password — confirmation link arrives in your inbox; the
account is usable immediately at the
free_reg tier. The
account dashboard manages keys
end-to-end: create, copy-once, revoke, last-used timestamps.
Keys are bcrypt-hashed at rest with a public prefix for
identification; the plaintext is shown exactly once at
creation.
Paid-tier pricing is per-engagement — drop a line to tom@defensiblelogic.com with the rough call volume and integration shape and we'll come back with terms.