List matched events
curl --request GET \
--url https://api.oddpool.com/reference/v2/eventsimport requests
url = "https://api.oddpool.com/reference/v2/events"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.oddpool.com/reference/v2/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.oddpool.com/reference/v2/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.oddpool.com/reference/v2/events"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.oddpool.com/reference/v2/events")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.oddpool.com/reference/v2/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"items": [
{}
],
"next_cursor": {}
}Matched events
List matched events
Page live or historical matched events, filtered by venue pair and fungibility.
GET
/
reference
/
v2
/
events
List matched events
curl --request GET \
--url https://api.oddpool.com/reference/v2/eventsimport requests
url = "https://api.oddpool.com/reference/v2/events"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.oddpool.com/reference/v2/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.oddpool.com/reference/v2/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.oddpool.com/reference/v2/events"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.oddpool.com/reference/v2/events")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.oddpool.com/reference/v2/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"items": [
{}
],
"next_cursor": {}
}Page through matched events. Each item is a full event, the same object as Get Oddpool ID by ticker.
To narrow to one venue pair, add
For incremental sync, store the largest
Pass
active=true for events with markets still trading today. Without it you get the full corpus, which is mostly long-settled events.Query parameters
boolean
true returns only events with at least one venue market still trading. Recommended for live trading. Omit for the full historical corpus.boolean
true returns events that are fully fungible on at least one venue pair: both venues present, same settlement rules, outcome sets correspond, and every outcome’s two sides settle identically. false returns the negation. Narrow to a specific pair with venue_pair; permit specific edge-case differences with allow.string
Requires
fungible. Comma-separated divergence axes (timing, source, condition_scope) that should not disqualify a pair from being fungible. For example, allow=condition_scope counts cancellation/postponement-only differences as fungible. See Fungibility and cancellation risk.string
Match events where some venue pair carries this settlement class:
identical, settlement_window, or settlement_source. Narrow to one pair with venue_pair.string
Match events where some venue pair’s outcome lists line up this way:
identical, partial, or disjoint. Narrow to one pair with venue_pair.string
Optional. Narrows
fungible, basis_class, and outcome_universe to one venue pair, e.g. kalshi,polymarket_us (order-insensitive). Omit to match on any venue pair the event has. Tags: kalshi, polymarket, polymarket_us. See Supported venues.string
Category filter, sourced from Kalshi (e.g.
Sports). Free text.datetime
ISO 8601. Returns only events re-evaluated on or after this time. Pass your last sync time to fetch just what changed.
integer
default:"50"
Items per page (1–2000).
string
The
next_cursor from the previous response.fungible, basis_class, and divergence_axes describe how two venues settle relative to each other. They’re defined in Response fields.Fungibility and cancellation risk
fungible=true is strict: a single outcome that settles differently on the two venues disqualifies the whole event, so it returns fewer events than you might expect.
The usual reason is condition_scope, the most common divergence axis, and usually just the venues’ standard cancellation or postponement policy. An outcome flagged with only condition_scope is the same outcome on both venues: it settles identically in the normal case and differs only on rare edge cases (a game is cancelled or postponed past a window, a tie), where one venue pays the last-traded price and the other resolves No or 50/50. It does not mean the two markets are different.
Which filter you want depends on whether you hold positions through settlement:
- You close before the event resolves. Cancellation risk never reaches you. Include these pairs and trade them:
?fungible=true&allow=condition_scope. - You hold to settlement. Either keep the strict
?fungible=truefor pairs with no edge-case risk at all, or addallow=condition_scopeto include the cancellation-policy pairs and read each one’sdivergence_reasonto size the risk before trading.
Request
# fungible on any venue pair, counting cancellation-policy-only differences as fungible
curl -H "X-API-Key: your_api_key" \
"https://api.oddpool.com/reference/v2/events?fungible=true&allow=condition_scope&active=true&limit=100"
import requests
r = requests.get(
"https://api.oddpool.com/reference/v2/events",
headers={"X-API-Key": "your_api_key"},
params={"fungible": "true", "allow": "condition_scope", "active": "true", "limit": 100},
)
page = r.json()
venue_pair=kalshi,polymarket_us.
Response
Event[]
A page of events. Each is the full event object. Every field is defined in Response fields.
string | null
Pass as
cursor to get the next page. null on the last page.{
"items": [
{
"oddpool_id": "OPI-OO4GBEZJ",
"title": "Gremio vs Corinthians",
"subject_domain": "Sports",
"classified_at": "2026-05-25T18:42:11Z",
"venue_listings": [
{
"oddpool_id": "OPL:KALSHI:KXBRASILEIROGAME-26MAY30GPACOR",
"venue": "kalshi",
"venue_event_id": "KXBRASILEIROGAME-26MAY30GPACOR",
"venue_question": "Who will win the Brasileirão match Grêmio vs Corinthians on May 30?"
},
{
"oddpool_id": "OPL:POLYMARKET:445404",
"venue": "polymarket",
"venue_event_id": "445404",
"venue_question": "Grêmio vs Corinthians: Match Result"
}
],
"venue_pairs": [
{
"venue_a": "kalshi",
"venue_a_event_id": "KXBRASILEIROGAME-26MAY30GPACOR",
"venue_b": "polymarket",
"venue_b_event_id": "445404",
"basis_class": "identical",
"outcome_universe": "identical"
}
],
"outcomes": [
{
"oddpool_id": "OPO:OO4GBEZJ:GREMIO-FBPA-JITQ",
"label": "Grêmio FBPA",
"outcome_kind": "multi_class_named",
"outcome_params": {},
"entity": {
"oddpool_id": "OPE:TEAM:GREMIO-FBPA",
"kind": "team",
"canonical_name": "Grêmio FBPA",
"aliases": ["Grêmio", "Gremio", "Tricolor Gaúcho"],
"wikidata_qid": "Q190301",
"metadata": {
"league": "Campeonato Brasileiro Série A",
"sport": "association football",
"city": "Porto Alegre"
}
},
"venue_listings": [
{
"oddpool_id": "OPL:KALSHI:KXBRASILEIROGAME-26MAY30GPACOR-GPA",
"venue": "kalshi",
"venue_market_id": "KXBRASILEIROGAME-26MAY30GPACOR-GPA",
"venue_event_id": "KXBRASILEIROGAME-26MAY30GPACOR",
"venue_yes_token": null,
"venue_no_token": null,
"venue_question": "Gremio"
},
{
"oddpool_id": "OPL:POLYMARKET:0X830BC4CBE4FC945B6CEE65EB1B0A5FEBACB16004C4C7B702A20376E6E12B1429",
"venue": "polymarket",
"venue_market_id": "0x830bc4cbe4fc945b6cee65eb1b0a5febacb16004c4c7b702a20376e6e12b1429",
"venue_event_id": "445404",
"venue_yes_token": "30372052209701849468461148034092137114942310389987253992056832502897412952481",
"venue_no_token": "74064390857285423190264600895526377480606423020566358098133374778358135580812",
"venue_question": "Grêmio FBPA"
}
],
"venue_pairs": [
{
"venue_a": "kalshi",
"venue_b": "polymarket",
"fungible": false,
"divergence_axes": ["condition_scope"],
"divergence_reason": "Both resolve Yes if Grêmio wins in 90 minutes plus stoppage time, but Kalshi sends cancellation/reschedule over two weeks to fair price while Polymarket keeps postponed games open and canceled-without-makeup resolves No."
}
]
}
]
}
],
"next_cursor": "eyJpZCI6MTI4NDQ0fQ"
}
Paging
Follownext_cursor until it’s null:
curl -H "X-API-Key: $KEY" "https://api.oddpool.com/reference/v2/events?active=true&limit=100"
curl -H "X-API-Key: $KEY" "https://api.oddpool.com/reference/v2/events?active=true&limit=100&cursor=<next_cursor>"
classified_at you’ve seen and pass it as classified_since next time.