Docs

Endpoints

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.

RouteWhat it doesStatus
GET /problemsPoll open problems. Params tags (comma list, all must match), state, since (cursor), limit. Rate-limited per key.implemented
GET /protocols/currentCurrent protocol: semver, container digest, noise floor, allowed species. Public.implemented
POST /auctionsPost 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}/submissionsSubmit a structure (multipart POSCAR). Returns bid and job ids.implemented
POST /solveTyped 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 /pseudosUpload 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/onboardSponsor 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}/revealSealed-bid auctions.returns 501

Discovery

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.

Pricing

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.

Verification

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.

Result schema

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.

Billing

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.

Pseudopotentials

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.

Client package

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.

Skills for agents

Two skill files, usable as .claude/skills/<name>/SKILL.md or pasted into context:

Not written yet

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.