Gemini — Tool Reference
Query Gemini prediction market odds, orderbooks, contract volume, and price history
Your AI assistant discovers and invokes these tools through the
MCP Server's meta tools —
it calls execute_tool with the tool name and arguments below.
# get_event_details read
Read one Gemini prediction-market event and all its contracts by event ticker.
Full description
Get a single Gemini event with its full contract list. Keyed on the EVENT TICKER (e.g. 'FED260917') from search_markets. Gemini returns 404 for an event id, slug, contract ticker, or instrumentSymbol — this tool reports that as NOT_FOUND with the same explanation rather than guessing an alternate lookup. Works for events in any status: active, approved, under_review, and settled all return successfully. Unlike search_markets, `live_only` defaults to FALSE so you see settled and closed contracts alongside live ones. Every contract carries `is_live` — true only when event status is active, contract status is active, marketState is open, and expiryDate is in the future — plus `not_live_reason` naming the first failing check. The event's own `is_in_play` is Gemini's in-play marker, NOT a rollup of those contract verdicts — an event can report is_in_play false with every contract live; `live_contract_count` is the rollup. Prices are quote/last-trade data as YES probabilities from 0.00 to 1.00; a contract that has never traded reports last_trade_price = null and is never given a bid/ask midpoint in its place. A resolved contract reports `result` (observed only as 'yes' or 'no', but any other side Gemini sends is passed through lowercased, so treat an unrecognized value as unknown rather than as a no), `resolved_at`, and — only where the outcome is numeric — `settlement_value`, the reference value the outcome was decided against in the underlying's own units (e.g. a BTC index price), never a probability. Since every quote field is null once a contract settles, `result` is the only outcome signal; a settled sports contract has a result and no settlement_value. The event carries its own `settlement_value` once the whole event settles numerically. Answering "which outcome won?" needs live_only=false, the default here. `volume` and `volume_24h` are EXECUTED CONTRACT QUANTITY, never USD notional, and may be fractional — contracts whose quantityIncrement is 0.01 trade in hundredths, so a volume of 30.59 is ordinary rather than a unit error. `volume_delta_24h_pct` is a percentage and carries no units. `best_bid` / `best_ask` / `spread` / `midpoint` are live YES-side quote data in probability terms, 0.0-1.0. `spread` and `midpoint` are null whenever either side of the book is missing — never synthesized from one side or from the last trade. Because all three venues quote in the same 0-1 probability terms, these fields ARE directly comparable across the kalshi, polymarket and gemini toolkits — unlike the volume fields, whose units differ by venue. Gemini-specific caveat: a null `best_bid` / `best_ask` here means the bulk event summary did not publish that side as a usable number — NOT that the direct order book is empty — and a null `spread` / `midpoint` likewise reports an incomplete SUMMARY quote rather than a one-sided book. The `buy_*` / `sell_*` fields are raw upstream indicative values that can simply mirror the last trade. When an executable quote matters, confirm with get_orderbook — the direct book, which is authoritative but costs one request per contract. Pass any contract's `instrument_symbol` to get_market_snapshot, get_market_history, get_market_changes, get_orderbook, or get_recent_trades.
| Parameter | Type | Required | Description |
|---|---|---|---|
event_ticker | string | Yes | Gemini EVENT ticker from search_markets, e.g. 'FED260917'. Case-insensitive. Event ids, slugs, contract tickers and instrumentSymbols are all rejected with NOT_FOUND — this endpoint keys on the event ticker only. |
live_only | boolean | No | When true, return only contracts passing the four-condition liveness check. Defaults to FALSE here so you can see settled and closed siblings; every contract carries is_live and not_live_reason either way. Default: false |
max_contracts | integer | null | No | Optional cap on contracts returned. Null (the default) returns all of them. |
Parameter schema (JSON)
{
"description": "Parameters for get_event_details tool.",
"properties": {
"event_ticker": {
"description": "Gemini EVENT ticker from search_markets, e.g. 'FED260917'. Case-insensitive. Event ids, slugs, contract tickers and instrumentSymbols are all rejected with NOT_FOUND — this endpoint keys on the event ticker only.",
"minLength": 1,
"title": "Event Ticker",
"type": "string"
},
"live_only": {
"default": false,
"description": "When true, return only contracts passing the four-condition liveness check. Defaults to FALSE here so you can see settled and closed siblings; every contract carries is_live and not_live_reason either way.",
"title": "Live Only",
"type": "boolean"
},
"max_contracts": {
"anyOf": [
{
"maximum": 100,
"minimum": 1,
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional cap on contracts returned. Null (the default) returns all of them.",
"title": "Max Contracts"
}
},
"required": [
"event_ticker"
],
"title": "GetEventDetailsParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"GeminiContractSummary": {
"description": "One tradable contract (outcome) inside a Gemini event.",
"properties": {
"instrument_symbol": {
"description": "Gemini instrumentSymbol, e.g. 'GEMI-FED260917-MAINTAIN'. This is the market_id used by get_market_snapshot, get_market_history, get_market_changes, get_orderbook and get_recent_trades. Case-sensitive when passed to the analytics tools.",
"title": "Instrument Symbol",
"type": "string"
},
"contract_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini internal contract id, e.g. '168090-404860'. Not accepted by any other tool in this toolkit.",
"title": "Contract Id"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Human-readable outcome label, e.g. 'Fed maintains rate'",
"title": "Label"
},
"ticker": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Short contract ticker within the event, e.g. 'MAINTAIN'",
"title": "Ticker"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini contract status, e.g. 'active' or 'settled'",
"title": "Status"
},
"market_state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini marketState: 'open', 'post_only', or 'closed'. 'post_only' contracts are not tradable even when status is 'active'.",
"title": "Market State"
},
"expiry_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 contract expiry (UTC)",
"title": "Expiry Date"
},
"effective_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 date the contract became effective (UTC)",
"title": "Effective Date"
},
"is_live": {
"description": "True only when event status is 'active' AND contract status is 'active' AND market_state is 'open' AND expiry_date is in the future. A missing or unparseable expiry_date makes this False. Computed here from those four upstream signals — Gemini publishes no per-contract liveness flag of its own. This is the ONLY field the live_only filter reads. Do not confuse it with GeminiEventSummary.is_in_play, Gemini's event-level in-play flag, which is an unrelated concept and is never consulted by this check.",
"title": "Is Live",
"type": "boolean"
},
"not_live_reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "When is_live is False, the first failing check: 'event_status', 'contract_status', 'market_state', or 'expiry'. None when is_live is True.",
"title": "Not Live Reason"
},
"is_combo": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "Whether this is a combination contract",
"title": "Is Combo"
},
"result": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Settlement side, lowercased from Gemini's resolutionSide. The only values observed are 'yes' and 'no' (measured over 1000 settled and 200 active events), but this is NOT a closed set: any other side Gemini sends — a void or cancellation, say — is passed through lowercased rather than dropped. So do not branch `== 'yes'` with an else that means no; test for 'yes' and 'no' explicitly and treat any other value as unknown rather than as a loss. Null means Gemini supplied no side, which is usually an unresolved contract but is not proof of one — `resolved_at` is read independently and can be set while this is null. Settled contracts have been observed carrying no usable price fields at all, leaving this the only outcome signal — that is Gemini's observed habit rather than an invariant this toolkit enforces, so read the price fields for what they are instead of inferring settlement from them. Lowercase matches the kalshi toolkit's `result`; the polymarket toolkit capitalizes its own ('Yes'/'No'), so never compare the two case-sensitively. A contract can resolve while its event is still 'active', so a non-null result does not imply the event has settled.",
"title": "Result"
},
"resolved_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 timestamp (UTC) at which Gemini resolved this contract; null when Gemini supplied no timestamp. Read independently of `result`, so neither implies the other: a contract can carry this with a null result (Gemini omitted the side) or a result with no timestamp. A contract that resolves early inside a still-active event carries both as soon as Gemini publishes them.",
"title": "Resolved At"
},
"settlement_value": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Reference value the outcome was settled against, expressed in the UNDERLYING's own units — e.g. 63306.96 for a BTC price market. This is NOT a probability in 0.0-1.0 like the price fields below, and NOT a contract quantity like the volume fields. Null unless Gemini published one: always null while the contract is unresolved, and also null on resolved contracts whose outcome is not numeric — a settled sports contract has a result and no settlement_value. Gemini sends it as a string; a value that will not parse as a number is reported as null.",
"title": "Settlement Value"
},
"best_bid": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Best bid as a YES probability 0.0-1.0. Quote data, not an executed price. Null means Gemini's bulk event summary published no usable bid — the field was absent, or its value would not parse as a number — and it does NOT prove the direct order book has no bids: contracts have been observed (2026-08-14) with both sides null here while get_orderbook returned resting orders. The direct book from get_orderbook is authoritative.",
"title": "Best Bid"
},
"best_ask": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Best ask as a YES probability 0.0-1.0. Quote data, not an executed price. Null means Gemini's bulk event summary published no usable ask — the field was absent, or its value would not parse as a number — and it does NOT prove the direct order book has no asks: contracts have been observed (2026-08-14) with both sides null here while get_orderbook returned resting orders. The direct book from get_orderbook is authoritative.",
"title": "Best Ask"
},
"spread": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "best_ask - best_bid, rounded to 4 decimals. Null unless Gemini's bulk event summary published BOTH sides as usable numbers — never derived from one side, from a last trade, or from the buy_* / sell_* indicative values. A null therefore says the summary carried an incomplete quote; it does NOT prove the direct order book is one-sided or empty, since contracts have been observed (2026-08-14) with both summary sides null while get_orderbook returned resting orders.",
"title": "Spread"
},
"midpoint": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "(best_bid + best_ask) / 2, rounded to 4 decimals. Null unless Gemini's bulk event summary published BOTH sides as usable numbers — never derived from one side, from a last trade, or from the buy_* / sell_* indicative values. A null therefore says the summary carried an incomplete quote; it does NOT prove the direct order book is one-sided or empty, since contracts have been observed (2026-08-14) with both summary sides null while get_orderbook returned resting orders.",
"title": "Midpoint"
},
"last_trade_price": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Last executed YES probability 0.0-1.0, as carried by Gemini's bulk event summary. Null when the summary carried none, or carried a value that would not parse as a number — usually a never-traded contract, but the field reports what the summary published, not trading history. Never replaced by a bid/ask midpoint.",
"title": "Last Trade Price"
},
"buy_yes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's raw indicative price to buy YES, 0.0-1.0, passed through unchanged. NOT a resting quote and not guaranteed executable: on contracts whose bulk summary omits bestBid/bestAsk, Gemini has been observed (2026-08-14) publishing this equal to last_trade_price. Never used to derive spread or midpoint. For an executable price, read the direct book via get_orderbook.",
"title": "Buy Yes"
},
"buy_no": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's raw indicative price to buy NO, 0.0-1.0, passed through unchanged. NOT a resting quote and not guaranteed executable: on contracts whose bulk summary omits bestBid/bestAsk, Gemini has been observed (2026-08-14) publishing this equal to last_trade_price. Never used to derive spread or midpoint. For an executable price, read the direct book via get_orderbook.",
"title": "Buy No"
},
"sell_yes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's raw indicative price to sell YES, 0.0-1.0, passed through unchanged. NOT a resting quote and not guaranteed executable: on contracts whose bulk summary omits bestBid/bestAsk, Gemini has been observed (2026-08-14) publishing this equal to last_trade_price. Never used to derive spread or midpoint. For an executable price, read the direct book via get_orderbook.",
"title": "Sell Yes"
},
"sell_no": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's raw indicative price to sell NO, 0.0-1.0, passed through unchanged. NOT a resting quote and not guaranteed executable: on contracts whose bulk summary omits bestBid/bestAsk, Gemini has been observed (2026-08-14) publishing this equal to last_trade_price. Never used to derive spread or midpoint. For an executable price, read the direct book via get_orderbook.",
"title": "Sell No"
}
},
"required": [
"instrument_symbol",
"is_live"
],
"title": "GeminiContractSummary",
"type": "object"
},
"GeminiEventSummary": {
"description": "A Gemini prediction-market event and its contracts.",
"properties": {
"event_ticker": {
"description": "Event ticker, e.g. 'FED260917'. This is the ONLY identifier get_event_details accepts.",
"title": "Event Ticker",
"type": "string"
},
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini internal event id. Not accepted by any tool in this toolkit.",
"title": "Event Id"
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Event title, e.g. 'Fed decision in September?'",
"title": "Title"
},
"slug": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL slug. Not accepted by any tool in this toolkit.",
"title": "Slug"
},
"event_type": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "'binary' (two outcomes) or 'categorical' (many outcomes)",
"title": "Event Type"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini-native category, e.g. 'Economics'. Use list_categories for the accepted values. These are NOT the same vocabulary as list_mover_categories.",
"title": "Category"
},
"subcategory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini subcategory name, e.g. 'Fed'",
"title": "Subcategory"
},
"subcategory_path": {
"description": "Subcategory breadcrumb, e.g. ['Economics', 'Fed']",
"items": {
"type": "string"
},
"title": "Subcategory Path",
"type": "array"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini event status: 'active', 'approved', 'under_review', or 'settled'",
"title": "Status"
},
"is_in_play": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's own IN-PLAY flag for the event, passed through from raw `isLive` unchanged and never computed here. It marks short-interval and currently-playing markets — an in-progress ball game, a next-hour crypto strike. It is NOT a tradability signal, NOT the contract lifecycle check, and no filter in this toolkit reads it: an event with is_in_play false routinely holds live, tradable contracts and is returned by live_only=true. For tradability read live_contract_count on this event, or GeminiContractSummary.is_live on each contract. Null when Gemini omits the flag. (Named is_live before 2026-08; that name collided with the per-contract field and is no longer emitted.)",
"title": "Is In Play"
},
"settlement_value": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Event-level settlement reference value, from Gemini's settlement.value, in the UNDERLYING's own units (e.g. a BTC index price) — not a probability and not a contract quantity. Null until the whole event settles, and null even then when the outcome is not numeric: Gemini sends an empty settlement block for sports events. Per-contract outcomes are on GeminiContractSummary.result / .settlement_value, which appear as soon as a contract resolves rather than waiting for the event.",
"title": "Settlement Value"
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Event description as plain text",
"title": "Description"
},
"tags": {
"description": "Free-form Gemini event tags. Not usable as a filter by any tool here.",
"items": {
"type": "string"
},
"title": "Tags",
"type": "array"
},
"volume": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini-reported total event volume in EXECUTED CONTRACT QUANTITY (contract quantity, which MAY BE FRACTIONAL — contracts whose quantityIncrement is 0.01 trade in hundredths), never USD notional. Reported by Gemini across all contracts in the event; it is collected independently of the per-contract volume returned by get_market_snapshot and the two are not reconciled.",
"title": "Volume"
},
"volume_24h": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini-reported trailing 24h event volume in EXECUTED CONTRACT QUANTITY, which may be fractional, never USD notional",
"title": "Volume 24H"
},
"volume_delta_24h_pct": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini-reported 24h percentage change in event contract volume",
"title": "Volume Delta 24H Pct"
},
"expiry_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 event expiry (UTC)",
"title": "Expiry Date"
},
"effective_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 date the event became effective (UTC)",
"title": "Effective Date"
},
"created_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 creation timestamp (UTC)",
"title": "Created At"
},
"terms_link": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the event's terms",
"title": "Terms Link"
},
"contracts": {
"description": "Contracts returned for this event, in Gemini's own order (never re-sorted). May be filtered by live_only and capped by max_contracts_per_event.",
"items": {
"$ref": "#/$defs/GeminiContractSummary"
},
"title": "Contracts",
"type": "array"
},
"contract_count": {
"default": 0,
"description": "Total contracts Gemini returned for this event, BEFORE any live_only filter or max_contracts_per_event cap",
"title": "Contract Count",
"type": "integer"
},
"live_contract_count": {
"default": 0,
"description": "How many of those contracts pass the four-condition liveness check, BEFORE the max_contracts_per_event cap. This — not is_in_play — is the event-level answer to 'is anything here tradable right now?', and it is what live_only filters on.",
"title": "Live Contract Count",
"type": "integer"
},
"contracts_truncated": {
"default": false,
"description": "True when max_contracts_per_event dropped contracts that would otherwise have been returned",
"title": "Contracts Truncated",
"type": "boolean"
}
},
"required": [
"event_ticker"
],
"title": "GeminiEventSummary",
"type": "object"
}
},
"description": "Result of get_event_details tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"event": {
"anyOf": [
{
"$ref": "#/$defs/GeminiEventSummary"
},
{
"type": "null"
}
],
"default": null,
"description": "The event and its contracts; null on failure"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Error message if operation failed",
"title": "Error"
}
},
"required": [
"success"
],
"title": "GetEventDetailsResult",
"type": "object"
} # get_market_changes read
Compute price and contract-volume change over 1h/24h/7d/30d for one Gemini market.
Full description
Get price and volume change over named windows for one Gemini contract. Takes the instrumentSymbol directly as the market id, URL-encoded into the request path. `windows` is a comma-separated subset of 1h, 24h, 7d, 30d, sent as a single parameter — the upstream API keeps only the last value of a repeated parameter, so a list form would silently lose windows. open and close are EXECUTED YES probabilities from 0.00 to 1.00. volume_24h_abs and volume_24h_pct describe trailing EXECUTED CONTRACT QUANTITY, never USD notional. That quantity may be fractional, so volume_24h_abs is routinely non-integer on markets Gemini quotes with a quantityIncrement of 0.01. A window with samples = 0 and null price fields is a VALID result, not an error: it means the market has no sufficient, gap-free retained history for that horizon. Gemini ingestion is comparatively young, so 7d and 30d windows are frequently null for markets whose 1h and 24h windows are populated. The upstream endpoint answers an unknown symbol with all-null windows rather than a 404, so an all-null result does not distinguish "no data yet" from "wrong symbol". When every requested window returns 0 samples, `note` says exactly that; call get_market_snapshot, which does return NOT_FOUND, to tell the two apart.
| Parameter | Type | Required | Description |
|---|---|---|---|
market_id | string | Yes | Gemini instrumentSymbol, e.g. 'GEMI-FED260917-MAINTAIN'. Case-sensitive. |
windows | string | No | Comma-separated subset of: 1h, 24h, 7d, 30d. A single comma-separated string, not a list. Default: "1h,24h,7d,30d" |
Parameter schema (JSON)
{
"description": "Parameters for get_market_changes tool.",
"properties": {
"market_id": {
"description": "Gemini instrumentSymbol, e.g. 'GEMI-FED260917-MAINTAIN'. Case-sensitive.",
"minLength": 1,
"title": "Market Id",
"type": "string"
},
"windows": {
"default": "1h,24h,7d,30d",
"description": "Comma-separated subset of: 1h, 24h, 7d, 30d. A single comma-separated string, not a list.",
"title": "Windows",
"type": "string"
}
},
"required": [
"market_id"
],
"title": "GetMarketChangesParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"WindowChange": {
"description": "Price and volume change over one named window.",
"properties": {
"window": {
"description": "One of: 1h, 24h, 7d, 30d",
"title": "Window",
"type": "string"
},
"samples": {
"description": "Number of retained observation buckets in the window. 0 means no usable data for this window.",
"title": "Samples",
"type": "integer"
},
"open": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Executed YES probability at the window start, 0.0-1.0",
"title": "Open"
},
"close": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Executed YES probability at the window end, 0.0-1.0",
"title": "Close"
},
"price_abs": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "close - open, in probability points",
"title": "Price Abs"
},
"price_pct": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "100 * (close - open) / open",
"title": "Price Pct"
},
"volume_24h_abs": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Change in trailing 24h EXECUTED CONTRACT QUANTITY over the window, which may be fractional, never USD notional",
"title": "Volume 24H Abs"
},
"volume_24h_pct": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Percentage change in trailing 24h EXECUTED CONTRACT QUANTITY over the window",
"title": "Volume 24H Pct"
}
},
"required": [
"window",
"samples"
],
"title": "WindowChange",
"type": "object"
}
},
"description": "Result of get_market_changes tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"market_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Market Id"
},
"windows": {
"items": {
"$ref": "#/$defs/WindowChange"
},
"title": "Windows",
"type": "array"
},
"volume_units": {
"default": "contracts",
"description": "Units for the volume_24h fields. Always 'contracts' — a divisible quantity, not a whole-contract count.",
"title": "Volume Units",
"type": "string"
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Set when every requested window came back with 0 samples",
"title": "Note"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Error"
}
},
"required": [
"success"
],
"title": "GetMarketChangesResult",
"type": "object"
} # get_market_history read
Read stored OHLC price history and per-bucket contract volume for one Gemini market.
Full description
Get stored OHLC history for one Gemini contract. Takes the instrumentSymbol directly as the market id, URL-encoded into the request path. Points come back ascending by timestamp in 5m, 1h, or 1d buckets — the only grains stored. open / high / low / close are EXECUTED YES probabilities from 0.00 to 1.00, built from real trades and Gemini's canonical candles. `volume` is the EXECUTED CONTRACT QUANTITY traded in that bucket and `volume_24h` is the trailing 24-hour contract quantity; neither is USD notional, and this tool never derives one by multiplying price by quantity. Both may be fractional — Gemini quotes a quantityIncrement of 0.01 on many markets — so a bucket volume of 30.59 is a real size, not a units mismatch. The first returned point routinely has volume = null, because per-bucket volume is a difference against the previous cumulative observation and there is none before the window starts. That is expected data, not an error. This makes exactly ONE request per call for at most `limit` points, whose ceiling is 2000 — it does not loop or auto-paginate. When older points exist before the first one returned, `truncated` is true and `continue_from` holds that first point's timestamp; re-invoke with `end_ts = continue_from` to walk backwards. That boundary bucket appears in both pages. A market with no stored history returns success with an empty `points` list and a note. The stored API answers with an empty series rather than a 404 for an unknown symbol, so an empty result does not by itself prove the symbol is wrong — use get_market_snapshot, which does return NOT_FOUND, to check that. Gemini's own public history reaches back only seven days, so buckets older than Toolforest's retained coverage for this symbol will not exist.
| Parameter | Type | Required | Description |
|---|---|---|---|
market_id | string | Yes | Gemini instrumentSymbol, e.g. 'GEMI-FED260917-MAINTAIN'. Case-sensitive. |
bucket | "5m" | "1h" | "1d" | No | Bucket size. These three are the only grains stored; there is no finer resolution. Default: "1h" |
start_ts | integer | string | null | No | Window start. Unix seconds or ISO 8601, e.g. 1786400000, '2026-08-04' or '2026-08-04T00:00:00Z'. Defaults by bucket: 24h back for 5m, 7 days for 1h, 90 days for 1d. |
end_ts | integer | string | null | No | Window end. Unix seconds or ISO 8601. Defaults to now. Use continue_from from a truncated response to page backwards. |
limit | integer | No | Maximum points to return in one call, and also this tool's own per-call ceiling, which is 2000. The stored API would accept 5000, but a 5000-point response does not fit this tool's size budget, so 2000 is the largest value it will take rather than a value it would quietly reduce. Use truncated / continue_from to page back further. Default: 2000 |
Parameter schema (JSON)
{
"description": "Parameters for get_market_history tool.",
"properties": {
"market_id": {
"description": "Gemini instrumentSymbol, e.g. 'GEMI-FED260917-MAINTAIN'. Case-sensitive.",
"minLength": 1,
"title": "Market Id",
"type": "string"
},
"bucket": {
"default": "1h",
"description": "Bucket size. These three are the only grains stored; there is no finer resolution.",
"enum": [
"5m",
"1h",
"1d"
],
"title": "Bucket",
"type": "string"
},
"start_ts": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Window start. Unix seconds or ISO 8601, e.g. 1786400000, '2026-08-04' or '2026-08-04T00:00:00Z'. Defaults by bucket: 24h back for 5m, 7 days for 1h, 90 days for 1d.",
"title": "Start Ts"
},
"end_ts": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Window end. Unix seconds or ISO 8601. Defaults to now. Use continue_from from a truncated response to page backwards.",
"title": "End Ts"
},
"limit": {
"default": 2000,
"description": "Maximum points to return in one call, and also this tool's own per-call ceiling, which is 2000. The stored API would accept 5000, but a 5000-point response does not fit this tool's size budget, so 2000 is the largest value it will take rather than a value it would quietly reduce. Use truncated / continue_from to page back further.",
"maximum": 2000,
"minimum": 1,
"title": "Limit",
"type": "integer"
}
},
"required": [
"market_id"
],
"title": "GetMarketHistoryParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"HistoryPoint": {
"description": "One stored OHLC bucket.",
"properties": {
"ts": {
"description": "ISO 8601 bucket start (UTC)",
"title": "Ts",
"type": "string"
},
"open": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "First executed YES probability in the bucket, 0.0-1.0",
"title": "Open"
},
"high": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Highest executed YES probability in the bucket, 0.0-1.0",
"title": "High"
},
"low": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Lowest executed YES probability in the bucket, 0.0-1.0",
"title": "Low"
},
"close": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Last executed YES probability in the bucket, 0.0-1.0",
"title": "Close"
},
"volume": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "EXECUTED CONTRACT QUANTITY traded during this bucket, which may be fractional, never USD notional. Null on the FIRST returned point when no prior cumulative observation is available to difference against — this is expected, not an error.",
"title": "Volume"
},
"volume_24h": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Trailing 24h EXECUTED CONTRACT QUANTITY as of the latest execution in this bucket, which may be fractional, never USD notional",
"title": "Volume 24H"
}
},
"required": [
"ts"
],
"title": "HistoryPoint",
"type": "object"
}
},
"description": "Result of get_market_history tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"market_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Market Id"
},
"bucket": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Bucket"
},
"since": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 window start actually requested",
"title": "Since"
},
"until": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 window end actually requested",
"title": "Until"
},
"points": {
"description": "Buckets ascending by ts",
"items": {
"$ref": "#/$defs/HistoryPoint"
},
"title": "Points",
"type": "array"
},
"count": {
"default": 0,
"title": "Count",
"type": "integer"
},
"volume_units": {
"default": "contracts",
"description": "Units for volume and volume_24h. Always 'contracts' — a divisible quantity, not a whole-contract count.",
"title": "Volume Units",
"type": "string"
},
"truncated": {
"default": false,
"description": "True when older points exist before the first returned point",
"title": "Truncated",
"type": "boolean"
},
"continue_from": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "When truncated, the ts of the FIRST (oldest) returned point. Re-invoke with end_ts = continue_from to fetch older data; that boundary bucket will be returned again in the next page.",
"title": "Continue From"
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Note"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Error"
}
},
"required": [
"success"
],
"title": "GetMarketHistoryResult",
"type": "object"
} # get_market_snapshot read
Read the latest stored price and contract volume for one Gemini market.
Full description
Get the latest stored Toolforest snapshot for one Gemini contract. Takes the instrumentSymbol from search_markets or get_event_details and uses it directly as the market id — Gemini needs no token or id translation. The symbol is case-sensitive here and is URL-encoded into the request path. `price` is the latest EXECUTED YES probability from 0.00 to 1.00, sourced from real trades and canonical candles. `volume` and `volume_24h` are EXECUTED CONTRACT QUANTITY — the quantity of contracts traded, which may be fractional — never USD notional, and they are per CONTRACT, not per event, so they will be smaller than the event-level volume search_markets reports. Gemini quotes a quantityIncrement of 0.01 on many markets, so values like 30.59 are genuine sizes rather than a units mismatch; never round them. A market with no stored observation returns price, volume, volume_24h AND ts all null — null meaning "nothing observed", not "observed as zero". Do not read a null volume as no trading: it means this market has no stored row, which is a different claim. That is valid data, not an error, and the missing price is never replaced by a bid/ask midpoint. A symbol that has never been stored at all returns NOT_FOUND. (Before 2026-08-13 this path returned volume 0.0 and a fabricated ts; market-data #83 changed both to null, which is the honest answer.)
| Parameter | Type | Required | Description |
|---|---|---|---|
market_id | string | Yes | Gemini instrumentSymbol, e.g. 'GEMI-FED260917-MAINTAIN'. Case-SENSITIVE: a lowercased symbol returns NOT_FOUND. |
Parameter schema (JSON)
{
"description": "Parameters for get_market_snapshot tool.",
"properties": {
"market_id": {
"description": "Gemini instrumentSymbol, e.g. 'GEMI-FED260917-MAINTAIN'. Case-SENSITIVE: a lowercased symbol returns NOT_FOUND.",
"minLength": 1,
"title": "Market Id",
"type": "string"
}
},
"required": [
"market_id"
],
"title": "GetMarketSnapshotParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"description": "Result of get_market_snapshot tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"venue": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Always 'gemini' on success",
"title": "Venue"
},
"market_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Market Id"
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Stored title, composed as '<event title>: <contract label>'",
"title": "Title"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Toolforest canonical category, which may differ from Gemini's native category name",
"title": "Category"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Stored lifecycle status; 'active' only when all four Gemini lifecycle conditions held at ingest",
"title": "Status"
},
"ts": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 timestamp of the stored observation (UTC)",
"title": "Ts"
},
"price": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Latest executed YES probability 0.0-1.0. Null when the stored observation carries no usable price — a market that has never traded, a market with no stored observation at all (which reports ts, volume and volume_24h null too), or a value that would not parse as a number. Never a bid/ask midpoint.",
"title": "Price"
},
"volume": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Cumulative EXECUTED CONTRACT QUANTITY for this contract, never USD notional. Per contract, not per event. May be fractional — a quantityIncrement of 0.01 makes hundredths tradable.",
"title": "Volume"
},
"volume_24h": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Trailing 24h EXECUTED CONTRACT QUANTITY for this contract, which may be fractional, never USD notional",
"title": "Volume 24H"
},
"volume_units": {
"default": "contracts",
"description": "Units for volume and volume_24h. Always 'contracts' — a divisible quantity, not a whole-contract count.",
"title": "Volume Units",
"type": "string"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Error"
}
},
"required": [
"success"
],
"title": "GetMarketSnapshotResult",
"type": "object"
} # get_movers read
List Gemini markets with the largest price moves over 1h, 24h, 7d, or 30d.
Full description
Rank Gemini prediction markets by price change over a window. Prices are EXECUTED YES probabilities from 0.00 to 1.00, so price_abs is in probability points and price_pct is a percentage of the opening probability. Windows are 1h, 24h, 7d, and 30d. For relative-VOLUME ranking use get_volume_movers instead; this tool returns no volume columns because the price ranking endpoint does not produce them. `min_volume_24h` is in EXECUTED CONTRACT QUANTITY, not dollars. Gemini publishes no USD notional for prediction markets. Contract quantity is divisible — many Gemini contracts quote a quantityIncrement of 0.01 — so the threshold and the volumes it filters on are floats, not counts. Defaults to active markets only — pass status="" to include settled and closed ones. Sports is excluded by default because it is roughly 3256 of Gemini's 4400 active stored markets; pass exclude_categories=[] to include it. Naming category or categories without also setting exclude_categories clears that default, so an explicit include filter is never cancelled by it. Use list_mover_categories to discover valid names — they are Toolforest's canonical categories and differ from Gemini's native ones (Gemini's 'Weather' is 'Climate and Weather' here, 'Tech' is 'Science and Technology'). Names are matched case-insensitively, and any value in category, categories, or exclude_categories outside the toolkit's PINNED vocabulary fails the call with INVALID_REQUEST listing the valid set, rather than quietly returning nothing. Validation is offline — it never reads the categories endpoint — so a category Gemini has newly started carrying is rejected here until the toolkit is redeployed, even though list_mover_categories will show it. That is deliberate: a live re-check on the request path could consume the invocation deadline on every typo. An empty result is VALID DATA, not a failure — a window can simply be quiet. Non-Sports category filters DO return movers: the earlier behaviour where every category except Sports came back empty was a market-data service defect (toolforest-market-data#81), fixed 2026-08-13, and is no longer expected sparseness. Long windows can still be thin, because Gemini's retained series only go back about a week. A mis-namespaced CATEGORY is no longer a cause either — unknown category names are rejected before the movers request is made. But nothing else here is vocabulary-checked, and `query` in particular is free text, so a typo there still surfaces as an empty result.
| Parameter | Type | Required | Description |
|---|---|---|---|
window | "1h" | "24h" | "7d" | "30d" | No | Window to rank price moves over. Use 24h for one day. Default: "1h" |
direction | "up" | "down" | "both" | No | Rank rising prices ('up'), falling prices ('down'), or by absolute percentage move ('both'). Default: "up" |
category | string | null | No | Single canonical market-data category filter. Case-insensitive. Prefer categories for multiple values. Use list_mover_categories for valid names — these differ from Gemini's native names in list_categories. An unrecognized name fails the call with INVALID_REQUEST listing the valid set; it is not silently ignored. Commas split this into several names, so 'Politics,Crypto' is two filters and each is validated on its own. |
categories | array<string> | null | No | Canonical market-data categories to include, e.g. ['Politics', 'Economics']. Merged with category. Empty means no include filter. Case-insensitive, and an unrecognized name fails the call with INVALID_REQUEST listing the valid set. |
exclude_categories | array<string> | null | No | Categories to exclude. Defaults to ['Sports'] because Sports is 3256 of Gemini's roughly 4400 active stored markets and would otherwise dominate every broad scan. Pass [] to include Sports. Passing category or categories without also setting this clears the default so your include filter is not cancelled. Case-insensitive, and an unrecognized name fails the call with INVALID_REQUEST listing the valid set. One name per entry — each entry is sent whole, so 'Sports,Crypto' is rejected as a single unknown name. |
query | string | null | No | Keyword filter applied before ranking, matched against market title or description, e.g. 'Fed' or 'election'. |
min_samples | integer | No | Minimum retained observation buckets a market must have in the window. Defaults to 2 so a reported move spans two genuinely distinct observations — Gemini's stored series are young and a single-sample window produces a meaningless 100% move. Default: 2 |
min_volume_24h | number | No | Minimum trailing 24h volume in EXECUTED CONTRACT QUANTITY, NOT dollars. Contract quantity may be fractional, so this is a float threshold, not a whole-contract count; a fractional value is accepted and sent to the ranking endpoint unchanged. Default 100 contracts; live active non-crypto Gemini contracts run roughly 180-900 contracts per 24h, so this suppresses dead markets without cutting real ones. Pass 0 to disable. Default: 100 |
limit | integer | No | Maximum movers to return (1..200). Default: 20 |
status | string | null | No | Filter to a single stored market status. Defaults to 'active', which excludes settled and closed markets. Pass an empty string to include all statuses. Default: "active" |
Parameter schema (JSON)
{
"description": "Parameters for get_movers tool.",
"properties": {
"window": {
"default": "1h",
"description": "Window to rank price moves over. Use 24h for one day.",
"enum": [
"1h",
"24h",
"7d",
"30d"
],
"title": "Window",
"type": "string"
},
"direction": {
"default": "up",
"description": "Rank rising prices ('up'), falling prices ('down'), or by absolute percentage move ('both').",
"enum": [
"up",
"down",
"both"
],
"title": "Direction",
"type": "string"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Single canonical market-data category filter. Case-insensitive. Prefer categories for multiple values. Use list_mover_categories for valid names — these differ from Gemini's native names in list_categories. An unrecognized name fails the call with INVALID_REQUEST listing the valid set; it is not silently ignored. Commas split this into several names, so 'Politics,Crypto' is two filters and each is validated on its own.",
"title": "Category"
},
"categories": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Canonical market-data categories to include, e.g. ['Politics', 'Economics']. Merged with category. Empty means no include filter. Case-insensitive, and an unrecognized name fails the call with INVALID_REQUEST listing the valid set.",
"title": "Categories"
},
"exclude_categories": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Categories to exclude. Defaults to ['Sports'] because Sports is 3256 of Gemini's roughly 4400 active stored markets and would otherwise dominate every broad scan. Pass [] to include Sports. Passing category or categories without also setting this clears the default so your include filter is not cancelled. Case-insensitive, and an unrecognized name fails the call with INVALID_REQUEST listing the valid set. One name per entry — each entry is sent whole, so 'Sports,Crypto' is rejected as a single unknown name.",
"title": "Exclude Categories"
},
"query": {
"anyOf": [
{
"maxLength": 200,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Keyword filter applied before ranking, matched against market title or description, e.g. 'Fed' or 'election'.",
"title": "Query"
},
"min_samples": {
"default": 2,
"description": "Minimum retained observation buckets a market must have in the window. Defaults to 2 so a reported move spans two genuinely distinct observations — Gemini's stored series are young and a single-sample window produces a meaningless 100% move.",
"maximum": 1000,
"minimum": 1,
"title": "Min Samples",
"type": "integer"
},
"min_volume_24h": {
"default": 100,
"description": "Minimum trailing 24h volume in EXECUTED CONTRACT QUANTITY, NOT dollars. Contract quantity may be fractional, so this is a float threshold, not a whole-contract count; a fractional value is accepted and sent to the ranking endpoint unchanged. Default 100 contracts; live active non-crypto Gemini contracts run roughly 180-900 contracts per 24h, so this suppresses dead markets without cutting real ones. Pass 0 to disable.",
"minimum": 0,
"title": "Min Volume 24H",
"type": "number"
},
"limit": {
"default": 20,
"description": "Maximum movers to return (1..200).",
"maximum": 200,
"minimum": 1,
"title": "Limit",
"type": "integer"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": "active",
"description": "Filter to a single stored market status. Defaults to 'active', which excludes settled and closed markets. Pass an empty string to include all statuses.",
"title": "Status"
}
},
"title": "GetMoversParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"PriceMoverInfo": {
"description": "One market's price change over the requested window.",
"properties": {
"venue": {
"description": "Always 'gemini'",
"title": "Venue",
"type": "string"
},
"market_id": {
"description": "Gemini instrumentSymbol — pass to get_market_snapshot, get_market_history, get_market_changes, get_orderbook, or get_recent_trades",
"title": "Market Id",
"type": "string"
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Stored title, '<event title>: <contract label>'",
"title": "Title"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Toolforest canonical category",
"title": "Category"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Status"
},
"samples": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Retained observation buckets used for this window",
"title": "Samples"
},
"open": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Executed YES probability at window start, 0.0-1.0",
"title": "Open"
},
"close": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Executed YES probability at window end, 0.0-1.0",
"title": "Close"
},
"price_abs": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "close - open, in probability points",
"title": "Price Abs"
},
"price_pct": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "100 * (close - open) / open",
"title": "Price Pct"
}
},
"required": [
"venue",
"market_id"
],
"title": "PriceMoverInfo",
"type": "object"
}
},
"description": "Result of get_movers tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"window": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Window"
},
"direction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Direction"
},
"limit": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Limit"
},
"min_volume_24h": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "The contract-quantity threshold actually applied. Contract quantity may be fractional, so this is a float, not a count.",
"title": "Min Volume 24H"
},
"movers": {
"items": {
"$ref": "#/$defs/PriceMoverInfo"
},
"title": "Movers",
"type": "array"
},
"count": {
"default": 0,
"title": "Count",
"type": "integer"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Error"
}
},
"required": [
"success"
],
"title": "GetMoversResult",
"type": "object"
} # get_orderbook read
Read the live Gemini order book for one contract: bid/ask levels with resting contract quantity.
Full description
Get the current Gemini order book for one prediction-market contract. Takes an instrumentSymbol from search_markets or get_event_details. Prices are YES probabilities from 0.00 to 1.00; sizes are resting CONTRACT QUANTITY, never USD notional. This tool never multiplies price by quantity. Sizes may be fractional — a contract whose quantityIncrement is 0.01 rests in hundredths, so treat every quantity as a real number. This is QUOTE data. It is a different thing from the executed-trade prices and volumes returned by get_market_snapshot, get_market_history, and get_market_changes, and the two must not be blended: a missing executed price is never filled in with a bid/ask midpoint. Books are typically shallow — three to six levels a side — and one-sided books are a real, normal state, so an empty `bids` or `asks` list is valid data rather than an error. `total_bid_quantity` and `total_ask_quantity` sum only the levels actually returned, which `depth` may have capped; bids_truncated / asks_truncated flag that. An empty side sums to 0.0, not null, so the totals are always safe to add up; best_bid, best_ask, spread and midpoint do stay null on an empty side, because a best price with no levels is undefined rather than zero. An unrecognized symbol is reported as INVALID_SYMBOL — Gemini rejects it with a 400 rather than a 404.
| Parameter | Type | Required | Description |
|---|---|---|---|
market_id | string | Yes | Gemini instrumentSymbol from search_markets or get_event_details, e.g. 'GEMI-FED260917-MAINTAIN' |
depth | integer | No | Maximum price levels to return per side. Must be at least 1 — Gemini reads a limit of 0 as 'unlimited', so this tool never sends 0. Default: 10 |
Parameter schema (JSON)
{
"description": "Parameters for get_orderbook tool.",
"properties": {
"market_id": {
"description": "Gemini instrumentSymbol from search_markets or get_event_details, e.g. 'GEMI-FED260917-MAINTAIN'",
"minLength": 1,
"title": "Market Id",
"type": "string"
},
"depth": {
"default": 10,
"description": "Maximum price levels to return per side. Must be at least 1 — Gemini reads a limit of 0 as 'unlimited', so this tool never sends 0.",
"maximum": 100,
"minimum": 1,
"title": "Depth",
"type": "integer"
}
},
"required": [
"market_id"
],
"title": "GetOrderbookParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"OrderbookLevel": {
"description": "One price level in the order book.",
"properties": {
"price": {
"description": "YES probability 0.0-1.0 at this level",
"title": "Price",
"type": "number"
},
"quantity": {
"description": "Resting size at this level, in CONTRACT QUANTITY (not USD). May be fractional: contracts whose quantityIncrement is 0.01 rest in hundredths, so this is a float, not a count of whole contracts.",
"title": "Quantity",
"type": "number"
},
"timestamp": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Unix seconds as reported by Gemini for this level. Null when Gemini omitted it or sent an unparseable value — never substituted with 0.",
"title": "Timestamp"
}
},
"required": [
"price",
"quantity"
],
"title": "OrderbookLevel",
"type": "object"
}
},
"description": "Result of get_orderbook tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"market_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Market Id"
},
"bids": {
"description": "Bid levels, highest price first, capped at depth. An empty list is a real state — one-sided books occur.",
"items": {
"$ref": "#/$defs/OrderbookLevel"
},
"title": "Bids",
"type": "array"
},
"asks": {
"description": "Ask levels, lowest price first, capped at depth. An empty list is a real state.",
"items": {
"$ref": "#/$defs/OrderbookLevel"
},
"title": "Asks",
"type": "array"
},
"best_bid": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Highest bid price, or null when there are no bids",
"title": "Best Bid"
},
"best_ask": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Lowest ask price, or null when there are no asks",
"title": "Best Ask"
},
"spread": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "best_ask - best_bid, rounded to 4 decimals to clear float-subtraction noise (Gemini quotes to 2). Null when either side is empty.",
"title": "Spread"
},
"midpoint": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "(best_bid + best_ask) / 2, rounded to 4 decimals. Null when either side is empty — never derived from one side or from a last trade.",
"title": "Midpoint"
},
"total_bid_quantity": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Sum of quantity across the RETURNED bid levels only, in contract quantity, which may be fractional. Capped by depth, so this is not the full book's resting size. An empty bid side sums to 0.0, not null — null appears only on a failed call (success=false).",
"title": "Total Bid Quantity"
},
"total_ask_quantity": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Sum of quantity across the RETURNED ask levels only, in contract quantity, which may be fractional. Capped by depth. An empty ask side sums to 0.0, not null — null appears only on a failed call (success=false).",
"title": "Total Ask Quantity"
},
"quantity_units": {
"default": "contracts",
"description": "Units for quantity, total_bid_quantity and total_ask_quantity. Always 'contracts' — Gemini publishes no USD notional for prediction markets. Contracts are divisible: those quoting a quantityIncrement of 0.01 carry fractional size.",
"title": "Quantity Units",
"type": "string"
},
"bids_truncated": {
"default": false,
"description": "True when Gemini returned more bid levels than depth",
"title": "Bids Truncated",
"type": "boolean"
},
"asks_truncated": {
"default": false,
"description": "True when Gemini returned more ask levels than depth",
"title": "Asks Truncated",
"type": "boolean"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Error"
}
},
"required": [
"success"
],
"title": "GetOrderbookResult",
"type": "object"
} # get_recent_trades read
List recent executed trades for one Gemini contract. Display only — never a volume source.
Full description
List recent executions for one Gemini prediction-market contract. Takes an instrumentSymbol from search_markets or get_event_details. Prices are executed probabilities from 0.00 to 1.00; sizes are CONTRACT QUANTITY, never USD notional, and are often fractional — Gemini fills in increments of 0.01 contracts on many markets, so 26.22 is a real size, not a bug. DO NOT SUM `quantity` INTO A VOLUME FIGURE. Gemini's generic trades feed emitted each prediction-market fill twice for a period that ended around 2026-08-08 — once in YES space and once in the complementary NO space, at the same millisecond and the same size — which double-counts the traded quantity. `paired_record_groups` counts how many such pairs are present in this response. This tool reports them; it does not remove them, and it does not guess which leg is the YES leg, because the cutover happened at different times for different symbols and the legs are not otherwise distinguishable. For any volume question use get_market_snapshot, get_market_history, or get_volume_movers, which are built on Gemini's canonical candles. Quiet markets can have all of their most recent trades on the affected side of that cutover, so a non-zero paired_record_groups is not unusual. Trades are returned newest first, in Gemini's own order, and `limit` always selects the NEWEST ones. That needs care on the `since_ts` path: Gemini truncates a timestamp-filtered window from the NEWEST end, returning the OLDEST limit_trades records of it, so this tool asks for Gemini's full 500-record cap and keeps the newest `limit` itself. When the window holds more than 500 trades even that is not enough — Gemini never sends the latest ones — and `newer_trades_omitted` plus `note` say so. Pass a later `since_ts`, or omit it entirely, to see the most recent activity. An empty list is valid data: the contract may simply never have traded.
| Parameter | Type | Required | Description |
|---|---|---|---|
market_id | string | Yes | Gemini instrumentSymbol from search_markets or get_event_details, e.g. 'GEMI-FED260917-MAINTAIN' |
limit | integer | No | Maximum trades to return. Always the NEWEST ones in scope, including when since_ts is set. Gemini's hard cap is 500. Default: 50 |
since_ts | integer | string | null | No | Return only trades at or after this time. Unix seconds or ISO 8601, e.g. 1786400000 or '2026-08-10T00:00:00Z'. Gemini caps a since_ts-filtered window at 500 records and drops the NEWEST ones to do it, so a window with more than 500 trades cannot show its latest trades at all; newer_trades_omitted flags that. |
Parameter schema (JSON)
{
"description": "Parameters for get_recent_trades tool.",
"properties": {
"market_id": {
"description": "Gemini instrumentSymbol from search_markets or get_event_details, e.g. 'GEMI-FED260917-MAINTAIN'",
"minLength": 1,
"title": "Market Id",
"type": "string"
},
"limit": {
"default": 50,
"description": "Maximum trades to return. Always the NEWEST ones in scope, including when since_ts is set. Gemini's hard cap is 500.",
"maximum": 500,
"minimum": 1,
"title": "Limit",
"type": "integer"
},
"since_ts": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Return only trades at or after this time. Unix seconds or ISO 8601, e.g. 1786400000 or '2026-08-10T00:00:00Z'. Gemini caps a since_ts-filtered window at 500 records and drops the NEWEST ones to do it, so a window with more than 500 trades cannot show its latest trades at all; newer_trades_omitted flags that.",
"title": "Since Ts"
}
},
"required": [
"market_id"
],
"title": "GetRecentTradesParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"TradeRecord": {
"description": "One executed trade as reported by Gemini's public trades feed.",
"properties": {
"tid": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini trade id",
"title": "Tid"
},
"timestamp": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Execution time, Unix seconds",
"title": "Timestamp"
},
"timestamp_ms": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Execution time, Unix milliseconds",
"title": "Timestamp Ms"
},
"price": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Executed price 0.0-1.0. NOTE: for older records this may be the NO-space leg of a paired emission — see paired_record_groups.",
"title": "Price"
},
"quantity": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Trade size in CONTRACT QUANTITY, never USD notional. Frequently fractional — fills of 26.22 or 7.85 contracts are ordinary on the contracts Gemini quotes with a quantityIncrement of 0.01.",
"title": "Quantity"
},
"side": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's `type` value for this execution: 'buy' or 'sell'",
"title": "Side"
}
},
"title": "TradeRecord",
"type": "object"
}
},
"description": "Result of get_recent_trades tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"market_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Market Id"
},
"trades": {
"description": "Trades newest first, in Gemini's own order — nothing is deduplicated or reordered. On the since_ts path this is the newest `limit` of the window Gemini returned; see newer_trades_omitted.",
"items": {
"$ref": "#/$defs/TradeRecord"
},
"title": "Trades",
"type": "array"
},
"count": {
"default": 0,
"title": "Count",
"type": "integer"
},
"quantity_units": {
"default": "contracts",
"description": "Units for quantity. Always 'contracts' — a divisible quantity, not a whole-contract count.",
"title": "Quantity Units",
"type": "string"
},
"paired_record_groups": {
"default": 0,
"description": "Number of same-millisecond groups containing a complementary pair (equal size, prices summing to 1.00) AMONG THE RETURNED TRADES. Any non-zero value means these records double-count executions and must not be summed into a volume.",
"title": "Paired Record Groups",
"type": "integer"
},
"newer_trades_omitted": {
"default": false,
"description": "True when since_ts selected more trades than Gemini will return in one response (500) — Gemini truncates such a window from the NEWEST end, so trades more recent than the first one here exist and were never sent. Always false when since_ts is omitted.",
"title": "Newer Trades Omitted",
"type": "boolean"
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Set when newer_trades_omitted is true, when paired_record_groups > 0, or both",
"title": "Note"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Error"
}
},
"required": [
"success"
],
"title": "GetRecentTradesResult",
"type": "object"
} # get_volume_movers read
List Gemini markets with unusually high or low relative contract volume over 1h or 24h.
Full description
Rank Gemini prediction markets by relative contract volume (RVOL). rvol = window_vol / baseline_per_window, where a 1h window is compared against a 24h trailing baseline and a 24h window against a 7d one. The API labels those baselines '1d' and '7d' respectively in baseline_window. RVOL is unitless because both terms are EXECUTED CONTRACT QUANTITY — the quantity of contracts traded, never USD notional. This toolkit never derives a dollar volume by multiplying price by quantity, because Gemini publishes none. Both terms are floats: many Gemini contracts quote a quantityIncrement of 0.01, and `baseline_per_window` is an average in any case, so a `window_vol` of 1856.44 is a real quantity, not a units error. Only 1h and 24h windows exist for relative volume; for longer horizons, or for ranking by price rather than volume, use get_movers. ALWAYS READ baseline_complete. When it is false the endpoint had less retained history than the requested baseline and used what it had, so the RVOL is not a full-baseline figure and must be described with that qualifier rather than presented as one; `note` restates it and `warning` carries any API-supplied text verbatim. Gaps are never filled with fabricated volume. `min_volume_24h` is in EXECUTED CONTRACT QUANTITY, not dollars, and is a float threshold rather than a whole-contract count. Defaults to active markets only — pass status="" to include settled and closed ones. Sports is excluded by default because it is roughly 3256 of Gemini's 4400 active stored markets; pass exclude_categories=[] to include it, and note that naming category or categories without setting exclude_categories clears that default. Use list_mover_categories for valid names — they are Toolforest canonical names, matched case-insensitively, and any value in category, categories, or exclude_categories outside the toolkit's PINNED vocabulary fails the call with INVALID_REQUEST listing the valid set, rather than quietly returning nothing. Validation is offline and never reads the categories endpoint, so a category Gemini has newly started carrying is rejected here until the toolkit is redeployed, even though list_mover_categories will show it. An empty result is VALID DATA, not a failure: a market can simply be new, inactive, illiquid, or missing the required baseline. A mis-namespaced CATEGORY is no longer one of the causes — unknown category names are rejected before the movers request is made — but nothing else here is vocabulary-checked, and `query` in particular is free text, so a typo there still surfaces as an empty result.
| Parameter | Type | Required | Description |
|---|---|---|---|
window | "1h" | "24h" | No | Numerator window for relative volume. Only 1h and 24h are supported — the upstream endpoint rejects 7d and 30d. Default: "1h" |
direction | "up" | "down" | "both" | No | 'up' returns the highest relative volume, 'down' the lowest, 'both' ranks by absolute relative volume. Default: "up" |
category | string | null | No | Single canonical market-data category filter. Case-insensitive. Use list_mover_categories for valid names. An unrecognized name fails the call with INVALID_REQUEST listing the valid set; it is not silently ignored. Commas split this into several names, each validated on its own. |
categories | array<string> | null | No | Canonical market-data categories to include. Merged with category. Case-insensitive, and an unrecognized name fails the call with INVALID_REQUEST listing the valid set. |
exclude_categories | array<string> | null | No | Categories to exclude. Defaults to ['Sports'] because Sports is 3256 of Gemini's roughly 4400 active stored markets. Pass [] to include Sports. Naming category or categories without setting this clears the default. Case-insensitive, and an unrecognized name fails the call with INVALID_REQUEST listing the valid set. One name per entry — each entry is sent whole, so 'Sports,Crypto' is rejected as a single unknown name. |
query | string | null | No | Keyword filter applied before ranking, matched against market title or description. |
min_volume_24h | number | No | Minimum trailing 24h volume in EXECUTED CONTRACT QUANTITY, NOT dollars. Contract quantity may be fractional, so this is a float threshold, not a whole-contract count; a fractional value is accepted and sent to the ranking endpoint unchanged. Default 100 contracts. Pass 0 to disable. Default: 100 |
limit | integer | No | Maximum movers to return (1..200). Default: 20 |
status | string | null | No | Filter to a single stored market status. Defaults to 'active'. Pass an empty string to include all statuses. Default: "active" |
Parameter schema (JSON)
{
"description": "Parameters for get_volume_movers tool.",
"properties": {
"window": {
"default": "1h",
"description": "Numerator window for relative volume. Only 1h and 24h are supported — the upstream endpoint rejects 7d and 30d.",
"enum": [
"1h",
"24h"
],
"title": "Window",
"type": "string"
},
"direction": {
"default": "up",
"description": "'up' returns the highest relative volume, 'down' the lowest, 'both' ranks by absolute relative volume.",
"enum": [
"up",
"down",
"both"
],
"title": "Direction",
"type": "string"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Single canonical market-data category filter. Case-insensitive. Use list_mover_categories for valid names. An unrecognized name fails the call with INVALID_REQUEST listing the valid set; it is not silently ignored. Commas split this into several names, each validated on its own.",
"title": "Category"
},
"categories": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Canonical market-data categories to include. Merged with category. Case-insensitive, and an unrecognized name fails the call with INVALID_REQUEST listing the valid set.",
"title": "Categories"
},
"exclude_categories": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Categories to exclude. Defaults to ['Sports'] because Sports is 3256 of Gemini's roughly 4400 active stored markets. Pass [] to include Sports. Naming category or categories without setting this clears the default. Case-insensitive, and an unrecognized name fails the call with INVALID_REQUEST listing the valid set. One name per entry — each entry is sent whole, so 'Sports,Crypto' is rejected as a single unknown name.",
"title": "Exclude Categories"
},
"query": {
"anyOf": [
{
"maxLength": 200,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Keyword filter applied before ranking, matched against market title or description.",
"title": "Query"
},
"min_volume_24h": {
"default": 100,
"description": "Minimum trailing 24h volume in EXECUTED CONTRACT QUANTITY, NOT dollars. Contract quantity may be fractional, so this is a float threshold, not a whole-contract count; a fractional value is accepted and sent to the ranking endpoint unchanged. Default 100 contracts. Pass 0 to disable.",
"minimum": 0,
"title": "Min Volume 24H",
"type": "number"
},
"limit": {
"default": 20,
"description": "Maximum movers to return (1..200).",
"maximum": 200,
"minimum": 1,
"title": "Limit",
"type": "integer"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": "active",
"description": "Filter to a single stored market status. Defaults to 'active'. Pass an empty string to include all statuses.",
"title": "Status"
}
},
"title": "GetVolumeMoversParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"VolumeMoverInfo": {
"description": "One market's relative contract volume over the requested window.",
"properties": {
"venue": {
"description": "Always 'gemini'",
"title": "Venue",
"type": "string"
},
"market_id": {
"description": "Gemini instrumentSymbol — pass to get_market_snapshot, get_market_history, get_market_changes, get_orderbook, or get_recent_trades",
"title": "Market Id",
"type": "string"
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Title"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Toolforest canonical category",
"title": "Category"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Status"
},
"window_vol": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "EXECUTED CONTRACT QUANTITY traded during the window, which may be fractional, never USD notional",
"title": "Window Vol"
},
"baseline_per_window": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Average EXECUTED CONTRACT QUANTITY per equivalent window over the trailing baseline, never USD notional. An average, so fractional even where the underlying contract trades in whole units.",
"title": "Baseline Per Window"
},
"rvol": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "window_vol / baseline_per_window. Unitless — both terms are contract quantity. 2.0 means twice the usual activity.",
"title": "Rvol"
},
"open": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Executed YES probability at window start, 0.0-1.0",
"title": "Open"
},
"close": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Executed YES probability at window end, 0.0-1.0",
"title": "Close"
},
"price_abs": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "close - open, in probability points",
"title": "Price Abs"
},
"price_pct": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "100 * (close - open) / open",
"title": "Price Pct"
}
},
"required": [
"venue",
"market_id"
],
"title": "VolumeMoverInfo",
"type": "object"
}
},
"description": "Result of get_volume_movers tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"window": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Window"
},
"baseline_window": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Baseline horizon label reported by the API: '1d' for a 1h window, '7d' for a 24h window",
"title": "Baseline Window"
},
"baseline_complete": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "Reported by the API. False means the requested baseline was not fully covered by retained history and a shorter available history was used instead, so rvol is not a full-baseline figure. Gaps are never filled with fabricated volume.",
"title": "Baseline Complete"
},
"warning": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Opaque qualifier string from the API, passed through verbatim. Usually null.",
"title": "Warning"
},
"direction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Direction"
},
"limit": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Limit"
},
"min_volume_24h": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "The contract-quantity threshold actually applied. Contract quantity may be fractional, so this is a float, not a count.",
"title": "Min Volume 24H"
},
"movers": {
"items": {
"$ref": "#/$defs/VolumeMoverInfo"
},
"title": "Movers",
"type": "array"
},
"count": {
"default": 0,
"title": "Count",
"type": "integer"
},
"volume_units": {
"default": "contracts",
"description": "Units for window_vol and baseline_per_window. Always 'contracts' — a divisible quantity, not a whole-contract count.",
"title": "Volume Units",
"type": "string"
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Set when baseline_complete is False, restating that qualifier",
"title": "Note"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Error"
}
},
"required": [
"success"
],
"title": "GetVolumeMoversResult",
"type": "object"
} # list_categories read
List the Gemini-native category values accepted by search_markets.
Full description
List Gemini's own category values for use with search_markets(category=...). Returns names only. Gemini's categories endpoint supplies no event counts and no volume, and this tool does not invent them — deriving counts would mean downloading the entire multi-megabyte event catalog for a taxonomy lookup. Two things worth knowing about this list, both true on every call: - Some values currently match zero events. They are still accepted by the filter; they simply return nothing. - 'Live' is a VIRTUAL filter, not a real category. Filtering on it returns in-play markets whose actual categories are Crypto, Commodities, Sports, and so on. In-play status is reported per event as `is_in_play`; it is a different concept from contract liveness and from search_markets' `live_only`, which filters on per-contract liveness alone. This is a DIFFERENT vocabulary from list_mover_categories, which returns Toolforest's canonical market-data categories (for example Gemini's 'Weather' appears there as 'Climate and Weather', and 'Tech' as 'Science and Technology'). Use this list for search_markets and that list for get_movers / get_volume_movers; a name from one is not guaranteed valid in the other.
No parameters.
Parameter schema (JSON)
{
"description": "Parameters for list_categories tool.",
"properties": {},
"title": "ListCategoriesParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"GeminiCategorySummary": {
"description": "One Gemini-native category value.",
"properties": {
"category": {
"description": "Category value accepted by search_markets(category=...). Matching is case-insensitive.",
"title": "Category",
"type": "string"
}
},
"required": [
"category"
],
"title": "GeminiCategorySummary",
"type": "object"
}
},
"description": "Result of list_categories tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"categories": {
"items": {
"$ref": "#/$defs/GeminiCategorySummary"
},
"title": "Categories",
"type": "array"
},
"count": {
"default": 0,
"title": "Count",
"type": "integer"
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Caveats about this list that apply on every successful call",
"title": "Note"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Error message if operation failed",
"title": "Error"
}
},
"required": [
"success"
],
"title": "ListCategoriesResult",
"type": "object"
} # list_mover_categories read
List the canonical categories accepted by get_movers and get_volume_movers, with stored market counts.
Full description
List Toolforest canonical categories for Gemini mover filters. Use these names with get_movers(category=...) / get_movers(categories=[...]) and the same arguments on get_volume_movers. Matching is case-insensitive. Those two tools validate against a PINNED COPY of this vocabulary, not against this live listing: a name outside that copy — in category, categories, or exclude_categories — fails with INVALID_REQUEST naming the offending value and listing the valid set, instead of returning an empty list that looks like a quiet market. The copy can therefore lag this endpoint. If a name appears here but a mover call rejects it, the pinned copy is stale and needs a toolkit change; retrying will not help. Validation is deliberately offline because a live re-check on the request path is an unbounded upstream call that could consume the invocation deadline on every mistyped category.
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | null | No | Filter to a single stored market status. Defaults to 'active'. Pass an empty string for no status filter. Only 'active' and 'inactive' actually match rows — any other non-empty value returns an empty list rather than an error. Default: "active" |
Parameter schema (JSON)
{
"description": "Parameters for list_mover_categories tool.",
"properties": {
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": "active",
"description": "Filter to a single stored market status. Defaults to 'active'. Pass an empty string for no status filter. Only 'active' and 'inactive' actually match rows — any other non-empty value returns an empty list rather than an error.",
"title": "Status"
}
},
"title": "ListMoverCategoriesParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"MoverCategorySummary": {
"description": "One canonical market-data category and its stored market count.",
"properties": {
"category": {
"description": "Category name to pass to get_movers(category=...) or get_volume_movers(category=...). Matching is case-insensitive, and a name outside this list is rejected there with INVALID_REQUEST.",
"title": "Category",
"type": "string"
},
"market_count": {
"default": 0,
"description": "Number of stored Gemini markets in this category matching the status filter. A market count, not an event count, and not a volume — so unlike every contract-quantity field in this toolkit, it is a genuine whole number and is never fractional.",
"title": "Market Count",
"type": "integer"
}
},
"required": [
"category"
],
"title": "MoverCategorySummary",
"type": "object"
}
},
"description": "Result of list_mover_categories tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"categories": {
"items": {
"$ref": "#/$defs/MoverCategorySummary"
},
"title": "Categories",
"type": "array"
},
"count": {
"default": 0,
"title": "Count",
"type": "integer"
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Note"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Error"
}
},
"required": [
"success"
],
"title": "ListMoverCategoriesResult",
"type": "object"
} # search_markets read
Search Gemini prediction-market events and their contracts by keyword, category, and live status.
Full description
Search Gemini prediction-market events and return their contracts. Queries Gemini's public events API directly. `query` is matched server-side as ONE contiguous case-insensitive substring of the event title, description, or ticker — not slugs, contract labels, or instrumentSymbols. Gemini does not split the query into terms and does not AND them, so word order and adjacency matter: "Fed decision" finds the September Fed event, while "fed september" finds nothing even though both words are in its title. If a multi-word query returns nothing, retry with a shorter phrase or a single distinctive word before concluding the market does not exist. `category` and `status` are also applied server-side. `live_only` (default true) is applied here, not by Gemini. A contract is live only when all four upstream signals agree: event status active, contract status active, marketState open, and expiryDate in the future. A missing or unparseable expiryDate is treated as NOT live. Gemini leaves stale metadata marked active, so no single flag is trusted: a settled contract with a 2027 expiry sitting inside an active event, and a post_only contract that is status-active with a future expiry, are both excluded only because all four checks run. That filter reads PER-CONTRACT liveness and nothing else. The event-level `is_in_play` field is Gemini's separate in-play marker — an in-progress game, a next-hour crypto strike — and takes no part in it: most events holding live contracts report is_in_play false, and live_only=true returns them anyway. Read `live_contract_count` for the event-level tradability count; is_in_play answers a different question. Every returned contract carries `instrument_symbol` — pass it to get_market_snapshot, get_market_history, get_market_changes, get_orderbook, or get_recent_trades. SETTLED OUTCOMES: a resolved contract reports `result` (observed only as 'yes' or 'no', but any other side Gemini sends is passed through, so treat an unrecognized value as unknown rather than as a no), `resolved_at`, and — only where the outcome is numeric — `settlement_value` in the underlying's own units, never a probability. Every quote field is null on a settled contract, so `result` is the only outcome signal. Reach them with status="settled" AND live_only=false: the default status "active" excludes settled events server-side, and live_only=true would drop every contract in one anyway. `result` also appears on the occasional contract that resolves early inside a still-active event. `volume` and `volume_24h` are Gemini's EXECUTED CONTRACT QUANTITY, never USD notional. Gemini publishes no USD volume for prediction markets and this tool never derives one. That quantity is NOT an integer count: Gemini sets a per-contract quantityIncrement, and the many contracts set to 0.01 — crypto, sports, commodities and weather — produce genuinely fractional volumes such as 30.59. Report them as given; do not round to whole contracts and do not read a fraction as a unit error. (`volume_delta_24h_pct` is a percentage, not a quantity, and carries no contract units at all.) PAGING: `limit` counts events surviving live_only. Because that filter is client-side, this tool pages Gemini forward until it has `limit` survivors, reaches its 4-page / 800-event scan cap, or runs low on invocation runtime. `next_offset` is the exact server offset of the first event not examined, so no event is skipped or seen twice; it is null only when the matched set really ran out. Whenever the scan stops early — scan cap, deadline, or a failed page — `note` says which, and `next_offset` lets you resume from exactly where it stopped. That holds even when Gemini omits the pagination total: `has_more` is false there because there is no total to compare against, not because the scan finished. `has_more` reports only that more server-MATCHED events remain — a follow-up page can legitimately return zero live events. `best_bid` / `best_ask` / `spread` / `midpoint` are live YES-side quote data in probability terms, 0.0-1.0. `spread` and `midpoint` are null whenever either side of the book is missing — never synthesized from one side or from the last trade. Because all three venues quote in the same 0-1 probability terms, these fields ARE directly comparable across the kalshi, polymarket and gemini toolkits — unlike the volume fields, whose units differ by venue. Gemini-specific caveat: a null `best_bid` / `best_ask` here means the bulk event summary did not publish that side as a usable number — NOT that the direct order book is empty — and a null `spread` / `midpoint` likewise reports an incomplete SUMMARY quote rather than a one-sided book. The `buy_*` / `sell_*` fields are raw upstream indicative values that can simply mirror the last trade. When an executable quote matters, confirm with get_orderbook — the direct book, which is authoritative but costs one request per contract. An empty `events` list is a valid result, not an error. WORKFLOW: 1. list_categories() -> valid Gemini category names 2. search_markets(query="Fed") -> events with contracts and instrument_symbol 3. get_market_snapshot(market_id) -> stored price and contract volume 4. get_market_history / get_market_changes -> stored time series
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | null | No | Keyword filter applied by Gemini server-side. ONE contiguous case-insensitive substring — the terms are NOT split and NOT ANDed, so word order and adjacency both matter: 'Fed decision' matches 'Fed decision in September?' but 'fed september' matches nothing. Matched against the event title, event description, and event ticker only — it does NOT match slugs, contract labels, or instrumentSymbols. Prefer a short phrase or a single word; a zero-result response means that exact substring was absent, not that no such market exists. Minimum 3 characters. Omit for an unfiltered browse. |
category | string | null | No | Single Gemini-native category filter, e.g. 'Economics'. Case-insensitive. Use list_categories for valid values; an invalid value is rejected by Gemini with an INVALID_REQUEST error. Prefer categories for multiple values. |
categories | array<string> | null | No | Gemini-native categories to include, e.g. ['Economics', 'Politics']. Merged with category. Note 'Live' is a virtual filter that returns in-play markets across real categories rather than a category of its own; in-play status is surfaced per event as is_in_play, which is unrelated to contract liveness and to live_only. |
status | string | null | No | Gemini event status filter: 'active', 'approved', 'under_review', or 'settled'. Defaults to 'active'. Pass an empty string to send no status at all — note that Gemini's unfiltered default is NOT active-only and mixes in recently settled and pending events. Default: "active" |
live_only | boolean | No | When true, return only contracts where event status is 'active' AND contract status is 'active' AND marketState is 'open' AND expiryDate is in the future, and drop events left with no such contract. When false, every contract is returned with is_live and not_live_reason set. Default: true |
limit | integer | No | Maximum events to return in this page, counted AFTER the live_only filter. Default: 20 |
start_offset | integer | No | Gemini server-side offset to resume scanning from. This is an offset into Gemini's MATCHED events, not into this tool's filtered output. Pass the next_offset value from a previous call to continue. Default: 0 |
max_contracts_per_event | integer | null | No | Cap on contracts returned per event; contracts_truncated flags any event that hit it. Pass null for no cap — Gemini events have had up to 69 contracts. Default: 10 |
Parameter schema (JSON)
{
"description": "Parameters for search_markets tool.",
"properties": {
"query": {
"anyOf": [
{
"maxLength": 200,
"minLength": 3,
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Keyword filter applied by Gemini server-side. ONE contiguous case-insensitive substring — the terms are NOT split and NOT ANDed, so word order and adjacency both matter: 'Fed decision' matches 'Fed decision in September?' but 'fed september' matches nothing. Matched against the event title, event description, and event ticker only — it does NOT match slugs, contract labels, or instrumentSymbols. Prefer a short phrase or a single word; a zero-result response means that exact substring was absent, not that no such market exists. Minimum 3 characters. Omit for an unfiltered browse.",
"title": "Query"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Single Gemini-native category filter, e.g. 'Economics'. Case-insensitive. Use list_categories for valid values; an invalid value is rejected by Gemini with an INVALID_REQUEST error. Prefer categories for multiple values.",
"title": "Category"
},
"categories": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini-native categories to include, e.g. ['Economics', 'Politics']. Merged with category. Note 'Live' is a virtual filter that returns in-play markets across real categories rather than a category of its own; in-play status is surfaced per event as is_in_play, which is unrelated to contract liveness and to live_only.",
"title": "Categories"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": "active",
"description": "Gemini event status filter: 'active', 'approved', 'under_review', or 'settled'. Defaults to 'active'. Pass an empty string to send no status at all — note that Gemini's unfiltered default is NOT active-only and mixes in recently settled and pending events.",
"title": "Status"
},
"live_only": {
"default": true,
"description": "When true, return only contracts where event status is 'active' AND contract status is 'active' AND marketState is 'open' AND expiryDate is in the future, and drop events left with no such contract. When false, every contract is returned with is_live and not_live_reason set.",
"title": "Live Only",
"type": "boolean"
},
"limit": {
"default": 20,
"description": "Maximum events to return in this page, counted AFTER the live_only filter.",
"maximum": 100,
"minimum": 1,
"title": "Limit",
"type": "integer"
},
"start_offset": {
"default": 0,
"description": "Gemini server-side offset to resume scanning from. This is an offset into Gemini's MATCHED events, not into this tool's filtered output. Pass the next_offset value from a previous call to continue.",
"minimum": 0,
"title": "Start Offset",
"type": "integer"
},
"max_contracts_per_event": {
"anyOf": [
{
"maximum": 100,
"minimum": 1,
"type": "integer"
},
{
"type": "null"
}
],
"default": 10,
"description": "Cap on contracts returned per event; contracts_truncated flags any event that hit it. Pass null for no cap — Gemini events have had up to 69 contracts.",
"title": "Max Contracts Per Event"
}
},
"title": "SearchMarketsParams",
"type": "object",
"additionalProperties": false
} Result schema (JSON)
{
"$defs": {
"GeminiContractSummary": {
"description": "One tradable contract (outcome) inside a Gemini event.",
"properties": {
"instrument_symbol": {
"description": "Gemini instrumentSymbol, e.g. 'GEMI-FED260917-MAINTAIN'. This is the market_id used by get_market_snapshot, get_market_history, get_market_changes, get_orderbook and get_recent_trades. Case-sensitive when passed to the analytics tools.",
"title": "Instrument Symbol",
"type": "string"
},
"contract_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini internal contract id, e.g. '168090-404860'. Not accepted by any other tool in this toolkit.",
"title": "Contract Id"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Human-readable outcome label, e.g. 'Fed maintains rate'",
"title": "Label"
},
"ticker": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Short contract ticker within the event, e.g. 'MAINTAIN'",
"title": "Ticker"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini contract status, e.g. 'active' or 'settled'",
"title": "Status"
},
"market_state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini marketState: 'open', 'post_only', or 'closed'. 'post_only' contracts are not tradable even when status is 'active'.",
"title": "Market State"
},
"expiry_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 contract expiry (UTC)",
"title": "Expiry Date"
},
"effective_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 date the contract became effective (UTC)",
"title": "Effective Date"
},
"is_live": {
"description": "True only when event status is 'active' AND contract status is 'active' AND market_state is 'open' AND expiry_date is in the future. A missing or unparseable expiry_date makes this False. Computed here from those four upstream signals — Gemini publishes no per-contract liveness flag of its own. This is the ONLY field the live_only filter reads. Do not confuse it with GeminiEventSummary.is_in_play, Gemini's event-level in-play flag, which is an unrelated concept and is never consulted by this check.",
"title": "Is Live",
"type": "boolean"
},
"not_live_reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "When is_live is False, the first failing check: 'event_status', 'contract_status', 'market_state', or 'expiry'. None when is_live is True.",
"title": "Not Live Reason"
},
"is_combo": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "Whether this is a combination contract",
"title": "Is Combo"
},
"result": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Settlement side, lowercased from Gemini's resolutionSide. The only values observed are 'yes' and 'no' (measured over 1000 settled and 200 active events), but this is NOT a closed set: any other side Gemini sends — a void or cancellation, say — is passed through lowercased rather than dropped. So do not branch `== 'yes'` with an else that means no; test for 'yes' and 'no' explicitly and treat any other value as unknown rather than as a loss. Null means Gemini supplied no side, which is usually an unresolved contract but is not proof of one — `resolved_at` is read independently and can be set while this is null. Settled contracts have been observed carrying no usable price fields at all, leaving this the only outcome signal — that is Gemini's observed habit rather than an invariant this toolkit enforces, so read the price fields for what they are instead of inferring settlement from them. Lowercase matches the kalshi toolkit's `result`; the polymarket toolkit capitalizes its own ('Yes'/'No'), so never compare the two case-sensitively. A contract can resolve while its event is still 'active', so a non-null result does not imply the event has settled.",
"title": "Result"
},
"resolved_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 timestamp (UTC) at which Gemini resolved this contract; null when Gemini supplied no timestamp. Read independently of `result`, so neither implies the other: a contract can carry this with a null result (Gemini omitted the side) or a result with no timestamp. A contract that resolves early inside a still-active event carries both as soon as Gemini publishes them.",
"title": "Resolved At"
},
"settlement_value": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Reference value the outcome was settled against, expressed in the UNDERLYING's own units — e.g. 63306.96 for a BTC price market. This is NOT a probability in 0.0-1.0 like the price fields below, and NOT a contract quantity like the volume fields. Null unless Gemini published one: always null while the contract is unresolved, and also null on resolved contracts whose outcome is not numeric — a settled sports contract has a result and no settlement_value. Gemini sends it as a string; a value that will not parse as a number is reported as null.",
"title": "Settlement Value"
},
"best_bid": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Best bid as a YES probability 0.0-1.0. Quote data, not an executed price. Null means Gemini's bulk event summary published no usable bid — the field was absent, or its value would not parse as a number — and it does NOT prove the direct order book has no bids: contracts have been observed (2026-08-14) with both sides null here while get_orderbook returned resting orders. The direct book from get_orderbook is authoritative.",
"title": "Best Bid"
},
"best_ask": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Best ask as a YES probability 0.0-1.0. Quote data, not an executed price. Null means Gemini's bulk event summary published no usable ask — the field was absent, or its value would not parse as a number — and it does NOT prove the direct order book has no asks: contracts have been observed (2026-08-14) with both sides null here while get_orderbook returned resting orders. The direct book from get_orderbook is authoritative.",
"title": "Best Ask"
},
"spread": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "best_ask - best_bid, rounded to 4 decimals. Null unless Gemini's bulk event summary published BOTH sides as usable numbers — never derived from one side, from a last trade, or from the buy_* / sell_* indicative values. A null therefore says the summary carried an incomplete quote; it does NOT prove the direct order book is one-sided or empty, since contracts have been observed (2026-08-14) with both summary sides null while get_orderbook returned resting orders.",
"title": "Spread"
},
"midpoint": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "(best_bid + best_ask) / 2, rounded to 4 decimals. Null unless Gemini's bulk event summary published BOTH sides as usable numbers — never derived from one side, from a last trade, or from the buy_* / sell_* indicative values. A null therefore says the summary carried an incomplete quote; it does NOT prove the direct order book is one-sided or empty, since contracts have been observed (2026-08-14) with both summary sides null while get_orderbook returned resting orders.",
"title": "Midpoint"
},
"last_trade_price": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Last executed YES probability 0.0-1.0, as carried by Gemini's bulk event summary. Null when the summary carried none, or carried a value that would not parse as a number — usually a never-traded contract, but the field reports what the summary published, not trading history. Never replaced by a bid/ask midpoint.",
"title": "Last Trade Price"
},
"buy_yes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's raw indicative price to buy YES, 0.0-1.0, passed through unchanged. NOT a resting quote and not guaranteed executable: on contracts whose bulk summary omits bestBid/bestAsk, Gemini has been observed (2026-08-14) publishing this equal to last_trade_price. Never used to derive spread or midpoint. For an executable price, read the direct book via get_orderbook.",
"title": "Buy Yes"
},
"buy_no": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's raw indicative price to buy NO, 0.0-1.0, passed through unchanged. NOT a resting quote and not guaranteed executable: on contracts whose bulk summary omits bestBid/bestAsk, Gemini has been observed (2026-08-14) publishing this equal to last_trade_price. Never used to derive spread or midpoint. For an executable price, read the direct book via get_orderbook.",
"title": "Buy No"
},
"sell_yes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's raw indicative price to sell YES, 0.0-1.0, passed through unchanged. NOT a resting quote and not guaranteed executable: on contracts whose bulk summary omits bestBid/bestAsk, Gemini has been observed (2026-08-14) publishing this equal to last_trade_price. Never used to derive spread or midpoint. For an executable price, read the direct book via get_orderbook.",
"title": "Sell Yes"
},
"sell_no": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's raw indicative price to sell NO, 0.0-1.0, passed through unchanged. NOT a resting quote and not guaranteed executable: on contracts whose bulk summary omits bestBid/bestAsk, Gemini has been observed (2026-08-14) publishing this equal to last_trade_price. Never used to derive spread or midpoint. For an executable price, read the direct book via get_orderbook.",
"title": "Sell No"
}
},
"required": [
"instrument_symbol",
"is_live"
],
"title": "GeminiContractSummary",
"type": "object"
},
"GeminiEventSummary": {
"description": "A Gemini prediction-market event and its contracts.",
"properties": {
"event_ticker": {
"description": "Event ticker, e.g. 'FED260917'. This is the ONLY identifier get_event_details accepts.",
"title": "Event Ticker",
"type": "string"
},
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini internal event id. Not accepted by any tool in this toolkit.",
"title": "Event Id"
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Event title, e.g. 'Fed decision in September?'",
"title": "Title"
},
"slug": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL slug. Not accepted by any tool in this toolkit.",
"title": "Slug"
},
"event_type": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "'binary' (two outcomes) or 'categorical' (many outcomes)",
"title": "Event Type"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini-native category, e.g. 'Economics'. Use list_categories for the accepted values. These are NOT the same vocabulary as list_mover_categories.",
"title": "Category"
},
"subcategory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini subcategory name, e.g. 'Fed'",
"title": "Subcategory"
},
"subcategory_path": {
"description": "Subcategory breadcrumb, e.g. ['Economics', 'Fed']",
"items": {
"type": "string"
},
"title": "Subcategory Path",
"type": "array"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini event status: 'active', 'approved', 'under_review', or 'settled'",
"title": "Status"
},
"is_in_play": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini's own IN-PLAY flag for the event, passed through from raw `isLive` unchanged and never computed here. It marks short-interval and currently-playing markets — an in-progress ball game, a next-hour crypto strike. It is NOT a tradability signal, NOT the contract lifecycle check, and no filter in this toolkit reads it: an event with is_in_play false routinely holds live, tradable contracts and is returned by live_only=true. For tradability read live_contract_count on this event, or GeminiContractSummary.is_live on each contract. Null when Gemini omits the flag. (Named is_live before 2026-08; that name collided with the per-contract field and is no longer emitted.)",
"title": "Is In Play"
},
"settlement_value": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Event-level settlement reference value, from Gemini's settlement.value, in the UNDERLYING's own units (e.g. a BTC index price) — not a probability and not a contract quantity. Null until the whole event settles, and null even then when the outcome is not numeric: Gemini sends an empty settlement block for sports events. Per-contract outcomes are on GeminiContractSummary.result / .settlement_value, which appear as soon as a contract resolves rather than waiting for the event.",
"title": "Settlement Value"
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Event description as plain text",
"title": "Description"
},
"tags": {
"description": "Free-form Gemini event tags. Not usable as a filter by any tool here.",
"items": {
"type": "string"
},
"title": "Tags",
"type": "array"
},
"volume": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini-reported total event volume in EXECUTED CONTRACT QUANTITY (contract quantity, which MAY BE FRACTIONAL — contracts whose quantityIncrement is 0.01 trade in hundredths), never USD notional. Reported by Gemini across all contracts in the event; it is collected independently of the per-contract volume returned by get_market_snapshot and the two are not reconciled.",
"title": "Volume"
},
"volume_24h": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini-reported trailing 24h event volume in EXECUTED CONTRACT QUANTITY, which may be fractional, never USD notional",
"title": "Volume 24H"
},
"volume_delta_24h_pct": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Gemini-reported 24h percentage change in event contract volume",
"title": "Volume Delta 24H Pct"
},
"expiry_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 event expiry (UTC)",
"title": "Expiry Date"
},
"effective_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 date the event became effective (UTC)",
"title": "Effective Date"
},
"created_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "ISO 8601 creation timestamp (UTC)",
"title": "Created At"
},
"terms_link": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the event's terms",
"title": "Terms Link"
},
"contracts": {
"description": "Contracts returned for this event, in Gemini's own order (never re-sorted). May be filtered by live_only and capped by max_contracts_per_event.",
"items": {
"$ref": "#/$defs/GeminiContractSummary"
},
"title": "Contracts",
"type": "array"
},
"contract_count": {
"default": 0,
"description": "Total contracts Gemini returned for this event, BEFORE any live_only filter or max_contracts_per_event cap",
"title": "Contract Count",
"type": "integer"
},
"live_contract_count": {
"default": 0,
"description": "How many of those contracts pass the four-condition liveness check, BEFORE the max_contracts_per_event cap. This — not is_in_play — is the event-level answer to 'is anything here tradable right now?', and it is what live_only filters on.",
"title": "Live Contract Count",
"type": "integer"
},
"contracts_truncated": {
"default": false,
"description": "True when max_contracts_per_event dropped contracts that would otherwise have been returned",
"title": "Contracts Truncated",
"type": "boolean"
}
},
"required": [
"event_ticker"
],
"title": "GeminiEventSummary",
"type": "object"
}
},
"description": "Result of search_markets tool.",
"properties": {
"success": {
"title": "Success",
"type": "boolean"
},
"events": {
"items": {
"$ref": "#/$defs/GeminiEventSummary"
},
"title": "Events",
"type": "array"
},
"count": {
"default": 0,
"description": "Number of events in this page, after the live_only filter",
"title": "Count",
"type": "integer"
},
"total_matched": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Total events matching the server-side query/category/status filters, BEFORE the client-side live_only filter. Not a count of live events. Null when Gemini omitted the pagination block.",
"title": "Total Matched"
},
"scanned": {
"default": 0,
"description": "How many server-side matched events this call examined",
"title": "Scanned",
"type": "integer"
},
"has_more": {
"default": false,
"description": "True when next_offset is still below total_matched, i.e. more MATCHED events remain to scan. It does not promise that any of them will survive live_only — a follow-up page can legitimately return zero events.",
"title": "Has More",
"type": "boolean"
},
"next_offset": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Server offset of the first event not examined. Pass as start_offset to continue. None ONLY when the matched set really ran out — an empty page, or an offset past total_matched. It is still set when Gemini omitted the pagination total, because the offset is exact either way; has_more is false there simply because there is no total to compare against.",
"title": "Next Offset"
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Set when the page scan stopped early — scan cap, invocation deadline, or a page that failed mid-scan. None means the scan ended on its own terms (limit reached or matched set exhausted).",
"title": "Note"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Error message if operation failed",
"title": "Error"
}
},
"required": [
"success"
],
"title": "SearchMarketsResult",
"type": "object"
}