REST API
매칭 피드, 실시간 arb 뷰, 페어 상세 정보, 카탈로그 및 피드백 경로를 위한 엔드포인트입니다. 모든 요청은 API 키를 베어러 토큰으로 전달합니다.
REST API는 매칭된 크로스벤뉴 피드, 그 위의 arb 뷰, 페어 상세 정보를 제공합니다. Free를 포함한 모든 플랜에서 사용할 수 있습니다. 베이스 URL은 https://api.dino.markets입니다.
인증
모든 요청은 API 키를 베어러 토큰으로 전달해야 합니다. 대시보드에서 키를 생성하세요. 키는 한 번만 표시됩니다.
Authorization: Bearer sk_live_...
속도 제한
요청은 플랜별로 분당 한도가 있습니다: Free 60, Basic 300, Pro 1200. 한도를 초과하면 요청은 429를 반환합니다. 스트림은 이 방식으로 제한되지 않으며, 대신 연결 수 상한으로 제어됩니다.
GET /v2/markets
표준 마켓 목록입니다. 필터 없이 요청하면 이는 식별용 카탈로그로, 매칭된 경기당 한 행이며 거래소 티커와 커버리지 플래그가 포함되고 가격은 null입니다. signal=arb를 추가하면 실시간 기회 스냅샷에서 가격이 매겨진 동일한 행을 확정된 arb만 걸러서 받을 수 있습니다. signal=candidates를 추가하면 candidates 채널 히스토리에서 가격이 매겨진 검토 대상(review-band) 행을 받을 수 있습니다.
| Param | In | Type | Meaning |
|---|---|---|---|
sport | query | string | baseball처럼 하나의 넓은 스포츠 범주로 제한합니다. 생략하면 모든 스포츠가 대상입니다. |
league | query | string | 스포츠 내에서 MLB처럼 하나의 리그로 좁힙니다. 대소문자를 구분하지 않습니다. signal=arb일 때는 무시됩니다. |
status | query | string | 쉼표로 구분: open, live, closed, settled. 기본값은 open,live입니다. |
signal | query | string | spread(기본 카탈로그 뷰), arb(가격이 매겨진, 확정된 arb 행만), 또는 candidates(검토 대상 행). |
sort | query | string | start_time(기본값) 또는 가장 넓은 크로스벤뉴 갭을 먼저 보여주는 -spread_pts. |
include | query | string | 쉼표로 구분된 옵트인 블록: ids, depth, settlement. Includes를 참고하세요. |
limit | query | integer | 1에서 1000까지. 기본값 500. |
유효한 sport 키는 baseball, basketball, football, hockey, soccer, tennis, cricket, lol, dota2, valorant, cs2, r6, ow, mma입니다. 각 행에는 league 필드도 있으며, 거래소 시리즈가 이름을 지정하지 않은 경우 null일 수 있습니다. 어떤 스포츠와 리그가 현재 시즌 중인지 보려면 /v2/leagues를 호출하세요.
curl -s "https://api.dino.markets/v2/markets?sport=baseball&league=MLB" \
-H "Authorization: Bearer sk_live_..."
{
"count": 1,
"sport": "baseball",
"has_more": false,
"markets": [
{
"id": "dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2",
"slug": null,
"sport": "baseball",
"league": "MLB",
"title": "Nyy vs Bos",
"home": null,
"away": null,
"start_time": "2026-07-01T23:05:00+00:00",
"status": "open",
"match": "high_confidence",
"signal": "spread",
"spread_pts": null,
"potential_arb_pct": null,
"coverage": { "kalshi": false, "polymarket": false },
"outcomes": [],
"result": null,
"updated_at": "2026-07-01T22:55:00+00:00",
"priced_at": "2026-07-01T22:55:00+00:00",
"links": { "kalshi": "https://kalshi.com/markets/KXMLBGAME-26JUL01NYYBOS", "polymarket": null }
}
]
}
필터 없는 뷰는 가격을 포함하지 않습니다. outcomes는 비어 있고, signal은 항상 spread이며, coverage는 두 거래소 모두 false로 표시됩니다. 이는 식별용 카탈로그이므로 정상입니다. 이 뷰의 priced_at은 실시간 가격 시점이 아니라 해당 행의 updated_at(페어 자체가 마지막으로 변경된 시점)을 그대로 반영합니다. 행별로 별도의 가격 타임스탬프는 아직 저장되지 않기 때문입니다. 동일한 행의 실시간 가격을 보려면 signal=arb(확정된 arb만) 또는 /v2/pairs/{market_id}에 include=depth를 추가해 단일 마켓으로 요청하세요.
arb 뷰: GET /v2/markets?signal=arb
같은 엔드포인트를, 실시간 기회 스냅샷에서 가격을 매기고 매칭 엔진이 arb로 확정한 행만 필터링해 반환합니다: 정산 정합성 확인, 차단 수준의 정산 리스크 없음, 모든 결과가 두 거래소에서 가격이 매겨져 있음. 이는 기존의 별도 opportunities 피드를 대체합니다. v2에는 별도의 arb 엔드포인트가 없습니다. 이는 마켓 카탈로그 위의 뷰입니다. 이를 뒷받침하는 스냅샷은 최선형이며 모든 플랜에서 약 2분 지연됩니다.
| Param | In | Type | Meaning |
|---|---|---|---|
sport | query | string | baseball처럼 하나의 넓은 스포츠 범주로 제한합니다. |
include | query | string | 쉼표로 구분된 옵트인 블록: ids, depth, settlement. |
limit | query | integer | 1에서 1000까지. 기본값 500. |
위의 카탈로그 파라미터는 이 뷰에도 동일하게 적용됩니다: status, sort, include는 동일하게 동작합니다(league 필터만 예외로, 여기서는 무시됩니다).
curl -s "https://api.dino.markets/v2/markets?signal=arb&sport=baseball" \
-H "Authorization: Bearer sk_live_..."
{
"count": 1,
"sport": "baseball",
"has_more": false,
"markets": [
{
"id": "dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2",
"slug": null,
"sport": "baseball",
"league": "MLB",
"title": "Nyy vs Bos",
"home": null,
"away": null,
"start_time": "2026-07-01T23:05:00+00:00",
"status": "open",
"match": "high_confidence",
"signal": "arb",
"spread_pts": 2.0,
"potential_arb_pct": 1.83,
"coverage": { "kalshi": true, "polymarket": true },
"outcomes": [
{ "key": "NYY", "name": "NYY", "kalshi": 0.52, "polymarket": 0.54, "cheaper": "kalshi" },
{ "key": "BOS", "name": "BOS", "kalshi": 0.50, "polymarket": 0.46, "cheaper": "polymarket" }
],
"result": null,
"updated_at": "2026-07-01T22:55:00+00:00",
"priced_at": "2026-07-01T22:55:00+00:00",
"links": { "kalshi": "https://kalshi.com/markets/KXMLBGAME-26JUL01NYYBOS", "polymarket": null }
}
]
}
markets 배열이 비어 있고 count: 0인 상태는 차익거래 사이의 정상적인 대기 상태입니다. potential_arb_pct는 거래소 수수료를 반영하기 전, ask 대 ask 기준의 총수익률입니다. 이는 스냅샷에 대한 관측값이며, 최선형이고 캐시로 인해 다소 지연될 수 있습니다. outcomes[].kalshi와 .polymarket은 각 거래소의 ask 가격(0에서 1)이며, cheaper는 해당 결과에서 ask가 더 낮은 거래소를 나타냅니다. REST에서 priced_at은 실시간 스냅샷 시점이 아니라 페어의 마지막 기록 시점인 updated_at을 그대로 반영합니다. WebSocket 피드는 모든 가격 틱마다 실제 북 조회 시점을 전달합니다.
Candidates: GET /v2/markets?signal=candidates
검토 대상(review-band) 행입니다: 매칭 엔진이 매칭했지만 아직 확정하지 않은 매치입니다. candidates:{sport} WebSocket 채널(Pro)에 피드되는 것과 동일한 채널 히스토리에서 최선형으로 가격이 매겨집니다. 검토 상태의 마켓은 어떤 가격이 붙어 있든 항상 spread로 결정됩니다. 이 뷰는 signal: "arb" 행을 절대 반환하지 않습니다.
| Param | In | Type | Meaning |
|---|---|---|---|
sport | query | string | 하나의 넓은 스포츠 범주로 제한합니다. |
league | query | string | 스포츠 내에서 하나의 리그로 좁힙니다. |
include | query | string | 쉼표로 구분된 옵트인 블록: ids, depth, settlement. |
limit | query | integer | 1에서 1000까지. 기본값 500. |
curl -s "https://api.dino.markets/v2/markets?signal=candidates&sport=baseball" \
-H "Authorization: Bearer sk_live_..."
위의 카탈로그 파라미터는 이 뷰에도 동일하게 적용됩니다: status, sort, include는 동일하게 동작합니다.
행은 카탈로그 뷰와 동일한 표준 형태로 반환됩니다. 후보(candidate)가 가격이 매겨진 매치를 가지고 있으면 outcomes와 coverage가 arb 뷰와 동일하게 채워지며, 아직 가격 정보가 없으면 가격은 null로 남습니다.
Includes
include는 각 market 객체에 옵트인 블록을 추가합니다. 포함되지 않은 블록은 키 자체가 아예 없는 것이며, null이 아닙니다. 필요한 것만 요청하세요.
| Value | Adds | Meaning |
|---|---|---|
ids | venues, confidence | 거래소 식별자(kalshi.event_ticker, polymarket.condition_id)와 매칭 엔진의 엔티티/결과 신뢰도(0에서 1). |
depth | outcomes[].kalshi_book, outcomes[].polymarket_book | 결과별 북 상세 정보(ask, 뎁스(호가 잔량), 티커/토큰 id)를 스칼라 ask 가격과 함께 제공합니다. 부가 필드이며 outcomes[].kalshi나 .polymarket을 대체하지 않습니다. |
settlement | settlement | parity(bool), risks[], 거래소 텍스트, 그리고 정산 완료 시 verdict. |
GET /v2/pairs/{market_id}
id로 조회하는 하나의 표준 market 객체이며, 기본적으로 include=ids,depth,settlement가 적용됩니다. 이는 상세 뷰입니다. dino_<uuid>(/v2/markets의 id 필드) 또는 순수 UUID를 받습니다.
| Param | In | Type | Meaning |
|---|---|---|---|
market_id | path | string | /v2/markets의 마켓 id로, dino_<uuid> 또는 순수 UUID입니다. |
예: MLB arb (정산 정합성 확정)
curl -s "https://api.dino.markets/v2/pairs/dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2" \
-H "Authorization: Bearer sk_live_..."
{
"id": "dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2",
"slug": null,
"sport": "baseball",
"league": "MLB",
"title": "Nyy vs Bos",
"home": null,
"away": null,
"start_time": "2026-07-01T23:05:00+00:00",
"status": "open",
"match": "high_confidence",
"signal": "arb",
"spread_pts": 2.0,
"potential_arb_pct": 1.83,
"coverage": { "kalshi": true, "polymarket": true },
"outcomes": [
{ "key": "NYY", "name": "NYY", "kalshi": 0.52, "polymarket": 0.54, "cheaper": "kalshi",
"kalshi_book": { "ask": 0.52, "bid": null, "depth": 3100, "ticker": "KXMLBGAME-26JUL01NYYBOS-NYY" },
"polymarket_book": { "ask": 0.54, "bid": null, "depth": 2900, "token_id": "0x1a2b..." } },
{ "key": "BOS", "name": "BOS", "kalshi": 0.50, "polymarket": 0.46, "cheaper": "polymarket",
"kalshi_book": { "ask": 0.50, "bid": null, "depth": 2400, "ticker": "KXMLBGAME-26JUL01NYYBOS-BOS" },
"polymarket_book": { "ask": 0.46, "bid": null, "depth": 3300, "token_id": "0x9f8e..." } }
],
"result": null,
"updated_at": "2026-07-01T22:55:00+00:00",
"priced_at": "2026-07-01T22:55:00+00:00",
"venues": {
"kalshi": { "event_ticker": "KXMLBGAME-26JUL01NYYBOS" },
"polymarket": { "condition_id": "0x1a2b...", "slug": null }
},
"confidence": { "entity": 1.0, "resolution": 1.0 },
"settlement": { "parity": true, "risks": [], "kalshi_text": null, "poly_text": null, "verdict": null },
"links": { "kalshi": "https://kalshi.com/markets/KXMLBGAME-26JUL01NYYBOS", "polymarket": null }
}
예: KBO spread (크로스벤뉴 정산 리스크)
curl -s "https://api.dino.markets/v2/pairs/dino_5c2b9f7e-d4a1-4e6c-8b3f-a9c2e5f1d8a4" \
-H "Authorization: Bearer sk_live_..."
{
"id": "dino_5c2b9f7e-d4a1-4e6c-8b3f-a9c2e5f1d8a4",
"sport": "baseball",
"league": "KBO",
"title": "Kia vs Kiw",
"start_time": "2026-07-15T10:00:00+00:00",
"status": "open",
"signal": "spread",
"spread_pts": null,
"potential_arb_pct": null,
"coverage": { "kalshi": false, "polymarket": false },
"outcomes": [],
"settlement": {
"parity": false,
"risks": [
{
"event": "tie",
"severity": "high",
"note": "This league can tie, but one venue's market does not carry an explicit tie->50/50 clause. A held two-leg position may not return $1 on a tie. Verify before trading."
}
],
"kalshi_text": null,
"poly_text": null,
"verdict": null
},
"links": { "kalshi": "https://kalshi.com/markets/KXMLBGAME-26JUL15KIWKIA", "polymarket": null }
}
페어에 대해 high 또는 critical 심각도의 리스크가 있으면, 가격이 맞아떨어지더라도 signal은 강제로 spread가 됩니다. 이 검사는 signal을 결정하는 네 가지 게이트 중 하나이며, 페어의 전체 내부 리스크 셋을 대상으로 실행됩니다. 응답되는 risks[]에는 취소(cancel_*) 이벤트가 서버 측에서 필터링되어 절대 포함되지 않습니다. arb이든 spread이든 어떤 행에 대해 실행하기 전에는 settlement.risks를 확인하세요. id가 존재하지 않으면 응답은 404이며 { "error": "pair not found" }가 반환됩니다.
GET /v2/leagues
현재 시즌 중인 스포츠와 리그를, 활성 피드에서 도출한 스포츠별 라이프사이클 카운트와 함께 제공합니다.
leagues 배열은 해당 스포츠에서 확인된 null이 아닌 리그를 나열합니다. count는 리그가 null인 행을 포함해 매칭된 모든 경기 수입니다. open은 오늘 기준 count와 동일하며, live와 settled는 항상 0입니다. 경기 중 상태와 취소된 행의 카운트가 아직 이 엔드포인트에 연결되지 않았기 때문입니다.
curl -s "https://api.dino.markets/v2/leagues" \
-H "Authorization: Bearer sk_live_..."
{
"sports": [
{ "sport": "baseball", "leagues": ["KBO", "MLB", "NPB"], "count": 54, "open": 54, "live": 0, "settled": 0 },
{ "sport": "soccer", "leagues": ["World Cup"], "count": 15, "open": 15, "live": 0, "settled": 0 },
{ "sport": "tennis", "leagues": ["ITFW"], "count": 165, "open": 165, "live": 0, "settled": 0 }
]
}
GET /v2/coverage
거래소 커버리지 센서스입니다: Kalshi에 나열된 모든 헤드투헤드 리그를, 매칭 피드에 연결되었는지 여부와 집계 상태와 함께 보여줍니다. 매칭 엔진은 대략 매시간마다 이를 다시 빌드합니다. 상태 값: aggregated(실시간으로 매칭됨), pending(연결되었지만 상대편 또는 확인 절차 대기 중), quiet(연결되었지만 열려 있는 것이 없음), kalshi_only(그곳에 나열되어 있으나 매칭이 연결되지 않음), dormant(시리즈는 존재하나 열려 있는 것이 없음). poly_unpaired는 스포츠 레벨의 Polymarket 잉여분을 담고 있습니다: 해당 회차에서 Kalshi 매칭이 없었던 파싱된 경기 일정입니다.
curl -s "https://api.dino.markets/v2/coverage" \
-H "Authorization: Bearer sk_live_..."
{
"as_of": "2026-07-04T03:30:21+00:00",
"counts": { "aggregated": 20, "pending": 7, "quiet": 43, "kalshi_only": 13, "dormant": 139 },
"series": [
{ "series_ticker": "KXFIBAGAME", "league": "FIBA", "sport": "basketball", "wired": true,
"status": "aggregated", "kalshi_open_events": 49, "paired": 46, "sport_poly_fixtures": 88,
"last_pass_at": "2026-07-04T03:30:21+00:00" }
],
"poly_unpaired": [ { "sport": "soccer", "fixtures": 258, "paired": 7, "unpaired": 251 } ]
}
이 센서스는 API에서만 제공되며, 내부 커버리지 뷰도 동일한 데이터를 기반으로 합니다.
POST /v1/stream/token
실시간 스트림을 위한 단기 티켓을 발급받습니다. 이 엔드포인트는 /v2로 이전되지 않았으며 /v1/stream/token을 그대로 사용합니다. Basic 또는 Pro가 필요하며, Free 키는 402를 받습니다. 전체 연결 플로우는 WebSocket 스트림 페이지를 참고하세요.
{
"ticket": "eyJhbGciOi...",
"ws_url": "wss://stream.dino.markets/connection/websocket",
"expires_in": 120,
"allowed": { "channels": ["markets:all", "status"], "max_conns": 3 }
}
POST /v2/report-bad-arb
잘못된 것으로 보이는 기회를 신고합니다. 검토 큐에 리포트가 접수됩니다. 아래 필드 중 최소 하나는 포함해야 합니다. opp_id에는 /v2/markets의 마켓 id 값을 전달하세요.
| Field | Type | Meaning |
|---|---|---|
opp_id | string | 마켓의 id 값 (dino_<uuid>). |
reason | string | 짧은 사유 또는 메모. |
sport | string | opp_id가 없을 때의 스포츠. |
market | string | 마켓 또는 경기. |
detail | string | 추가 상세 정보. |
curl -s -X POST "https://api.dino.markets/v2/report-bad-arb" \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "opp_id": "dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2", "reason": "legs do not settle the same way" }'
성공한 리포트는 202와 { "status": "received" }를 반환합니다. opp_id, reason, sport, market, detail 중 최소 하나를 전송하세요. 빈 본문을 보내면 400이 반환됩니다.