Developer documentation

The API base is https://api.trueodd.xyz. Interactive schema: https://api.trueodd.xyz/docs · OpenAPI JSON.

Discovery

Fetch GET https://api.trueodd.xyz/v1/service-metadata for the service catalog, identifiers, endpoints, and payment configuration. GET /v1/supported-markets lists venues, families, and series.

ServiceMethod & pathBilling
rwoo.check_marketPOST /v1/check-marketPaid
rwoo.cross_venue_edgePOST /v1/cross-venue-edgePaid
rwoo.get_calibrationGET /v1/calibrationFree

Payments — OKX Agent Payments (x402)

Paid endpoints use the HTTP 402 flow. An unpaid request receives a 402 whose body is the x402 challenge (x402Version + accepts[]) with WWW-Authenticate: Payment. Re-send the identical request body with an X-PAYMENT header; on success you receive 200, a PAYMENT-RESPONSE header, and a receipt whose payment_reference links the settlement.

Prices, asset, network, and recipient are set by the operator and appear in /v1/service-metadata. Until configured, paid endpoints run in free mode or fail closed.

Call it

curl -sS -X POST https://api.trueodd.xyz/v1/check-market \
  -H 'Content-Type: application/json' \
  -d '{"market": {"venue": "kalshi", "market_id": "KXHIGHNY-26JUL12-B85"}}'
# A paid deployment answers 402 with an x402 challenge first;
# re-send the identical body plus an X-PAYMENT header to receive 200.
import httpx

BASE = "https://api.trueodd.xyz"
req = {"market": {"venue": "kalshi", "market_id": "KXHIGHNY-26JUL12-B85"}}
r = httpx.post(f"{BASE}/v1/check-market", json=req, timeout=30)
if r.status_code == 402:
    challenge = r.json()          # x402: pay with your agent wallet,
    # then re-POST the same body with the X-PAYMENT header.
print(r.json())
const BASE = "https://api.trueodd.xyz";
const req = { market: { venue: "kalshi", market_id: "KXHIGHNY-26JUL12-B85" } };
const r = await fetch(`${BASE}/v1/check-market`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(req),
});
// On 402, read the x402 challenge and retry with an X-PAYMENT header.
console.log(await r.json());
{"market": {"venue": "kalshi", "market_id": "KXHIGHNY-26JUL12-B85"}}

Refusals vs errors

A market the oracle cannot safely price is a refusal: HTTP 200, status: "refused", a stable reason_code, an explanation, the missing capability, and a receipt. Malformed requests, unknown services, and payment failures are transport errors (4xx) with a stable JSON shape: { "error": { "code", "message" }, "request_id" }.

CodeHTTPMeaning
INVALID_REQUEST400Body failed schema validation or exceeded limits.
MARKET_NOT_FOUND404The venue has no such market id.
UNSUPPORTED_VENUE400Venue is not one of kalshi, polymarket, limitless.
UNSUPPORTED_MARKET422No wired engine covers this market shape (refusal).
ENTITY_UNBOUND422The entity/location/strike could not be bound (refusal).
YES_SIDE_UNBOUND422Which side YES prices could not be determined (refusal).
SOURCE_UNAVAILABLE503A required upstream source could not be reached.
SOURCE_STALE422Source data is older than the freshness limit (refusal).
SOURCE_CONFLICT422Sources disagree beyond tolerance (refusal).
MODEL_MISSING422The engine declined to emit a probability (refusal).
FEE_UNKNOWN422The venue fee term is not quantified (refusal).
RATE_LIMITED429Too many requests, or an upstream rate limit.
PAYMENT_REQUIRED402A paid endpoint was called without payment (x402 challenge).
PAYMENT_INVALID402The presented payment failed verification.
PAYMENT_REPLAYED402The payment nonce was already used.
UPSTREAM_TIMEOUT504An upstream source timed out.
INTERNAL_ERROR500An unexpected error (no stack trace is exposed).

Semantics you must not confuse

Idempotency

Send an Idempotency-Key header to make a retry return the same result and receipt without re-charging.

Rate limits & timeouts

Requests are bounded and time-limited; a slow upstream returns UPSTREAM_TIMEOUT rather than hanging.

Confidence is not calibration

Model agreement (confidence) is the spread of the ensemble now. Calibration is the measured, precommitted historical hit rate. A high confidence with zero resolved events is not evidence — the API always reports the independent sample count.

Missing data stays missing

An unknown entity, unbound strike, or unverified fee never becomes a silent zero — it becomes a refusal.

Versions & changelog

Every forecast carries its model_version. Current engine versions:

FamilyModel version
weather.temperatureweather-ensemble-v2
weather.precipitationweather-hurdle-v2
economics.core_cpicore-cpi-official-ensemble-v2
economics.headline_cpiheadline-cpi-official-ensemble-v2
economics.gdpgdp-official-ensemble-v2
economics.laborlabor-official-history-v1
economics.fed_ratesfed-hold-only-v1
economics.recessionspf-recession-v1
sports.world_cupworld-cup-live-bracket-elo-v2
sports.tennistennis-uts-elo-v1
sports.nbanba-point-differential-v1
sports.mlbmlb-season-elo-v1
sports.club_soccerclubelo-match-v1