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>"
}
}
]
}
}Órdenes
Colocar órdenes
Envía una o más órdenes al exchange
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>"
}
}
]
}
}Envía una o más órdenes. El endpoint acepta un lote: los resultados por orden vuelven en
El tipo de orden por defecto. Se ejecuta contra cualquier liquidez coincidente a tu precio o mejor como taker (con comisión de taker) y deja el resto en el libro como maker. Una orden
Crea una oferta en espera. Si la orden se cruzaría con liquidez existente al colocarla, el servidor la rechaza (
Se comporta como
Ejecuta de inmediato como taker contra el tamaño disponible a tu precio o mejor y cancela el resto. Un
Todo o nada. Tu
Sin liquidez cruzable en absoluto, la misma orden se rechaza con Escenario 6 —
Escenario 7 —
Para apostar a que el equipo local no gana, envía
data.createdSessions, de forma posicional respecto a tu array orders de entrada. Cada entrada es un resultado correcto { matched, unmatched } o un fallo { error, errorType }.
Solicitud
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"
}
]
}
Campos de la orden
Tipos de orden
limit
El tipo de orden por defecto. Se ejecuta contra cualquier liquidez coincidente a tu precio o mejor como taker (con comisión de taker) y deja el resto en el libro como maker. Una orden limit puede quedar totalmente cruzada, totalmente en espera, o parcialmente cruzada con el resto en espera.
post
Crea una oferta en espera. Si la orden se cruzaría con liquidez existente al colocarla, el servidor la rechaza (rejected_order_type_rules, p. ej. "post order cannot have matches").
postArb
Se comporta como post, pero puedes colocar incluso cuando la orden se cruzaría, solo si tus cuotas americanas están dentro del 1% de las cuotas de la orden en espera con la que te cruzarías. Si la diferencia de precio es mayor al 1%, la colocación se rechaza.
Ejemplos:
- Mejor oferta +100 y colocas +100 —
postse rechaza (se cruzaría);postArbestá permitido. - Mejor oferta +200 —
postArba +190 se rechaza (demasiado lejos de +200). Alrededor de +198 está en el borde de la banda del 1% frente a +200. - Mejor orden −200 — la banda se extiende hasta alrededor de −202 en el lado negativo (la misma regla del 1%).
- Con +100 en el libro, −101 es el límite de referencia en el otro lado para la tolerancia del 1%.
postArb evita las comisiones de taker de un cruce normal: no se te cobran comisiones de taker en este flujo como sí ocurriría si tomases liquidez en espera como taker.
Las órdenes colocadas como postArb se marcan con isPostArb: true en sus actualizaciones del feed de usuario y del feed de precios; el campo se omite en los demás tipos de orden. La propia respuesta de colocación no incluye el indicador.
fillAndKill
Ejecuta de inmediato como taker contra el tamaño disponible a tu precio o mejor y cancela el resto. Un fillAndKill nunca queda en el libro.
Un cruce parcial es un resultado correcto: si envías bet: 1000 y solo hay 400deliquidezcruzable,setecruzapor400 y se cancela el resto de $600: la respuesta es un éxito, no un error. La orden solo se rechaza (rejected_order_type_rules, "fill and kill has no matches") cuando no hay liquidez cruzable en absoluto. Consulta los escenarios de Fill And Kill en los ejemplos más abajo.
fillOrKill
Todo o nada. Tu bet entero debe cruzar de inmediato a tu precio o mejor; de lo contrario se rechaza toda la orden, incluidos los cruces que hubiera hecho por el camino, que se revierten. Un fillOrKill nunca queda en el libro y nunca te deja parcialmente cruzado.
Úsalo cuando una posición parcial sea peor que ninguna posición, por ejemplo cuando la orden es una pata de una cobertura que solo puedes ejecutar por completo.
Los rechazos llevan errorType: rejected_order_type_rules con uno de dos mensajes, que distinguen «no había nada» de «no había suficiente»:
"fill or kill has no matches"— no hay liquidez cruzable a tu precio."fill or kill matched but not fully"— parte de la liquidez cruzó, pero no tu tamaño completo. Esos cruces se revirtieron.
Un residual de **10omenos∗∗cuentacomocompleto:un‘fillOrKill‘de1,000 que cruza 992tieneeˊxito,porquelos8 que no se pudieron cruzar están por debajo del tamaño mínimo que podría quedar en el libro. Un residual mayor de $10 rechaza la orden.
Respuesta
array
Resultados por orden, en el mismo orden posicional que el array
orders de entrada. Cada entrada es una colocación correcta o un error.Mostrar Colocación correcta
Mostrar Colocación correcta
array
Cruces producidos por esta orden. Vacío cuando la orden no cruzó.
Mostrar MatchedFill
Mostrar MatchedFill
number
Importe apostado de la porción cruzada.
integer
Cuotas americanas del cruce.
number
Spread o total del cruce (
null en moneylines).string
Tipo de mercado.
string
Lado de la orden.
string
Presente en
moneyline1x2.string
Id de la orden de la oferta cruzada del otro lado.
string
Id de transacción del cruce.
string
Id generado por el servidor que agrupa cada cruce u oferta derivado de esta orden de entrada.
string
number
number
Importe a ganar, neto de la comisión de taker.
number
Importe a ganar antes de comisión.
Ejemplos
Escenario 1 — Órdenes que cruzan sin liquidez restante
Tres órdenes que cruzan al instante con la liquidez disponible.{
"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": {} }
]
}
}
Escenario 2 — Orden limit con liquidez restante
Una orden limit de 300 que cruza parcialmente y deja el resto en espera.{
"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
}
}
]
}
}
Escenario 3 — Fill and Kill, cruce completo
{
"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": {}
}
]
}
}
Escenario 4 — Fill and Kill, sin cruce
UnfillAndKill sin cruce devuelve un error por orden.
{
"data": {
"createdSessions": [
{
"error": "fill and kill has no matches",
"errorType": "rejected_order_type_rules"
}
]
}
}
Escenario 5 — Fill or Kill, liquidez insuficiente
fillOrKill es el homólogo de todo o nada de fillAndKill. Donde un fillAndKill de 1,000contra400 de liquidez cruza 400ycancelaelresto,un‘fillOrKill‘rechazatodalaordenyrevierteesos400: nunca te deja parcialmente cruzado.
{
"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" en su lugar.
Escenario 6 — postArb frente a post cuando el libro cruzaría
post rechaza una orden que cruzaría liquidez en espera (por ejemplo, mejor oferta +100 e intentas colocar +100). postArb permite esa situación cuando tus cuotas están dentro del 1% de la orden con la que te cruzarías, de modo que la misma colocación a +100 puede tener éxito como postArb y evitas las comisiones de taker que pagarías en un cruce normal. Si tu precio está demasiado lejos de la cotización en espera (p. ej. mejor oferta +200 pero envías +190), postArb se rechaza.
{
"orders": [
{
"gameID": "688c0516fbc14da0c202d426",
"type": "moneyline",
"side": "5c12bc1ce0daba000f47ba8b",
"odds": 100,
"bet": 50,
"orderType": "postArb",
"userReference": "docs-postarb-same-line-as-offer"
}
]
}
Escenario 7 — moneyline1x2 (fútbol a tres vías)
moneyline1x2 es el mercado de fútbol a tres vías — local / visitante / empate — colocado como una apuesta yes/no sobre el resultado indicado por market. Abajo, yes sobre el empate a +250.
{
"orders": [
{
"gameID": "65f0c3...",
"type": "moneyline1x2",
"side": "yes",
"market": "draw",
"odds": 250,
"bet": 50,
"orderType": "post",
"userReference": "docs-ml1x2-yes-draw"
}
]
}
side: "no" y pon market al id del participante local.
Retraso en vivo
Las órdenes colocadas en un juego marcado como live siguen estas reglas:- Si la orden no cruza ninguna liquidez existente, se coloca de inmediato.
- Si la orden sí cruza liquidez existente, incurre en un retraso antes de la ejecución.
- Distintas ligas tienen distintos periodos de retraso en vivo:
- NFL, UFCMMA, NCAAF — 3 segundos.
- NCAAB, NBA — 5 segundos.
- ATP, WTA — 8 segundos.
- Por defecto — 10 segundos.
- Tras el retraso, la orden intenta ejecutarse:
- Si las cuotas mejoran, cruza al instante.
- Si las cuotas empeoran, no cruza.
Errores por orden
Ejemplo con varias colocaciones, algunas con error:{
"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" }
]
}
}
Autorizaciones
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.
Cuerpo
application/json
Show child attributes
Show child attributes
Respuesta
Per-order results (positional with input).
Show child attributes
Show child attributes
¿Esta página le ayudó?