Place orders
curl --request POST \
--url https://api.4casters.io/session/v3/place \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"orders": [
{
"gameID": "<string>",
"side": "<string>",
"odds": 123,
"bet": 123,
"market": "<string>",
"number": 123,
"expirationMinutes": 123,
"userReference": "<string>"
}
]
}
'import requests
url = "https://api.4casters.io/session/v3/place"
payload = { "orders": [
{
"gameID": "<string>",
"side": "<string>",
"odds": 123,
"bet": 123,
"market": "<string>",
"number": 123,
"expirationMinutes": 123,
"userReference": "<string>"
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
orders: [
{
gameID: '<string>',
side: '<string>',
odds: 123,
bet: 123,
market: '<string>',
number: 123,
expirationMinutes: 123,
userReference: '<string>'
}
]
})
};
fetch('https://api.4casters.io/session/v3/place', 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.4casters.io/session/v3/place",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'orders' => [
[
'gameID' => '<string>',
'side' => '<string>',
'odds' => 123,
'bet' => 123,
'market' => '<string>',
'number' => 123,
'expirationMinutes' => 123,
'userReference' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.4casters.io/session/v3/place"
payload := strings.NewReader("{\n \"orders\": [\n {\n \"gameID\": \"<string>\",\n \"side\": \"<string>\",\n \"odds\": 123,\n \"bet\": 123,\n \"market\": \"<string>\",\n \"number\": 123,\n \"expirationMinutes\": 123,\n \"userReference\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.4casters.io/session/v3/place")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"orders\": [\n {\n \"gameID\": \"<string>\",\n \"side\": \"<string>\",\n \"odds\": 123,\n \"bet\": 123,\n \"market\": \"<string>\",\n \"number\": 123,\n \"expirationMinutes\": 123,\n \"userReference\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.4casters.io/session/v3/place")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"orders\": [\n {\n \"gameID\": \"<string>\",\n \"side\": \"<string>\",\n \"odds\": 123,\n \"bet\": 123,\n \"market\": \"<string>\",\n \"number\": 123,\n \"expirationMinutes\": 123,\n \"userReference\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"data": {
"createdSessions": [
{
"matched": [
{
"amount": 123,
"odds": 123,
"number": 123,
"type": "moneyline",
"side": "<string>",
"market": "<string>",
"orderID": "<string>",
"txID": "<string>",
"wagerRequestID": "<string>",
"userReference": "<string>",
"risk": 123,
"win": 123,
"winWithoutCommission": 123
}
],
"unmatched": {
"orderID": "<string>",
"wagerRequestID": "<string>",
"offered": 123,
"odds": 123,
"type": "moneyline",
"side": "<string>",
"market": "<string>",
"number": 123,
"userReference": "<string>"
}
}
]
}
}訂單
下單
向交易所提交一筆或多筆訂單
POST
/
session
/
v3
/
place
Place orders
curl --request POST \
--url https://api.4casters.io/session/v3/place \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"orders": [
{
"gameID": "<string>",
"side": "<string>",
"odds": 123,
"bet": 123,
"market": "<string>",
"number": 123,
"expirationMinutes": 123,
"userReference": "<string>"
}
]
}
'import requests
url = "https://api.4casters.io/session/v3/place"
payload = { "orders": [
{
"gameID": "<string>",
"side": "<string>",
"odds": 123,
"bet": 123,
"market": "<string>",
"number": 123,
"expirationMinutes": 123,
"userReference": "<string>"
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
orders: [
{
gameID: '<string>',
side: '<string>',
odds: 123,
bet: 123,
market: '<string>',
number: 123,
expirationMinutes: 123,
userReference: '<string>'
}
]
})
};
fetch('https://api.4casters.io/session/v3/place', 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.4casters.io/session/v3/place",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'orders' => [
[
'gameID' => '<string>',
'side' => '<string>',
'odds' => 123,
'bet' => 123,
'market' => '<string>',
'number' => 123,
'expirationMinutes' => 123,
'userReference' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.4casters.io/session/v3/place"
payload := strings.NewReader("{\n \"orders\": [\n {\n \"gameID\": \"<string>\",\n \"side\": \"<string>\",\n \"odds\": 123,\n \"bet\": 123,\n \"market\": \"<string>\",\n \"number\": 123,\n \"expirationMinutes\": 123,\n \"userReference\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.4casters.io/session/v3/place")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"orders\": [\n {\n \"gameID\": \"<string>\",\n \"side\": \"<string>\",\n \"odds\": 123,\n \"bet\": 123,\n \"market\": \"<string>\",\n \"number\": 123,\n \"expirationMinutes\": 123,\n \"userReference\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.4casters.io/session/v3/place")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"orders\": [\n {\n \"gameID\": \"<string>\",\n \"side\": \"<string>\",\n \"odds\": 123,\n \"bet\": 123,\n \"market\": \"<string>\",\n \"number\": 123,\n \"expirationMinutes\": 123,\n \"userReference\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"data": {
"createdSessions": [
{
"matched": [
{
"amount": 123,
"odds": 123,
"number": 123,
"type": "moneyline",
"side": "<string>",
"market": "<string>",
"orderID": "<string>",
"txID": "<string>",
"wagerRequestID": "<string>",
"userReference": "<string>",
"risk": 123,
"win": 123,
"winWithoutCommission": 123
}
],
"unmatched": {
"orderID": "<string>",
"wagerRequestID": "<string>",
"offered": 123,
"odds": 123,
"type": "moneyline",
"side": "<string>",
"market": "<string>",
"number": 123,
"userReference": "<string>"
}
}
]
}
}提交一筆或多筆訂單。該介面接受批次請求——每筆訂單的結果在
預設訂單型別。作為 taker(收取 taker 佣金)按你的價格或更優價格吃掉任何可匹配流動性,未成交部分作為 maker 留在訂單簿上。
建立一筆掛單。如果下單時該訂單會與已有流動性成交,伺服器會拒絕它(
行為類似
立即作為 taker 按你的價格或更優價格吃掉當前可成交數量,並取消剩餘部分。
全成或全撤。你的整筆
若完全沒有可匹配流動性,同一筆訂單會改為以 場景 6 — 訂單簿會成交時的
場景 7 —
若要投注主隊不勝,傳送
data.createdSessions 中回傳,並與輸入的 orders 陣列按位置一一對應。每條結果要麼是成功的 { matched, unmatched },要麼是失敗的 { error, errorType }。
請求
POST /session/v3/place
curl -X POST https://api.4casters.io/session/v3/place \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"orders": [
{
"gameID": "688c0516fbc14da0c202d426",
"type": "moneyline",
"side": "5c12bc1ce0daba000f47ba8b",
"odds": -175,
"bet": 100,
"orderType": "post",
"userReference": "docs-example"
}
]
}'
{
"orders": [
{
"gameID": "4CASTER_GAME_ID",
"type": "moneyline | spread | total | moneyline1x2",
"side": "PARTICIPANT_ID | over | under | yes | no",
"market": "PARTICIPANT_ID | draw",
"odds": -110,
"bet": 100,
"number": 3.5,
"orderType": "limit | post | postArb | fillAndKill | fillOrKill",
"expirationMinutes": 10,
"userReference": "OPTIONAL_CLIENT_SIDE_IDENTIFIER"
}
]
}
訂單欄位
訂單型別
limit
預設訂單型別。作為 taker(收取 taker 佣金)按你的價格或更優價格吃掉任何可匹配流動性,未成交部分作為 maker 留在訂單簿上。limit 訂單最終可以是全部成交、全部掛單,或部分成交、剩餘掛單。
post
建立一筆掛單。如果下單時該訂單會與已有流動性成交,伺服器會拒絕它(rejected_order_type_rules,例如 "post order cannot have matches")。
postArb
行為類似 post,但即使訂單會成交也可以掛單,前提是你的美式賠率與將被成交的掛單賠率相差不超過 1%。若價差超過 1%,下單會被拒絕。
示例:
- 最優報價 +100,你掛 +100 —
post會被拒絕(會成交);postArb允許。 - 最優報價 +200 — 以 +190 提交的
postArb會被拒絕(距離 +200 過遠)。約 +198 處於相對 +200 的 1% 區間邊緣。 - 最優訂單 −200 — 負向一側區間大約延伸到 −202(同一條 1% 規則)。
- 訂單簿上有 +100 時,對側的 1% 容差參考限價為 −101。
postArb 可避免普通成交中的 taker 手續費 — 走該流程時,不會像作為 taker 主動吃掉掛單流動性那樣被收取 taker 手續費。
以 postArb 下的訂單,會在其使用者推送和行情推送更新中帶有 isPostArb: true;其他訂單型別會省略該欄位。下單回應本身不包含該標誌。
fillAndKill
立即作為 taker 按你的價格或更優價格吃掉當前可成交數量,並取消剩餘部分。fillAndKill 永遠不會留在訂單簿上。
部分成交也是成功結果:如果你傳送 bet: 1000 而可匹配流動性只有 400,則成交400,剩餘 $600 被取消——回應為成功,而非錯誤。僅當完全沒有可匹配流動性時,訂單才會被拒絕(rejected_order_type_rules,"fill and kill has no matches")。參見下方示例中的 Fill And Kill 場景。
fillOrKill
全成或全撤。你的整筆 bet 必須立即按你的價格或更優價格成交,否則整筆訂單被拒絕——包括途中已發生的成交,都會復原。fillOrKill 永遠不會留在訂單簿上,也不會留下部分倉位。
適用於部分倉位還不如沒有倉位的情形,例如該訂單是一筆必須整筆執行的對沖腿。
拒絕時 errorType: rejected_order_type_rules,並帶有以下兩條訊息之一,用於區分「完全沒有流動性」和「流動性不足」:
"fill or kill has no matches"— 你的價格上沒有任何可匹配流動性。"fill or kill matched but not fully"— 有部分流動性成交,但未達到你的全部數量。這些成交已被復原。
剩餘量 **10及以下∗∗視為已完成:一筆1,000 的
fillOrKill 若成交 992即算成功,因為未能成交的8 低於訂單簿上可掛單的最小數量。剩餘量大於 $10 則會拒絕該訂單。回應
array
每筆訂單的結果,與輸入的
orders 陣列按位置對應。每條要麼是成功下單,要麼是錯誤。顯示 下單成功
顯示 下單成功
array
示例
場景 1 — 訂單全部成交、無剩餘掛單
三筆訂單均立即與可用流動性成交。{
"orders": [
{ "gameID": "688c0516fbc14da0c202d426", "type": "moneyline", "side": "5c12bc1ce0daba000f47ba8b", "odds": -175, "bet": 100, "orderType": "post" },
{ "gameID": "688c0516fbc14da0c202d426", "type": "spread", "side": "5c12bc1ce0daba000f47ba8b", "odds": -110, "bet": 100, "orderType": "post", "number": 3.5 },
{ "gameID": "688c0516fbc14da0c202d426", "type": "total", "side": "over", "odds": -104, "bet": 100, "orderType": "post", "number": 50 }
]
}
{
"data": {
"createdSessions": [
{
"matched": [
{
"amount": 50, "odds": -175, "type": "moneyline",
"side": "5d48bd5198366d41ec7238da",
"orderID": "68d42f82cfebf0b249a2c26e",
"txID": "68d42f83cfebf0b249a2c279",
"wagerRequestID": "68d42f83cfebf0b249a2c278",
"risk": 50.286, "win": 28.286, "winWithoutCommission": 28.571
}
],
"unmatched": {}
},
{ "matched": [/* spread fill */], "unmatched": {} },
{ "matched": [/* total fill */], "unmatched": {} }
]
}
}
場景 2 — 限價單有剩餘掛單
一筆數量為 300 的限價單部分成交,剩餘部分掛在訂單簿上。{
"data": {
"createdSessions": [
{
"matched": [
{
"amount": 185, "odds": -185, "type": "moneyline",
"side": "5d48bd5198366d41ec7238da",
"orderID": "68d43574cfebf0b249a2c28d",
"txID": "68d43574cfebf0b249a2c290",
"wagerRequestID": "68d43574cfebf0b249a2c28f",
"risk": 186, "win": 99, "winWithoutCommission": 100
}
],
"unmatched": {
"orderID": "68d43575cfebf0b249a2c292",
"wagerRequestID": "68d43574cfebf0b249a2c28f",
"offered": 115, "odds": -185, "type": "moneyline",
"side": "5d48bd5198366d41ec7238da", "number": null
}
}
]
}
}
場景 3 — Fill and Kill,全部成交
{
"data": {
"createdSessions": [
{
"matched": [
{
"amount": 100, "odds": -186, "type": "moneyline",
"side": "5d48bd5198366d41ec7238da",
"orderID": "68d4368acfebf0b249a2c298",
"txID": "68d436bacfebf0b249a2c29b",
"wagerRequestID": "68d436bacfebf0b249a2c29a",
"userReference": "docs-fillandkill-match",
"risk": 100.538, "win": 53.226, "winWithoutCommission": 53.763
}
],
"unmatched": {}
}
]
}
}
場景 4 — Fill and Kill,無成交
沒有成交的fillAndKill 會回傳單筆訂單錯誤。
{
"data": {
"createdSessions": [
{
"error": "fill and kill has no matches",
"errorType": "rejected_order_type_rules"
}
]
}
}
場景 5 — Fill or Kill,流動性不足
fillOrKill 是 fillAndKill 的全成或全撤對應物。一筆 1,000的‘fillAndKill‘面對400 流動性會成交 400並取消剩餘;而‘fillOrKill‘會拒絕整筆訂單並復原那400——你不會留下部分倉位。
{
"orders": [
{
"gameID": "688c0516fbc14da0c202d426",
"type": "moneyline",
"side": "5c12bc1ce0daba000f47ba8b",
"odds": -110,
"bet": 1000,
"orderType": "fillOrKill",
"userReference": "docs-fillorkill-partial"
}
]
}
{
"data": {
"createdSessions": [
{
"error": "fill or kill matched but not fully",
"errorType": "rejected_order_type_rules"
}
]
}
}
"fill or kill has no matches" 被拒絕。
場景 6 — 訂單簿會成交時的 postArb 與 post
post 會拒絕將與掛單流動性成交的訂單(例如最優報價 +100,你嘗試掛 +100)。當你的賠率與將被成交訂單相差不超過 1% 時,postArb 允許這種情況——因此同樣掛 +100 可以作為 postArb 成功,並避免普通成交中需支付的 taker 手續費。若你的價格距離掛單報價過遠(例如最優報價 +200 但你傳送 +190),postArb 會被拒絕。
{
"orders": [
{
"gameID": "688c0516fbc14da0c202d426",
"type": "moneyline",
"side": "5c12bc1ce0daba000f47ba8b",
"odds": 100,
"bet": 50,
"orderType": "postArb",
"userReference": "docs-postarb-same-line-as-offer"
}
]
}
場景 7 — moneyline1x2(足球三項盤)
moneyline1x2 是足球三項盤——主勝 / 客勝 / 平局——以 market 所指定結果的是/否投注形式下單。以下為平局 yes,賠率 +250。
{
"orders": [
{
"gameID": "65f0c3...",
"type": "moneyline1x2",
"side": "yes",
"market": "draw",
"odds": 250,
"bet": 50,
"orderType": "post",
"userReference": "docs-ml1x2-yes-draw"
}
]
}
side: "no",並將 market 設為主隊參賽者 id。
滾球延遲
在標記為滾球的賽事上下單時,遵循以下規則:- 若訂單不與任何已有流動性成交,則立即掛單。
- 若訂單會與已有流動性成交,則在執行前會有一段延遲。
- 不同聯賽的滾球延遲不同:
- NFL、UFCMMA、NCAAF — 3 秒。
- NCAAB、NBA — 5 秒。
- ATP、WTA — 8 秒。
- 預設 — 10 秒。
- 延遲結束後,訂單嘗試執行:
- 若賠率變得更優,則立即成交。
- 若賠率變差,則不成交。
單筆訂單錯誤
多筆下單、部分出錯的示例:{
"data": {
"createdSessions": [
{ "matched": [/* successful fill */], "unmatched": {} },
{ "error": "game not found: invalid gameID", "errorType": "validation_error" },
{ "error": "Insufficient balance.", "errorType": "rejected_liability" },
{ "error": "post order cannot have matches", "errorType": "rejected_order_type_rules" },
{ "error": "failed to interact with database","errorType": "system_error" }
]
}
}
授權
Pass your auth token in the Authorization header. The Bearer prefix is optional; the server also accepts a signed auth cookie or a token field in the request body.
主體
application/json
Show child attributes
Show child attributes
回應
Per-order results (positional with input).
Show child attributes
Show child attributes
這個頁面有幫助嗎?