Skip to content

Resources: validate ranges client-side per spec bounds #212

Description

@TexasCoding

Summary

The Kalshi OpenAPI spec consistently caps limit per endpoint (e.g. MarketLimitQuery 1–1000, LimitQuery 1–100/1–200, MilestoneLimitQuery 1–500). Every resource method types limit: int | None = None and forwards it untouched to the server.

Callers passing limit=0, limit=-5, or limit=10000 discover the bound only after a wasted round trip and a 400 error.

milestones.list is the worst offender — its docstring (milestones.py:69-70) literally says "limit REQUIRED, range 1-500" but only types it as int with no validation. Same gap on structured_targets.list(page_size=...) (spec 1–2000, default 100).

For a high-reliability trading SDK, fail-fast with spec-correct bounds is the documented contract; deferring to the server burns a round trip and produces a less-actionable error.

Location

  • kalshi/resources/orders.py:407, 502, 505limit: int | None = None, no validation
  • kalshi/resources/milestones.py:69-70, 82, 104 — docstring promises bounds, code doesn't enforce
  • kalshi/resources/structured_targets.pypage_size similarly unvalidated
  • Many other *.list methods

Recommended fix

Add a _validate_limit helper in kalshi/resources/_base.py:

def _validate_limit(value: int | None, *, lo: int = 1, hi: int, name: str = "limit") -> int | None:
    if value is None:
        return None
    if not (lo <= value <= hi):
        raise ValueError(
            f"{name} must be in [{lo}, {hi}], got {value}. "
            f"Kalshi enforces this server-side; validating client-side avoids a wasted round trip."
        )
    return value

Call it from each resource's param builder using the spec-correct bounds (sources of truth live in specs/openapi.yaml under each *LimitQuery schema):

def list(self, *, limit: int | None = None, ...):
    limit = _validate_limit(limit, hi=1000)  # MarketLimitQuery
    ...

For required limits (e.g. milestones.list), make limit: int (no default) and validate.

Add a per-resource parametrized test: test_limit_below_lo_raises, test_limit_above_hi_raises, test_limit_at_boundaries_accepted.

Severity & category

medium / consistency, spec-drift

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestspec-driftUpstream OpenAPI/AsyncAPI spec changed since last sync

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions