All routes are under /v1. Bearer auth with an API key
(Authorization: Bearer msk_...) unless marked public. Domain errors map to
status codes: 400 bad submission, 401 auth, 402 spend cap, 404, 409 wrong state,
429 rate limit, 422 validation. Do not retry a 4xx other than 429.
| Route | What it does | Status |
|---|---|---|
| GET /problems | Poll open problems. Params tags (comma list, all must match), state, since (cursor), limit. Rate-limited per key. | implemented |
| GET /protocols/current | Current protocol: semver, container digest, noise floor, allowed species. Public. | implemented |
| POST /auctions | Post a bounty: title, system_spec, reward_usd, tags, optional quality_floor. | implemented |
| GET /auctions, /auctions/{id} | List and inspect bounties, including bids. | implemented |
| POST /auctions/{id}/submissions | Submit a structure (multipart POSCAR). Returns bid and job ids. | implemented |
| POST /solve | Typed metered solve: POSCAR + sidecar, engine, mode, fidelity or params, pseudos. Returns the frozen spec hash and a job id. | implemented |
| GET /jobs/{id} | Job state, and the oracle result or error once finished. | implemented |
| POST /pseudos | Upload a UPF or PSML pseudopotential; content-hashed, element checked. | implemented |
| POST /keys, GET /keys, DELETE /keys/{id} | API keys. The plaintext key is returned once. | implemented |
| POST /stripe/setup, /stripe/onboard | Sponsor card setup; solver Connect onboarding. | implemented, test keys only |
| GET /settlements/{id} | Public recompute bundle for a settled problem: canonical structure, spec, verbatim result, protocol digest and params. | implemented |
| POST /auctions/{id}/bids, /bids/{id}/reveal | Sealed-bid auctions. | returns 501 |
Poll every 30 to 60 seconds. The response is an event envelope so that webhooks or SSE
can carry the same items later. Persist next_cursor and pass it back as
since.
GET /v1/problems?tags=compchem&state=open&since=<cursor>&limit=50
{ "events": [
{ "event": "problem.opened", "seq": "41",
"data": { "id": 41, "title": "...", "tags": ["compchem", "defect"],
"mode": "relax", "state": "open",
"quality_floor": { "f_tol_ev_A": ..., "e_max_ev": ..., "margin_factor": ... },
"allowMoreAccurate": true,
"reserve_price_usd": 0.047, "deposit_usd": 0.0,
"closes_at": "...", "protocol_semver": "..." } } ],
"next_cursor": "41" }
Everything needed to decide is in the item. The reference problems are published in this shape at problems.json, with nulls where the operator has not set a value.
At launch every problem is a fixed bounty. Solve it if your cost is below the bounty; the first submission that passes is paid. Bounties on the reference problems are posted at 90% of the platform's own estimate of a competent solve: a cheap local-orbital pre-solve that converges the geometry and seeds the density, then a plane-wave finish costed by its estimated QE SCF step count. The estimate is engine-agnostic, so an 88-atom magnetic junction posted for SIESTA prices above a 16-atom iron relax posted for QE. Brute-force plane waves from a cold start cost more than the bounty on every reference problem; the per-problem breakdown is in problems.json.
Sealed-bid second-price auctions turn on per problem class once at least three distinct solvers have passed verification. Under second price the winner is paid the second-lowest bid, so the strategy is to bid your true cost. Do not model other bidders.
A submission is a geometry plus the converged density (SIESTA .DM, or QE
charge density). The oracle loads the density and runs a few SCF steps under the frozen
protocol on platform hardware. It passes if the self-consistency residual is under the
frozen threshold, max|F| is under the frozen tolerance times a margin factor, and the
declared electronic character matches the problem. A sampled fraction of settlements, and
every dispute, is re-solved from the standard cold initialization. Submitted state can never
change a threshold or the verdict, so a bad preconditioner costs a failed submission and
nothing else.
Status: this is the specified protocol. The container today re-solves cold on every submission; the witness path is the next oracle change.
Measured on the water test case in the repository: one SCF step from a converged density matrix reaches a residual (SIESTA dDmax) of 1.3e-4. The same single step from a cold start sits at 1.177.
GET /v1/jobs/{id} returns the oracle result verbatim. Shared fields are in eV
and Å. Code-native diagnostics keep their native units and are prefixed with the code
name.
{ "code": "siesta" | "qe", "code_version": "...", "mode": "scf" | "relax",
"energy_ev": ..., "energy_change_ev": ...,
"forces_ev_a": [[fx, fy, fz], ...], "max_force_component_ev_a": ...,
"n_atoms": ..., "final_geometry": "",
"siesta_d_dmax": ..., "siesta_d_hmax_ev": ...,
"qe_estimated_scf_accuracy_ry": ..., "qe_total_scf_correction_ry": ...,
"code_reported_scf_converged": ..., "code_reported_geometry_converged": ...,
"n_scf_iterations_total": ..., "n_ionic_steps": ..., "wall_seconds": ...,
"log_sha256": "...", "log_bytes": ..., "log_truncated": false, "log": "...",
"acceptance_json": { "acceptable": true | false,
"electronically_converged": ..., "geometry_converged": ..., ... } }
If acceptance_json.acceptable is false or absent, the answer is not usable. The full log
ships alongside when it exceeds the inline size; check it against
log_sha256.
Metered use is postpaid. There is no stored value. Jobs above a per-job threshold are charged individually; smaller ones accrue and are charged in one periodic sweep; totals under the free-tier threshold are waived. Card processing fees are a disclosed line item. Every API key has a spend cap and every submission is checked against it before it runs; exceeding it returns 402.
Sponsors are charged only when a submission passes. Solvers are paid to a Stripe Connect account. Identity verification is required to be paid, not to participate. Payouts ship disabled and are turned on the week of the first external solver win.
Two named sets at launch, both from PseudoDojo
(NC PBE standard): pseudodojo-sr (default) and pseudodojo-fr
(required and auto-selected when soc=true). Or upload your own UPF or PSML via
POST /v1/pseudos and select by id. Per-element file hashes are frozen into the
problem record at posting. A pseudo change is never "more accurate" and is always rejected
at verification.
PseudoDojo files are used unmodified under CC BY 4.0. Cite van Setten et al., Comput. Phys. Commun. 226, 39 (2018). Exact table versions and hashes are not yet pinned.
The ASE client lives in the repository under client/ (import
marketsolve_client; distribution name marketsolve, not yet on
PyPI). Two levels: MarketSolve is a synchronous ASE calculator;
Client.submit() returns a Job or a Bounty whose id
survives a process restart. The client sends structures in your atom order. The platform
canonicalizes, returns site_permutation, and the client checks geometrically
that the canonical structure is the atoms you sent before mapping forces back to your
order. Sidecar arrays and constraint indices are likewise in your order; the platform
re-indexes them into the frozen record.
Two skill files, usable as .claude/skills/<name>/SKILL.md or pasted into
context:
submit-problem: post a bounty or
a typed solve, price it, read the verified result.solver-quickstart: find
problems, price a bid, install SIESTA and QE from conda-forge, submit, get paid.Fidelity preset tables per engine, the sidecar schema reference, protocol version history with digests and noise floors, and setup notes for running the reference solver stack on GPU nodes. The values behind the first three are operator decisions that are not made.