Skip to content

discovery: listEvents omits closed by default — first-page result is dominated by settled events #75

@Nexory

Description

@Nexory

ListEventsRequestSchema (packages/client/src/actions/events.ts:34-75) declares closed: z.boolean().optional(), and toEventsSearchParams drops keys whose value is undefined. So calling client.listEvents({ pageSize: 10 }) sends no closed parameter to the Gamma /events/keyset endpoint — and the Gamma server in turn applies its own default, which is closed=true rather than false.

Live evidence

$ curl -s 'https://gamma-api.polymarket.com/events?series_id=10345&limit=100' \
    | jq '[.[] | .closed] | group_by(.) | map({(.[0]|tostring): length}) | add'
{"true": 100}

(Reproduction borrowed from Polymarket/polymarket-cli#71, which reports the same default-mismatch propagating into the CLI. The CLI omits the parameter when neither --closed nor --active is set; the new SDKs do the same when callers omit closed.)

For a less-filtered query the bias is also visible:

$ curl -s 'https://gamma-api.polymarket.com/events?limit=50' \
    | jq '[.[] | .closed] | group_by(.) | map({(.[0]|tostring): length}) | add'

returns roughly two-thirds closed=true, even though "list events" is the natural thing a brand-new SDK user does when exploring the surface.

Why this looks like a UX gap rather than a deliberate default

Both listEvents @example blocks (packages/client/src/actions/events.ts:144-160) pass closed: false explicitly:

const result = listEvents(client, {
  closed: false,
  pageSize: 10,
});

So the maintainer already knew that omitting closed produces the wrong result for the common "show me current events" workflow — the examples set it deliberately. New SDK users skimming the surface and calling client.listEvents({ pageSize: 10 }) (the example without closed) instead surface settled-only events. The docstring doesn't explain the default behavior.

The public Gamma docs (https://docs.polymarket.com/developers/gamma-markets-api/get-events) say closed defaults to false, which conflicts with the server's actual behavior — so this might also be a server contract drift rather than purely an SDK doc gap.

Possible directions

  1. Document the actual server default in the SDK — add a TSDoc note on closed?: boolean in ListEventsRequest clarifying that omitting it returns settled events. Cheapest fix; doesn't change behavior.
  2. Apply a client-side default closed: false in listEvents / toEventsSearchParams so the SDK behavior matches the Gamma docs and the example usage. Behavior-changing, but matches the "intent" the example already encodes. Might also be worth applying to listMarkets if the same default exists there.
  3. Server-side: change the Gamma default to false so docs + server + SDK all agree. Out of scope for this repo but worth flagging back to the API team.

I'd lean (2) — the maintainer's own examples already use closed: false, so codifying that as the SDK default is consistent and removes a footgun for new users. But this is a contract decision that could go differently.

Cross-SDK

Same pattern in py-sdk: list_events(closed: bool | None = None) (src/polymarket/clients/async_public.py, clients/public.py) → list_events_spec adds closed to params via _add_optional only when non-None → server applies its closed=true default. Filing companion issue at Polymarket/py-sdk.

(Also applies cross-repo to Polymarket/polymarket-cli#71, which is the original CLI report.)

Happy to PR (1) or (2) once you've decided which direction is right.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions