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
此页面对您有帮助吗?