Agara

Markets

Find the events, markets, and outcome tokens to trade. Lists, full event detail, and how to extract the condition_id and token_id every trading endpoint expects.

Every trading endpoint is keyed by IDs that come from this surface:

  • token_id — the outcome you want to buy or sell. Required by order placement, outcome-specific orderbook reads, and the orderbook and best_quote stream channels.
  • condition_id — the on-chain market the outcome belongs to. Used by Portfolio filters, condition-wide position operations, and the trades and market_status stream channels.

Both live on the same public endpoints documented here. No authentication required — the markets API is open.

Base URL: https://app.agara.xyz — every path below is under /api/v1.

Endpoint summary

What you want to doMethodPath
Browse / search eventsGET/events
Read one event + its marketsGET/events/{slug}
List markets (with outcomes inline)GET/markets?source=agara

A single event groups one or more markets (e.g. "2025 Presidential Election" with one binary market per candidate). Each market currently has exactly two outcomes, including markets in a neg-risk group. You trade against an outcome's token_id.

List events

GET /api/v1/events?source=AGARA

Paginated, keyset cursor. Defaults to live events first, then upcoming events by start time, with finished and settled events last. Keep source=AGARA on the first request and every paginated request.

Query parameters

ParamTypeNotes
limitint1–100, default 20
cursorstringOpaque cursor from a previous response's next_cursor
categorystringFilter to a category slug (politics, sports, tennis, …)
event_type_bucketgames | propsNarrow to sports games or prop markets
filterstringNamed filter, e.g. sports_live
sourcestringFilter to AGARA with source=AGARA
exclude_endedtrue | falseDrop events that have already finished, and events with nothing left to trade. A repeating market is kept while its next round is still being set up, so check the market's state before you try to place an order
resolutionall | active | proposed | disputed | resolvedFilter by settlement status. active = not yet in resolution
sorttime | volume | marketsSecondary sort within each lifecycle group (live always leads); default is soonest-first
include_marketstrue | falseAdd listing_markets with lean market/outcome data for prop and multi-outcome rows (off by default)

filter=sports_live is the exception to the response below: it returns leaf-category sections without pagination. Other filters return the flat, paginated events shape.

Response

{
  "events": [
    {
      "id": "1afe337d-ae98-4d43-9d6a-2ba14b8bbe4c",
      "slug": "city-a-vs-city-b",
      "title": "City A vs City B",
      "event_type": "GAME",
      "is_accepting_orders": true,
      "is_live": true,
      "start_time": "2025-11-23T17:00:00.000Z",
      "end_time": null,
      "volume_micro": "311413555387",
      "market_count": 1,
      "primary_category": { "slug": "sports", "label": "Sports" },
      "resolution_status": "ACTIVE",
      "is_group_root": false,
      "main_market_id": null
    }
  ],
  "pagination": {
    "next_cursor": "eyJiIjowLCJ0IjoxNzYzOTE3MjAwLCJ2IjozMTE0MTM1NTUzODcsImkiOiIxYWZl…",
    "limit": 20
  }
}

By default, events[] is a list view with display metadata rather than order-ready IDs. Setting include_markets=true adds a lean listing_markets subset with outcome token IDs for non-game events: the main market for a proposition or the top candidate markets for a multi-outcome event. Fetch event detail by slug when you need the event's complete market set and full fields.

primary_category is the event's main category as curated by the operators, and is null when the event has none.

market_count is how many markets the event covers. It is left out for a recurring series, which opens a fresh market every cycle and so has no meaningful total to report. Treat the field as optional and fall back to the markets you were sent rather than to zero.

Examples

curl "$AGARA_BASE_URL/api/v1/events?source=AGARA&category=politics&limit=10"
import os, requests
 
AGARA_BASE_URL = os.environ["AGARA_BASE_URL"]
 
resp = requests.get(f"{AGARA_BASE_URL}/api/v1/events", params={
    "source": "AGARA",
    "category": "politics",
    "limit": 10,
    "exclude_ended": "true",
})
resp.raise_for_status()
for ev in resp.json()["events"]:
    print(ev["slug"], "—", ev["title"])

Pagination

Pass the previous response's pagination.next_cursor back as ?cursor=. When it comes back null, you've reached the end.

cursor = None
while True:
    params = {"source": "AGARA", "limit": 50}
    if cursor:
        params["cursor"] = cursor
    page = requests.get(f"{AGARA_BASE_URL}/api/v1/events", params=params).json()
    for ev in page["events"]:
        ...
    cursor = page["pagination"]["next_cursor"]
    if cursor is None:
        break

Event detail

GET /api/v1/events/{slug}

The full event, its markets, and every outcome on each market — including the condition_id per market and the token_id per outcome. Recurring series (such as the 5-minute crypto markets) return the live window plus the nearest upcoming and most recent settled cycles, not the full cycle history.

Response (trimmed to trading-relevant fields)

{
  "id": "1afe337d-…",
  "slug": "city-a-vs-city-b",
  "title": "City A vs City B",
  "is_accepting_orders": true,
  "is_live": true,
  "resolution_status": "ACTIVE",
  "markets": [
    {
      "slug": "will-city-a-win",
      "question": "Will City A win?",
      "display_label": "City A",
      "exchange": "AGARA",
      "condition_id": "0x03957fd52ec3c1bc1985a566fa8fb7d1e5ee717d3ca5f83f0905aa24fbc33d96",
      "is_accepting_orders": true,
      "tick_size_micro": "1000",
      "min_order_size_micro": "5000000",
      "outcomes": [
        {
          "label": "Yes",
          "token_id": "95507039935216817251467799435356219871689771437585117346620907704908494213017",
          "price_micro": "27000",
          "best_bid_micro": "26000",
          "best_ask_micro": "28000"
        },
        {
          "label": "No",
          "token_id": "39084258401822540125900462921743234567890123456789012345678901234567890123456",
          "price_micro": "973000",
          "best_bid_micro": "972000",
          "best_ask_micro": "974000"
        }
      ]
    }
  ]
}

Real responses carry more fields (category breadcrumbs, sport enrichment, market tabs, display bags); the table below is the subset that matters for trading.

Fields you'll use

PathUse it for
markets[].condition_idPositions filter, market_status channel
markets[].outcomes[].token_idPlace order, Orderbook, streaming orderbook / best_quote channels
markets[].is_accepting_ordersIf false, orders on this market's tokens will be rejected with 400
markets[].tick_size_microSmallest price increment, in micro-probability — your price_micro must be a multiple of this
markets[].min_order_size_microCatalogue metadata; use Order size limits for enforced AGARA bounds
markets[].exchangeExchange the market settles on; AGARA docs use AGARA
outcomes[].best_bid_micro / best_ask_microTop-of-book quote — useful for picking a price without a separate orderbook call

Errors

StatusReason
404No event with that slug, or it's archived

Examples

curl "$AGARA_BASE_URL/api/v1/events/$AGARA_EVENT_SLUG"
event_slug = os.environ["AGARA_EVENT_SLUG"]
ev = requests.get(
    f"{AGARA_BASE_URL}/api/v1/events/{event_slug}"
).json()
 
for m in ev["markets"]:
    if not m["is_accepting_orders"]:
        continue
    outcome = m["outcomes"][0]
    print(m["display_label"], outcome["label"], "token →", outcome["token_id"])

List AGARA-only markets

GET /api/v1/markets?source=agara

Dedicated discovery surface for AGARA-native markets — every non-archived market where exchange == "AGARA", with its outcomes (and token_ids) inline. One request hands you everything you need to place an order. Unauthenticated, like the rest of the markets API.

Use this when you want the full AGARA catalogue with outcomes inline in a single request. The general /events endpoint is broader and lighter — use it for browsing and pagination by event.

Query parameters

ParamTypeNotes
sourcestringRequired. Only agara (case-insensitive) is accepted today.
limitint1–256, default 64
cursorstringOpaque cursor from a previous response's next_cursor
event_slugstringNarrow to markets on a single event
statestringRestrict to one lifecycle state, such as ACTIVE (case-insensitive)

Response

{
  "markets": [
    {
      "id": "9c8a…",
      "exchange": "AGARA",
      "slug": "will-foo-happen",
      "question": "Will Foo happen by year-end?",
      "display_label": "Will Foo happen by year-end?",
      "display_type": "BINARY",
      "is_accepting_orders": true,
      "volume_micro": "12345678",
      "volume_24h_micro": "1234567",
      "tick_size_micro": "1000",
      "min_order_size_micro": "5000000",
      "line": null,
      "condition_id": "0x4a8b…",
      "neg_risk_id": null,
      "resolution_status": null,
      "outcomes": [
        {
          "id": "…",
          "index": 0,
          "label": "Yes",
          "token_id": "21345678901234567890123456789012345678901234567890123456789012345678901234567",
          "price_micro": "500000",
          "best_bid_micro": "490000",
          "best_ask_micro": "510000"
        },
        {
          "id": "…",
          "index": 1,
          "label": "No",
          "token_id": "98765432109876543210987654321098765432109876543210987654321098765432109876543",
          "price_micro": "500000",
          "best_bid_micro": "490000",
          "best_ask_micro": "510000"
        }
      ],
      "event": {
        "id": "1afe…",
        "slug": "agara-foo-event",
        "title": "Foo Event",
        "active": true
      }
    }
  ],
  "pagination": {
    "next_cursor": "eyJ2ZXIiOjEsImEiOjEsInYiOiIxMjM0NTY3OCIsImkiOiI5YzhhLi4uIiwiZiI6ImE3YjNjOWQxZTVmMiJ9",
    "limit": 64
  }
}

Rows are sorted by is_accepting_orders (tradeable first), then volume_micro (highest first), then id for cursor stability. condition_id is populated once the market is deployed on-chain and remains available after the market stops accepting orders. It is null before deployment. Trade against the outcomes[].token_id directly.

Examples

curl "$AGARA_BASE_URL/api/v1/markets?source=agara&limit=20"
import os, requests
 
AGARA_BASE_URL = os.environ["AGARA_BASE_URL"]
 
resp = requests.get(f"{AGARA_BASE_URL}/api/v1/markets", params={"source": "agara", "limit": 20})
resp.raise_for_status()
for m in resp.json()["markets"]:
    yes = next((o for o in m["outcomes"] if o["label"].lower() == "yes"), m["outcomes"][0])
    print(m["slug"], "→ YES token", yes["token_id"])

Pagination

Same shape as /events — pass the previous response's pagination.next_cursor back as ?cursor=. Reaching the end returns next_cursor: null.

A cursor is tied to the filters of the request that produced it. If you reuse it while changing source, event_slug, or state, the request fails with 400 — drop the cursor and start again from the first page.

Picking the right outcome

All markets are currently binary — two outcomes labelled "Yes" and "No", or two team names. Complementary prices sum to one collateral unit, but a SELL requires you to hold that outcome token; choose the side and order direction that match your inventory and strategy.

A few patterns to know:

  • Yes/No prop: outcomes[].label is literally "Yes" / "No".
  • Team moneyline: outcomes[].label is the team name. The helper field short_label (e.g. "LAL", "BOS") is good for compact UIs.
  • Over/Under totals: outcomes[].label is "Over" / "Under"; the threshold lives on the parent market's line field.
  • Multi-outcome (elections, championships): one event holds many binary markets, one per candidate. Each market has its own condition_id and a Yes/No pair of token_ids. Pick the market for the candidate you want, then the YES outcome to bet on them winning.
  • Three-way moneyline: the web app may group Home / Draw / Away binary members into one card or grid. Each member retains separate YES and NO controls; there is no three-outcome CTF condition.

Neg-risk works the same way at the market level: it groups mutually exclusive binary markets, but every member keeps its own condition, two outcome tokens, and orderbook.

End-to-end: from slug to placed order

import os, requests
 
AGARA_BASE_URL = os.environ["AGARA_BASE_URL"]
TOKEN = os.environ["AGARA_TOKEN"]
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
 
# 1. Find a tradeable AGARA event, then fetch its full detail.
listing = requests.get(
    f"{AGARA_BASE_URL}/api/v1/events",
    params={"source": "AGARA", "exclude_ended": "true", "limit": 20},
).json()
event_slug = next(e["slug"] for e in listing["events"] if e["is_accepting_orders"])
ev = requests.get(f"{AGARA_BASE_URL}/api/v1/events/{event_slug}").json()
 
# 2. Pick a market and either of its two outcomes.
market = next(m for m in ev["markets"] if m["is_accepting_orders"])
outcome = market["outcomes"][0]
token_id = outcome["token_id"]
 
# 3. Trade the token. From here every call is the standard
#    /trade/v1 surface — see Orders / Orderbook / Portfolio.
order = requests.post(f"{AGARA_BASE_URL}/trade/v1/orders", headers=HEADERS, json={
    "token_id": token_id,
    "side": "BUY",
    "type": "LIMIT",
    "time_in_force": "GTC",
    "price_micro": str(int(0.30 * 1_000_000)),
    "shares_micro": str(1 * 1_000_000),
}).json()
print("placed", order["order_id"])

What's next

  • Quickstart — discover, place, and watch an order end-to-end in one script.
  • Orders — every order operation, with the token_id you just discovered.
  • Orderbook — live depth for a token_id.
  • Streaming — subscribe to orderbook / best_quote by token_id, and market_status by condition_id.
  • Catalogue API reference — every discovery endpoint, parameter, and status code.