> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gomry.com/llms.txt
> Use this file to discover all available pages before exploring further.

# List Catalog Events

> Page the cross-organization catalog of live public events

# List Catalog Events

Returns a cursor-paginated list of live, public, discoverable events from **every organizer on Gomry**.

<Note>
  Requires `catalog: read`. Unlike the rest of this API, this endpoint is not scoped to your organization — see [Agentic Commerce](/commerce/introduction).
</Note>

## Query Parameters

<ParamField query="limit" type="integer" default="50">
  Events per page. Maximum `100`.
</ParamField>

<ParamField query="cursor" type="string">
  Opaque. Pass `pagination.next_cursor` back verbatim — never construct one.
</ParamField>

<ParamField query="city" type="string">
  Filter by venue city.
</ParamField>

<ParamField query="country" type="string">
  ISO-3166 country name or code, matched case-insensitively.
</ParamField>

<ParamField query="starts_after" type="string">
  ISO 8601 datetime. Only events starting at or after this instant.
</ParamField>

<ParamField query="starts_before" type="string">
  ISO 8601 datetime. Must be at or after `starts_after`.
</ParamField>

<ParamField query="currency" type="string">
  Three-letter currency code. **Required whenever you send `min_price` or `max_price`.**
</ParamField>

<ParamField query="min_price" type="number">
  Minimum ticket price in major units. Requires `currency`.
</ParamField>

<ParamField query="max_price" type="number">
  Maximum ticket price in major units. Requires `currency`, and must be at or above `min_price`.
</ParamField>

<ParamField query="is_free" type="boolean">
  `true` or `false`. Filters to free or paid events.
</ParamField>

<ParamField query="q" type="string">
  Substring match on name and description. Up to 200 characters.
</ParamField>

<Warning>
  `currency` is mandatory alongside a price bound. Without it, `max_price=50` would compare 50 EUR against 50 USD against 50 GBP across a mixed-currency catalog and return results you cannot detect as wrong. Prices are not converted to a base currency on this endpoint.
</Warning>

## Response

<ResponseField name="data" type="array">
  <Expandable title="Catalog event object">
    <ResponseField name="id" type="string">Event identifier. Always a string — never assume numeric.</ResponseField>
    <ResponseField name="name" type="string">Event name.</ResponseField>
    <ResponseField name="description" type="string | null">Event description.</ResponseField>
    <ResponseField name="url" type="string">The canonical, buyable event page. Safe to send a buyer to.</ResponseField>

    <ResponseField name="start" type="object">
      <Expandable>
        <ResponseField name="utc" type="string | null">ISO 8601 instant in UTC.</ResponseField>
        <ResponseField name="timezone" type="string | null">IANA timezone.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="end" type="object">Same shape as `start`.</ResponseField>

    <ResponseField name="venue" type="object | null">
      <Expandable>
        <ResponseField name="name" type="string | null">Venue name.</ResponseField>
        <ResponseField name="address" type="string | null">Street address.</ResponseField>
        <ResponseField name="city" type="string | null">City.</ResponseField>
        <ResponseField name="state" type="string | null">State or province.</ResponseField>
        <ResponseField name="country" type="string | null">Country.</ResponseField>
        <ResponseField name="latitude" type="number | null">Latitude.</ResponseField>
        <ResponseField name="longitude" type="number | null">Longitude.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="location_type" type="string | null">`physical`, `virtual`, or `null` when undetermined. Treat `null` as unknown, not as physical.</ResponseField>
    <ResponseField name="cover_image" type="string | null">Cover image URL.</ResponseField>

    <ResponseField name="price" type="object">
      <Expandable>
        <ResponseField name="min" type="number | null">Cheapest ticket in major units. **`null` means unknown, never free.**</ResponseField>
        <ResponseField name="max" type="number | null">Most expensive ticket. Same rule.</ResponseField>
        <ResponseField name="currency" type="string | null">ISO 4217 code.</ResponseField>
        <ResponseField name="is_free" type="boolean">Only `true` on a real zero.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="availability" type="string">`on_sale`, `sold_out`, `not_yet_open`, `closed`, or `unknown`.</ResponseField>
    <ResponseField name="sales_start" type="object | null">When sales open, if they have not yet.</ResponseField>
    <ResponseField name="categories" type="array">Organizer-assigned categories.</ResponseField>

    <ResponseField name="organization" type="object">
      <Expandable>
        <ResponseField name="id" type="string">Organizer identifier.</ResponseField>
        <ResponseField name="name" type="string | null">Organizer name. This is the seller.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="ticket_types" type="null">Always `null` here. Use the [detail endpoint](/api-reference/catalog/get-catalog-event).</ResponseField>
    <ResponseField name="registration_questions" type="null">Always `null` here. Use the detail endpoint.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="pagination" type="object">
  <Expandable>
    <ResponseField name="next_cursor" type="string | null">Pass to `cursor` for the next page. `null` means the catalog ends here.</ResponseField>
    <ResponseField name="has_more" type="boolean">Whether more pages follow.</ResponseField>
  </Expandable>
</ResponseField>

There is deliberately **no `total`**. A cross-organization count is not knowable without a full scan, and a number that is either expensive or wrong is worse than an authoritative end signal.

## Paging correctly

<Warning>
  **`next_cursor: null` is the only end-of-catalog signal.** An empty `data` array mid-catalog is a page where every event failed the eligibility gate — not a terminator. A consumer that stops on an empty page silently truncates its sweep and never notices.
</Warning>

```javascript Sweeping the whole catalog theme={null}
let cursor = null;
const events = [];

do {
  const url = new URL("https://www.gomry.com/api/v1/catalog/events");
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("cursor", cursor);

  const res = await fetch(url, { headers: { "X-API-KEY": key } });
  if (!res.ok) throw new Error(`Catalog page failed: ${res.status}`);

  const page = await res.json();
  events.push(...page.data);
  cursor = page.pagination.next_cursor; // the ONLY stop condition
} while (cursor);
```

Errors always leave as a non-2xx. We never answer a failure with an empty page and a null cursor, because that would assert "the catalog ends here" and make an outage look like a shrinking catalog.

## Event price vs ticket type prices

There are two layers, and you will use both.

**Event level** — `price` and `availability`, on every list row. `price` is a range, not a single number: `min` is the cheapest way in ("from \$25"), `max` the dearest. `availability` is one verdict for the whole event. Use these to search, rank, filter and display.

**Ticket type level** — `ticket_types[]`, each with its own `price` and `available`. These are the things a buyer actually orders, and `ticket_types[].id` is what you reference when building an order. They are returned by the [detail endpoint](/api-reference/catalog/get-catalog-event) only; on this endpoint `ticket_types` is always `null`.

When the event range is derived from live ticket classes, only types a buyer could buy **right now** count toward it. Anything not on sale, anything outside its sales window, and donation or pay-what-you-want types (which have no single price to quote) are excluded — so the "from" price is never one checkout would refuse.

<Note>
  The two layers are computed at different moments and, for sellers whose inventory lives behind their own API, by different paths. On a fast-moving event they can differ slightly — a tier can sell out between your list call and your detail call.

  Treat the event range as the number you **display**, and the ticket type's own `price` as the number you **charge against**. If they disagree, the ticket type is authoritative.
</Note>

## Understanding `unknown`

`availability: "unknown"` means we could not determine it. It is **not** a synonym for `on_sale`. Treat it as "check the event page" — never as a green light to sell.

Likewise `price.min: null` means the price is unknown, not that the event is free. `is_free: true` is the only statement that a ticket costs nothing.

Both are uncommon: `price` and `availability` are resolved from live ticket classes, so a populated value is the normal case. You will still see `unknown` or a `null` price where an event has nothing we can read a price from — and you should keep handling both, because they are the honest answer rather than a guess.

<RequestExample>
  ```bash cURL theme={null}
  curl -H "X-API-KEY: your_api_key" \
    "https://www.gomry.com/api/v1/catalog/events?limit=50&city=New%20York"
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch(
    "https://www.gomry.com/api/v1/catalog/events?limit=50&city=New+York",
    { headers: { "X-API-KEY": "your_api_key" } }
  );
  const { data, pagination } = await res.json();
  ```

  ```python Python theme={null}
  import requests

  res = requests.get(
      "https://www.gomry.com/api/v1/catalog/events",
      headers={"X-API-KEY": "your_api_key"},
      params={"limit": 50, "city": "New York"},
  )
  page = res.json()
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": [
      {
        "id": "AbCdEfGhIjKlMnOpQrSt",
        "name": "Summer Tech Conference 2026",
        "description": "A two-day conference covering the latest in tech.",
        "url": "https://www.gomry.com/event/summer-tech-conference-2026-AbCdEfGhIjKlMnOpQrSt",
        "start": { "utc": "2026-07-15T14:00:00.000Z", "timezone": "America/New_York" },
        "end": { "utc": "2026-07-16T22:00:00.000Z", "timezone": "America/New_York" },
        "venue": {
          "name": "Convention Center",
          "address": "123 Main St",
          "city": "New York",
          "state": "NY",
          "country": "US",
          "latitude": 40.7128,
          "longitude": -74.006
        },
        "location_type": "physical",
        "cover_image": "https://storage.googleapis.com/...",
        "price": { "min": 79.0, "max": 249.0, "currency": "USD", "is_free": false },
        "availability": "on_sale",
        "sales_start": null,
        "categories": ["Technology"],
        "organization": { "id": "OrgAbCdEfGhIjKlMnOpQ", "name": "Tech Events Co" },
        "ticket_types": null,
        "registration_questions": null
      }
    ],
    "pagination": {
      "next_cursor": "eyJlbmQiOiIyMDI2LTA3LTE2VDIyOjAwOjAwWiJ9",
      "has_more": true
    }
  }
  ```
</ResponseExample>
