Convexly Public API

v1

Use the API when you need Convexly evidence inside another workflow: Market Trust labels, wallet-quality diagnostics, track-record status, and caveats that can travel with an internal memo or product surface.

Current

Edge Score API

Jump to reference. Compute the Edge Score composite from features or end-to-end from a Polymarket wallet address. Auth: Supabase JWT.

https://www.convexly.app/api/engine/edge-score

Current

Market Trust API

Jump to reference. Public v0.1 market-quality cards with evidence counts, caveats, row hashes, links to full card payloads, plus a candidate preview endpoint for canary cards.

/api/market-trust · /api/market-trust/v0.2

Current

Track Record Summary

Jump to reference. Public aggregate status for wallet ratings, Coherence Signals, Market Trust coverage, and caveat language.

https://www.convexly.app/api/track-record/summary

Current

Wallet Verdict API

Jump to reference. Four-state skill verdict (skilled / luck / insufficient / flagged) for one wallet or a tier-capped batch. Auth: X-API-Key.

https://www.convexly.app/api/engine/public/wallets

The public engine hostname is the same-origin proxy at https://www.convexly.app/api/engine. The old dedicated API subdomain is not published; do not use it in new integrations.

Authentication

Current · Edge Score API

Uses your Supabase access token (JWT) via the Authorization: Bearer <token> header. The Edge Score routes do NOT accept X-API-Key. Sign in at convexly.app/login to obtain a session token (or use the Supabase client library with your project credentials). Plan-gated to Researcher and above.

curl -H "Authorization: Bearer <supabase_jwt>" \
  https://www.convexly.app/api/engine/edge-score/from-wallet \
  -H "Content-Type: application/json" \
  -d '{"wallet_address": "0x..."}'

Current · Wallet Verdict API

Uses the X-API-Key header: single and batch four-state skill verdicts for a Polymarket wallet. See the Wallet Verdict reference for full paths, batch caps, and daily quotas. Same plan requirement as Edge Score above (Researcher tier or higher).

curl -H "X-API-Key: cvx_your_api_key_here" \
  "https://www.convexly.app/api/engine/public/wallets/0x56687bf447db6ffa42ffe2204a05edaa20f55839/verdict"

Keep your credentials secret. Both Supabase JWTs and X-API-Keys grant access to data scoped to your account. If a credential is compromised, rotate it immediately (sign out + back in for JWT; revoke + regenerate for X-API-Key).

Check your quota usage

GET /public/usage reports the calling key's wallet-read consumption for the current UTC day: plan, wallets used, cap, and remaining. It costs no quota, and the number it reports is read from the same counter that enforces the cap, so the two can never disagree.

curl -H "X-API-Key: cvx_your_api_key_here" \
  "https://www.convexly.app/api/engine/public/usage"

# {"plan":"researcher","usage_date":"2026-08-14",
#  "wallets_used":3,"wallets_cap":200,"wallets_remaining":197}

Edge Score

The Edge Score API exposes the Edge Score composite (posture + conviction + discipline) to external callers. The full methodology is published at Edge Score Methodology V1. Two endpoints: compute from precomputed features, or compute end-to-end from a Polymarket wallet address.

Base URL: https://www.convexly.app/api/engine/edge-score

Authentication: Supabase access token (JWT) via Authorization: Bearer <token>. The Edge Score API does not accept X-API-Key. Sign in via the Convexly frontend to obtain a session token, or use the Supabase client library with your project credentials.

Versioning: every response carries version: "v3b". A future composite (V4, etc.) will be served under a new version string with deprecation notice to all active callers.

POST/edge-score/from-features

Compute Edge Score from precomputed features. Rate limit: 60 requests / minute / IP.

Request Body

{
  "skill_brier": 0.05,      // null triggers median imputation
  "concentration": 0.6,     // 0-5, null triggers median imputation
  "n_positions": 40         // required, >= 0
}

Response (200)

{
  "edge_score": 72.4,                     // 0-100 percentile
  "label": "Strong",
  "tagline": "Above average on the composite...",
  "risk_flag": false,
  "breakdown": {
    "edge_score": 72.4,
    "posture_percentile": 65.1,
    "conviction_percentile": 78.3,
    "discipline_percentile": 71.0,
    "posture_contribution": 0.47,
    "conviction_contribution": 2.12,
    "discipline_contribution": -0.28,
    "raw_score": 2.31,
    "n_positions_used": 40,
    "used_median_skill": false,
    "used_median_concentration": false,
    "calibration_percentile": 65.1,       // deprecated alias
    "calibration_contribution": 0.47      // deprecated alias
  },
  "version": "v4"
}
POST/edge-score/from-wallet

End-to-end: pages the public Polymarket Data API for up to 3,500 recent trades (500-row pages, hard offset cap at 3,100), with a positions fallback for resolved positions beyond that cap, resolves outcomes, computes features, runs the composite. Cached for 24 hours per wallet, and the response reports its own cache age. Rate limit: 10 requests / minute / IP.

Request Body

{
  "wallet_address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839"
}

Response (200)

{
  "edge_score": 91.2,
  "label": "Elite",
  "tagline": "Top decile by composite score...",
  "risk_flag": true,
  "breakdown": { ... },                   // same shape as /from-features
  "version": "v4",
  "wallet_address": "0x0000000000000000000000000000000000000000",
  "n_trades": 1843,                       // venue-window trade count
  "score_cache": {
    "cached": true,
    "cached_at": "2026-08-10T04:11:00+00:00",
    "age_seconds": 39240,
    "next_refresh_at": "2026-08-11T04:11:00+00:00",
    "ttl_hours": 24
  },
  "record_proof": {                       // chain record; null-filled if none
    "emission_completeness": "provably_full",
    "is_provably_full": true,
    "completeness_proof": {
      "basis": "chain",
      "reconciled_at_block": 91130322,
      "reconciled_as_of": "2026-07-01T00:00:00+00:00",
      "chain_stale": false,
      "is_provably_full": true,
      "verification": "Ask Polygon whether this address moved an outcome token after reconciled_at_block..."
    },
    "certified_at_block": 91130322,
    "realized_edge": 0.0231,
    "realized_edge_ci_bca": [0.0159, 0.0345],
    "realized_edge_n": 127,               // priced sample
    "n_resolved_complete": 214,           // full resolved set
    "method_version": "1c6e3ddc...",
    "unavailable_reason": null
  }
}

record_proof is the block-anchored completeness proof for the wallet, plus the realized edge priced off the reconstructed on-chain record. It is a different quantity from edge_score, which is a composite over a window of recent venue trades; neither bounds the other, so do not compare or combine them. When no current complete read exists for the wallet, every key in the block is null and unavailable_reason says which: no current complete read, or the lookup itself failed. The keys are never omitted. That is a statement about our files, not about the address.

Errors

401 Missing or invalid JWT

422 Invalid wallet address format

429 Rate limit exceeded; retry after the Retry-After header

502 Polymarket upstream temporarily unreachable

Usage metering: every successful and rate-limited call writes one row to our internal usage log (user ID, route, plan tier, status code, duration, wallet address if applicable). Append-only, user-read-own, service-role write. No metered billing is applied today. Pricing tiers may layer on top in a future release with advance notice.

Backwards compatibility: the calibration_percentile and calibration_contribution fields in the breakdown are deprecated aliases for posture_percentile and posture_contribution (the pillar rename landed 2026-04-18 and is explained in this post). Aliases will be removed in a later major version with at least 60 days of deprecation notice.

Market Trust

The public Market Trust API exposes the same v0.1 daily card artifact used by /market-trust. It is intentionally caveated: pending pillars are listed as pending and unscored, not silently treated as clean evidence. Response-shape version is separate from methodology version:api_versiontracks the envelope; methodology_versiontracks the rating model.

GET/api/market-trust

List public Market Trust card summaries. Optional filters: rating, q, and limit.

curl "https://www.convexly.app/api/market-trust?rating=discount&limit=5"
{
  "schema_version": "market_trust_public_index_v1",
  "api_version": "v1.1",
  "methodology_version": "mtc_v0_1",
  "tier": "public_24h",
  "source_snapshot_date": "20260506",
  "source_snapshot_at_utc": "2026-05-06T00:00:00.000Z",
  "source_snapshot_lag_seconds": 3600,
  "count": 5,
  "returned_count": 5,
  "cards": [
    {
      "condition_id": "0x...",
      "venue": "polymarket",
      "rating": "discount",
      "composite_score": 50,
      "measured_only_composite": {
        "score": 63,
        "n_measured": 3,
        "n_pillars": 7,
        "measured_weight_share": 0.51
      },
      "evidence": { "measured": 3, "heuristic": 3, "pending": 1 },
      "pending_pillars": ["participant_quality"],
      "market_quality_interval": [42, 66],
      "evidence_confidence_score": 48,
      "evidence_confidence_label": "low",
      "verdict_driver": "rating_capped_by_evidence_depth",
      "hard_caps_applied": ["rating_capped_by_evidence_depth"],
      "robustness": {
        "leave_one_pillar_out_range": [44, 57],
        "weight_perturbation_range": [48, 53],
        "verdict_stable_under_sensitivity": true
      },
      "score_waterfall": [{ "key": "coherence", "contribution": 12.2 }],
      "row_hash": "57907a..."
    }
  ]
}

What the math envelope adds

The stable endpoint also surfaces the v0.2 math envelope as non-breaking additions: market_quality_interval, evidence_confidence_score, evidence_confidence_label, verdict_driver, hard_caps_applied, and robustness. These fields expose uncertainty, evidence confidence, rating caps, and sensitivity diagnostics; they do not promote Market Trust to v1. Calibration against the outcome ledger is still the v1 promotion gate. See the methodology contract.

Responses also include X-Convexly-Snapshot-At and X-Convexly-Snapshot-Lag-Seconds headers for freshness monitoring without parsing JSON.

Public snapshot endpoints are CDN-cached and do not emit paid-tier rate-limit headers yet. Authenticated enterprise API keys should add enforced X-RateLimit-* headers when that quota layer ships. Vercel caching uses CDN-Cache-Control and Vercel-CDN-Cache-Control for the 5-minute public snapshot TTL.

GET/api/market-trust/{condition_id}

Return the full public card payload for one condition ID. Polymarket is the only v0.1 venue exposed today.

curl "https://www.convexly.app/api/market-trust/0x...?venue=polymarket"
{
  "schema_version": "market_trust_public_card_v1",
  "api_version": "v1.1",
  "methodology_version": "mtc_v0_1",
  "audit_artifact_id": "57907a...",
  "source_snapshot_at_utc": "2026-05-06T00:00:00.000Z",
  "source_snapshot_lag_seconds": 3600,
  "contract_status": "stable_legacy_snapshot",
  "market_quality_interval": [42, 66],
  "evidence_confidence_score": 48,
  "evidence_confidence_label": "low",
  "verdict_driver": "rating_capped_by_evidence_depth",
  "hard_caps_applied": ["rating_capped_by_evidence_depth"],
  "robustness": {
    "leave_one_pillar_out_range": [44, 57],
    "weight_perturbation_range": [48, 53],
    "verdict_stable_under_sensitivity": true
  },
  "measured_only_composite": { "score": 63, "n_measured": 3 },
  "score_waterfall": [],
  "card": { "...": "full public card" }
}
GET/api/market-trust/v0.2Preview

List the current preview Market Trust cards. These are experimental, not the stable production endpoint; label them as preview wherever you display them.

curl "https://www.convexly.app/api/market-trust/v0.2?venue=polymarket&limit=5"
{
  "schema_version": "market_trust_v0_2_preview_index_v1",
  "api_version": "v1.2-preview",
  "methodology_version": "mtc_v0_2_candidate",
  "contract_status": "canary_preview",
  "stable_contract_url": "/api/market-trust",
  "promotion_gate": "freshness_coverage_outcome_history",
  "promotion_blockers": [
    "v1_candidate_requires_resolved_outcome_ledger_row"
  ],
  "validation_state": {
    "schema_version": "market_trust_validation_state_v1",
    "canary_pipeline": {
      "status": "ready",
      "label": "Current card is operationally reviewable.",
      "current_card_issues": []
    },
    "public_v1_promotion": {
      "status": "pending",
      "contract_version": "market_trust_v1_candidate_contract_draft_2026_05_19",
      "label": "1 public v1 validation gate still open.",
      "gates_remaining_count": 1,
      "gates_remaining": [
        {
          "code": "v1_candidate_requires_resolved_outcome_ledger_row",
          "label": "At least one resolved-outcome row is still needed in the validation ledger."
        }
      ],
      "explainer_url": "/research/market-trust-v1-promotion-contract"
    }
  },
  "tier": "experimental_v0_2_preview",
  "source_snapshot_at_utc": "2026-05-08T10:20:12.000Z",
  "source_snapshot_lag_seconds": 480,
  "caveats": [
    "Candidate diagnostic scorecard, not investment advice or certification.",
    "Participant-quality is measured only when identity, data-rights, and coverage gates pass."
  ],
  "cards": [
    {
      "condition_id": "0x...",
      "venue": "polymarket",
      "rating": "use_with_caveats",
      "score": 68,
      "market_quality_score": 68,
      "market_quality_interval": [55, 76],
      "evidence_confidence_score": 62,
      "evidence_confidence_label": "medium",
      "verdict_driver": "rating_capped_by_quality_interval_low",
      "hard_caps_applied": [
        "cap_use_until_outcome_history_matures"
      ],
      "promotion_blockers": [
        "v1_candidate_requires_resolved_outcome_ledger_row"
      ],
      "validation_state": {
        "canary_pipeline": {
          "status": "ready",
          "current_card_issues": []
        },
        "public_v1_promotion": {
          "status": "pending",
          "contract_version": "market_trust_v1_candidate_contract_draft_2026_05_19",
          "gates_remaining": [
            {
              "code": "v1_candidate_requires_resolved_outcome_ledger_row",
              "label": "At least one resolved-outcome row is still needed in the validation ledger."
            }
          ]
        }
      },
      "robustness": {
        "verdict_stable_under_sensitivity": false,
        "leave_one_pillar_out_range": [51, 72],
        "weight_perturbation_range": [64, 70]
      },
      "evidence_counts": {
        "measured": 4,
        "heuristic": 2,
        "pending": 1,
        "blocked_data_rights": 0
      },
      "source_snapshot_lag_seconds": 480,
      "audit_artifact_id": "d3b0..."
    }
  ]
}

The detail endpoint is /api/market-trust/v0.2/{condition_id}?venue=polymarket. It returns the full candidate card payload, includingaudit_artifact_id, market-quality interval, evidence confidence, robustness diagnostics, raw promotion-blocker codes, structured validation state, evidence caveats, allowed wording, not-claiming language, and source freshness. Treat this as a preview endpoint. Its fields and scores may change until enough markets resolve to validate them, so do not present its output as final.

Track Record

The public track-record summary is a compact status endpoint for Convexly's evidence layer. It reports artifact coverage and caveats; it does not claim mature realized performance before resolved outcomes and matched controls support that wording.

GET/api/track-record/summary

Aggregate public status for wallet ratings, Coherence Signals, Market Trust, and evidence caveats.

publicLeaderboardSize counts the rows currently published on the Convexly wallet board. That board is a closed set of wallet addresses selected once on 2026-04-19 and never added to, screened at selection on realized PnL, and rescored daily. It is not a ranking of Polymarket wallets, and the field name is retained only for backward compatibility.

curl "https://www.convexly.app/api/track-record/summary"
{
  "schema_version": "platform_track_record_summary_v1",
  "methodology_versions": {
    "wallet_ratings": "edge_score_v3b_public_snapshot",
    "coherence_signals": "cme_v0_1",
    "market_trust": "mtc_v0_1_public_snapshot"
  },
  "summary": {
    "walletRatings": { "publicLeaderboardSize": 50 },
    "coherenceSignals": { "signalsEmittedToday": 96 },
    "marketTrust": { "publicCards": 5 }
  }
}

Wallet Verdict

The Wallet Verdict API returns the four-state skill-badge machine (skilled / luck / insufficient / flagged) for a Polymarket wallet, with the realized edge always paired to its BCa bootstrap 95% interval in the same object. This is a different surface from the Edge Score composite above: same kind of input (a wallet address), different math, different response shape. Authentication is X-API-Key, not JWT.

Base URL: https://www.convexly.app/api/engine/public/wallets

Authentication: X-API-Key: cvx_your_api_key_here header on every request below except the badge redirect, which is keyless. Generate keys from Settings → API Keys (Researcher tier or higher: Researcher, Trader, or Enterprise; legacy Pro is also permitted).

GET/public/wallets/{address}/verdict

Single-wallet verdict. Served from the on-chain-reconciled record only: nothing is computed on request, and no Polymarket window is read on this path. A wallet carries an estimate only while a full-history read has been reconciled for it and nothing has moved on chain since the block that read was reconciled at. Otherwise every estimate field comes back null (second response below). Rate limit: 30 requests / minute / IP.

curl -H "X-API-Key: cvx_your_api_key_here" \
  "https://www.convexly.app/api/engine/public/wallets/0x56687bf447db6ffa42ffe2204a05edaa20f55839/verdict"

Response (200)

{
  "address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839",
  "state": "skilled",
  "styles": ["directional"],
  "state_definition": "n_resolved >= 30, at least 20 independent resolved events, and the BCa bootstrap 95% lower bound on realized edge is above zero, all on the resolved record. Retrospective and in-sample, uncorrected for multiple comparisons. See styles for how the edge is earned and its payoff shape.",
  "realized_edge": 0.11,
  "realized_edge_ci_bca": [0.047, 0.267],
  "n_resolved": 45,
  "edge_disclosure": "+11.0pp (95% CI [+4.7pp, +26.7pp], n=45 of 58 resolved...",
  "n_resolved_complete": 58,
  "n_priced": 45,
  "n_unpriced": 13,
  "concentration": 0.2,
  "owner_verified": {
    "claimed": false,
    "verified": false,
    "verified_at": null,
    "verification_expires_at": null
  },
  "as_of": "2026-07-01T00:00:00.000Z",
  "reconciled_at_block": 91130322,
  "completeness_proof": {
    "basis": "chain",
    "reconciled_at_block": 91130322,
    "reconciled_as_of": "2026-07-01T00:00:00.000Z",
    "chain_stale": false,
    "is_provably_full": true,
    "verification": "Ask Polygon whether this address moved an outcome token after reconciled_at_block. A negative answer is the completeness claim; no venue API response can carry an equivalent check."
  },
  "method_hash": "b41f8c...a97d3e",
  "methodology_version": "badge-v1",
  "disclaimer": "Diagnostics on a public on-chain record, not investment advice, not a forecast."
}

Response (200), no current reconciled read

{
  "address": "0x1111111111111111111111111111111111111111",
  "state": "insufficient",
  "styles": ["directional"],
  "state_definition": "No skill state is returned because this wallet has no current on-chain-reconciled full-history read. A read is current only while the wallet has not moved an outcome token since the block it was reconciled at; without one, the only record available omits redeemed positions and is not the complete resolved record. No realized edge is returned in its place, and n_resolved is null rather than zero: the resolved record was never counted here, and a count that was never taken is not a count that came to zero.",
  "realized_edge": null,
  "realized_edge_ci_bca": null,
  "n_resolved": null,
  "edge_disclosure": null,
  "n_resolved_complete": null,
  "n_priced": null,
  "n_unpriced": null,
  "concentration": null,
  "owner_verified": {
    "claimed": false,
    "verified": false,
    "verified_at": null,
    "verification_expires_at": null
  },
  "as_of": null,
  "reconciled_at_block": null,
  "completeness_proof": null,
  "method_hash": null,
  "methodology_version": "badge-v1",
  "disclaimer": "Diagnostics on a public on-chain record, not investment advice, not a forecast."
}

Branch on n_resolved, not on state. n_resolved is a positive integer exactly when a read served, and null when none did. It is never 0. state does not answer the same question: insufficient is also returned WITH a realized_edge and an interval, for a wallet whose resolved record was read in full but whose sample size, event count, or share of positions carrying a recoverable entry price left that record unreadable either way. On that response n_resolved counts the priced subset the interval was computed on, which can be a small share of the resolved record, and state_definition says which cause applied. Read the edge on an insufficient response as a diagnostic, not as a skill reading.

Both denominators travel in the payload. n_resolved_complete is the size of the full resolved record, n_priced (equal to n_resolved) is the subset the edge was computed on, n_unpriced is the remainder no entry price was recovered for, and edge_disclosure carries the edge, its interval, and both counts in one string. These are the same fields, from the same stored read, that the reconciled-read endpoint below returns.

Check currency yourself with reconciled_at_block. reconciled_at_block is the Polygon block this read's completeness was checked against: at that block the reconstructed record matched the wallet's on-chain settlement history in full. Ask the chain whether the address moved an outcome token after this block; if it did, the read no longer covers the whole record. The field does not say the read is current now, only where an independent currency check must start. It is null on the no-read response, never 0.

The proof rides in the payload. completeness_proof gathers the chain anchors this response already carries into one block, plus the verification instruction you can run against Polygon yourself. Its presence is packaging, not a verdict: read the is_provably_full and chain_stale values inside it, not the block's existence. method_hash is the hash of the frozen methodology the read was computed and gated under; pin it and compare across reads to detect a method change. Both are null on the no-read response.

Errors

400 Address is not a 0x-prefixed 42-character hex string

401 Missing, invalid, or revoked API key

403 Key's plan does not permit public API access

429 Daily wallet quota reached (see table below) or the per-minute rate limit was exceeded

502 Analysis failed; Polymarket may be temporarily unreachable

POST/public/wallets/verdict/batch

Verdicts for up to your plan's per-call cap of DISTINCT addresses (see table below; 100 is the platform ceiling regardless of plan). Invalid addresses return a per-item error instead of failing the whole batch. Rate limit: 10 requests / minute / IP.

Request Body

{
  "addresses": [
    "0x56687bf447db6ffa42ffe2204a05edaa20f55839",
    "0x1111111111111111111111111111111111111111"
  ]
}
curl -X POST "https://www.convexly.app/api/engine/public/wallets/verdict/batch" \
  -H "X-API-Key: cvx_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"addresses": ["0x56687bf447db6ffa42ffe2204a05edaa20f55839"]}'

Response (200)

{
  "requested": 2,
  "returned": 2,
  "plan": "researcher",
  "max_for_plan": 10,
  "results": [
    {
      "address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839",
      "ok": true,
      "verdict": { "...": "same shape as the single verdict response above" }
    },
    {
      "address": "0x1111111111111111111111111111111111111111",
      "ok": true,
      "verdict": { "...": "same shape as the single verdict response above" }
    }
  ],
  "disclaimer": "Diagnostics on a public on-chain record, not investment advice, not a forecast."
}

403 when the number of distinct valid addresses exceeds your plan's per-call cap (no compute runs that call); otherwise the same error set as the single-wallet endpoint above applies.

GET/public/wallets/{address}/badgeNo key required

302 redirect to the embeddable badge SVG. Kept as a stable URL family for embedders. Rate limit: 60 requests / minute / IP.

curl -i "https://www.convexly.app/api/engine/public/wallets/0x56687bf447db6ffa42ffe2204a05edaa20f55839/badge"
# HTTP/1.1 302 Found
# Location: https://www.convexly.app/api/badge/wallet/0x56687bf447db6ffa42ffe2204a05edaa20f55839

For an embed, point an img tag at the destination URL directly rather than following the redirect on every page load: https://www.convexly.app/api/badge/wallet/{address}. Pass ?style=light for the light theme.

Per-plan batch cap and daily wallet quota

The batch cap bounds DISTINCT wallets in a single call; the daily quota bounds total wallets served per key, per UTC day, across both the single and batch endpoints above. The quota resets at 00:00 UTC.

Free

Not included

Researcher / Pro

10 / batch call
200 / UTC day

Trader

25 / batch call
1,000 / UTC day

Enterprise

100 / batch call
25,000 / UTC day

100 distinct wallets per batch call is the platform ceiling regardless of plan. Some Enterprise accounts run on a custom-quoted cap or quota instead of the default shown here; contact us if yours should differ.

Wallet Set

Your plan does not buy a number of reads; it buys a bounded set of distinct wallets that Convexly keeps current on a per-tier cadence. These three endpoints make that set fully self-service over the API (previously available only from the dashboard): add a wallet, list your set with each member's lifecycle and staleness, and remove one. Once a wallet is in your set, read its on-chain-reconciled full-history read with the sibling GET /wallets/{address}/reconciled-read endpoint documented below. Authentication is X-API-Key, same as Wallet Verdict above.

Base URL: https://www.convexly.app/api/engine/public/wallets/reconciled-set

Authentication: X-API-Key: cvx_your_api_key_here on every request below. Generate keys from Settings → API Keys (Researcher tier or higher).

GET/public/wallets/reconciled-set

List every wallet in your set. Always returns 200, even at a cap of zero (limit: 0, can_add: false) -- viewing your own set is never refused. Rate limit: 30 requests / minute / IP.

curl -H "X-API-Key: cvx_your_api_key_here" \
  "https://www.convexly.app/api/engine/public/wallets/reconciled-set"

Response (200)

{
  "plan": "researcher",
  "limit": 10,
  "count": 1,
  "can_add": true,
  "refresh_days": 30,
  "members": [
    {
      "wallet_address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839",
      "read_state": "ready",
      "added_at": "2026-07-20T00:00:00+00:00",
      "last_read_at": "2026-07-27T00:00:00+00:00",
      "next_refresh_due_at": "2026-08-26T00:00:00+00:00",
      "refresh_days": 30,
      "read_staleness": "fresh",
      "read_age_days": 2.0
    }
  ]
}

read_state is one of queued, running, ready, or failed, set by the refresh worker on its own schedule. read_staleness is fresh, due, or overdue relative to your plan's cadence; a wallet that has never been read is always overdue, never fresh.

POST/public/wallets/reconciled-set

Add one wallet to your set. Serialized against your plan cap on the database side, so concurrent adds (multiple keys, multiple processes, or a mix of the API and the dashboard) cannot land more wallets than your plan allows. Rate limit: 10 requests / minute / IP.

Request Body

{
  "wallet_address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839"
}
curl -X POST "https://www.convexly.app/api/engine/public/wallets/reconciled-set" \
  -H "X-API-Key: cvx_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"wallet_address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839"}'

Response (201)

{
  "member": {
    "wallet_address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839",
    "read_state": "queued",
    "added_at": "2026-07-29T00:00:00+00:00",
    "last_read_at": null,
    "next_refresh_due_at": null,
    "refresh_days": 30,
    "read_staleness": "overdue",
    "read_age_days": null
  },
  "limit": 10,
  "count": 1
}

Errors

400 Address is not a 0x-prefixed 42-character hex string

403 Your set is already at its cap. The body names the limit and your current count: {"code": "wallet_set_cap_reached", "limit": 10, "count": 10, "plan": "researcher"}

409 This wallet is already in your set

502 The add could not be completed; retry

DELETE/public/wallets/reconciled-set/{address}

Remove one wallet from your set, freeing a slot under your plan's cap. Rate limit: 30 requests / minute / IP.

curl -X DELETE "https://www.convexly.app/api/engine/public/wallets/reconciled-set/0x56687bf447db6ffa42ffe2204a05edaa20f55839" \
  -H "X-API-Key: cvx_your_api_key_here"

Response (200)

{
  "success": true,
  "wallet_address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839"
}

404 when the address is not in your set (or belongs to another account); 400 for a malformed address, before any delete is attempted.

GET/public/wallets/{address}/reconciled-read

Read the on-chain-reconciled full-history read for one wallet in your set. 404s for a wallet you have not added (costs no daily quota). Rate limit: 30 requests / minute / IP.

curl -H "X-API-Key: cvx_your_api_key_here" \
  "https://www.convexly.app/api/engine/public/wallets/0x56687bf447db6ffa42ffe2204a05edaa20f55839/reconciled-read"

Response (200, read landed)

{
  "address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839",
  "available": true,
  "member": {
    "read_state": "ready",
    "added_at": "2026-07-20T00:00:00+00:00",
    "last_read_at": "2026-07-27T00:00:00+00:00",
    "next_refresh_due_at": "2026-08-26T00:00:00+00:00",
    "refresh_days": 30
  },
  "state": "skilled",
  "state_definition": "n_resolved >= 30, ...",
  "styles": ["directional"],
  "realized_edge": 0.0174,
  "realized_edge_ci_bca": [0.0102, 0.0244],
  "edge_disclosure": "realized edge 0.0174, 95% CI [0.0102, 0.0244], n=4099 of 4948 resolved...",
  "n_resolved_complete": 4948,
  "n_priced": 4099,
  "n_unpriced": 849,
  "reconciled_as_of": "2026-07-28T13:00:00+00:00",
  "reconciled_at_block": 91134712,
  "completeness_proof": {
    "basis": "chain",
    "reconciled_at_block": 91134712,
    "reconciled_as_of": "2026-07-28T13:00:00+00:00",
    "chain_stale": false,
    "is_provably_full": true,
    "verification": "Ask Polygon whether this address moved an outcome token after reconciled_at_block. A negative answer is the completeness claim; no venue API response can carry an equivalent check."
  },
  "method_hash": "b41f8c...a97d3e",
  "disclaimer": "Diagnostics on a public on-chain record, not investment advice, not a forecast."
}

Before the read lands, or when a stored read no longer proves complete, this returns {"available": false, "unavailable_reason": "not_reconciled", ...} with every estimate field null -- a partial or windowed number is never returned in its place.

reconciled_at_block is the Polygon block this read's completeness was checked against, so you can check currency independently: ask the chain whether the address moved an outcome token after this block. It does not say the read is current now. Null when available is false, never 0.

completeness_proof and method_hash carry the same anchors as the verdict endpoint: the proof block gathers the reconciliation stamps plus the verification instruction, and the hash pins the frozen methodology the read was gated under. Read the values inside the block, not its presence. Both are null when available is false.

Per-plan set cap and refresh cadence

The cap bounds distinct wallets in your set; the cadence is how often the refresh worker re-reads each member. These figures are generated from the same plan table the API enforces, so this page cannot state a limit the product does not apply.

Free

Not included

Researcher / Pro

10 wallets
30-day cadence

Trader

50 wallets
7-day cadence

Enterprise

100 wallets
7-day cadence

100 wallets is the platform ceiling regardless of plan. Some Enterprise accounts run on a custom-quoted cap instead of the default shown here; a bespoke cap always resolves no higher than 100 and, if left unset on an otherwise-customized account, floors to the Researcher cap rather than the Enterprise default.

Key Management

Key management endpoints use JWT authentication (your Supabase session), not API keys. These are used by the Settings page to manage keys.

POST/keys

Generate a new API key. The raw key is returned once and cannot be retrieved again.

Request Body

{
  "label": "My Slack integration"   // optional, default "default"
}

Response (201)

{
  "raw_key": "cvx_abc123...",
  "key_id": "uuid",
  "label": "My Slack integration",
  "created_at": "2026-03-07T12:00:00Z",
  "message": "Store this key securely -- it will not be shown again."
}
GET/keys

List your API keys (shows last 4 characters only).

Response (200)

{
  "keys": [
    {
      "id": "uuid",
      "label": "My Slack integration",
      "key_preview": "3xyz",
      "revoked": false,
      "last_used_at": "2026-03-07T10:00:00Z",
      "created_at": "2026-03-01T12:00:00Z"
    }
  ]
}
DELETE/keys/{key_id}

Revoke an API key. The key will immediately stop working.

Response (200)

{
  "revoked": true,
  "key_id": "uuid"
}

Rate Limits & Plan Access

Free

Edge Score API not included. Use the analyzer for one-off scoring.

Researcher · Trader INCLUDED

60 req/min on /from-features, 10 req/min on /from-wallet. Authenticate via Supabase JWT (X-API-Key for the wallet-verdict and key-authenticated public routes). Generate keys from Settings → API Keys.

Custom Enterprise

Higher rate limits, dedicated capacity statement, support windows, and named-cohort benchmarking only when written into an approved pilot or contract. Contact for terms.

Rate-limited responses (429) include a Retry-After header (the window length, in seconds); there is no X-RateLimit-Remaining header today. Free-tier callers receive HTTP 403 with detail.error = "plan_required" and an upgrade_url pointing at /pricing.

Code Examples

Edge Score from a Polymarket wallet

curl -X POST https://www.convexly.app/api/engine/edge-score/from-wallet \
  -H "Authorization: Bearer $SUPABASE_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "wallet_address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839"
  }'

Wallet skill verdict (X-API-Key)

curl https://www.convexly.app/api/engine/public/wallets/0x56687bf447db6ffa42ffe2204a05edaa20f55839/verdict \
  -H "X-API-Key: cvx_your_key"

Error Responses

401

Missing or invalid API key

404

Resource not found (or doesn't belong to you)

422

Validation error (missing or invalid fields)

429

Rate limit exceeded

502

Upstream database error

Need help? Manage API keys · Go to Dashboard

Machine-readable spec: the X-API-Key surface (Wallet Verdict and the other key-authenticated public routes) publishes an OpenAPI 3 document at convexly.app/openapi.json, generated from the FastAPI schema and checked in CI against drift from the code.