curl --request POST \
--url https://perps-api.vooi.io/bots \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"exchanges": [],
"leverage": 2,
"maxRoundTripCostBps": 12,
"notionalUsd": 500,
"categories": [],
"maxHoldHours": 24
}
'import requests
url = "https://perps-api.vooi.io/bots"
payload = {
"exchanges": [],
"leverage": 2,
"maxRoundTripCostBps": 12,
"notionalUsd": 500,
"categories": [],
"maxHoldHours": 24
}
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({
exchanges: [],
leverage: 2,
maxRoundTripCostBps: 12,
notionalUsd: 500,
categories: [],
maxHoldHours: 24
})
};
fetch('https://perps-api.vooi.io/bots', 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://perps-api.vooi.io/bots",
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([
'exchanges' => [
],
'leverage' => 2,
'maxRoundTripCostBps' => 12,
'notionalUsd' => 500,
'categories' => [
],
'maxHoldHours' => 24
]),
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://perps-api.vooi.io/bots"
payload := strings.NewReader("{\n \"exchanges\": [],\n \"leverage\": 2,\n \"maxRoundTripCostBps\": 12,\n \"notionalUsd\": 500,\n \"categories\": [],\n \"maxHoldHours\": 24\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://perps-api.vooi.io/bots")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"exchanges\": [],\n \"leverage\": 2,\n \"maxRoundTripCostBps\": 12,\n \"notionalUsd\": 500,\n \"categories\": [],\n \"maxHoldHours\": 24\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://perps-api.vooi.io/bots")
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 \"exchanges\": [],\n \"leverage\": 2,\n \"maxRoundTripCostBps\": 12,\n \"notionalUsd\": 500,\n \"categories\": [],\n \"maxHoldHours\": 24\n}"
response = http.request(request)
puts response.read_body{
"config": {
"exchanges": [
"aster"
],
"leverage": 2,
"maxRoundTripCostBps": 12,
"notionalUsd": 500,
"categories": [
"commodities"
],
"maxHoldHours": 24,
"requiredExchange": "aster"
},
"createdAt": "2023-11-07T05:31:56Z",
"id": "12",
"lastError": "<string>",
"state": {
"asset": "<string>",
"baseQuantity": "<string>",
"limitLeg": "long",
"long": {
"baseSymbol": "<string>",
"exchange": "aster",
"marketId": "<string>",
"quoteSymbol": "<string>"
},
"openedAt": "2023-11-07T05:31:56Z",
"orderId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"phase": "closing",
"short": {
"baseSymbol": "<string>",
"exchange": "aster",
"marketId": "<string>",
"quoteSymbol": "<string>"
},
"submittedAt": "2023-11-07T05:31:56Z",
"closeAttempts": 0
},
"status": "halted",
"updatedAt": "2023-11-07T05:31:56Z"
}Create a bot
Creates a stopped funding bot. Once started it scans the arbitrage scanner for cross-venue pairs with a positive funding spread whose round trip costs at most maxRoundTripCostBps, opens the one expected to pay the most over maxHoldHours, holds until the spread turns clearly negative or maxHoldHours pass, closes, and scans again.
curl --request POST \
--url https://perps-api.vooi.io/bots \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"exchanges": [],
"leverage": 2,
"maxRoundTripCostBps": 12,
"notionalUsd": 500,
"categories": [],
"maxHoldHours": 24
}
'import requests
url = "https://perps-api.vooi.io/bots"
payload = {
"exchanges": [],
"leverage": 2,
"maxRoundTripCostBps": 12,
"notionalUsd": 500,
"categories": [],
"maxHoldHours": 24
}
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({
exchanges: [],
leverage: 2,
maxRoundTripCostBps: 12,
notionalUsd: 500,
categories: [],
maxHoldHours: 24
})
};
fetch('https://perps-api.vooi.io/bots', 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://perps-api.vooi.io/bots",
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([
'exchanges' => [
],
'leverage' => 2,
'maxRoundTripCostBps' => 12,
'notionalUsd' => 500,
'categories' => [
],
'maxHoldHours' => 24
]),
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://perps-api.vooi.io/bots"
payload := strings.NewReader("{\n \"exchanges\": [],\n \"leverage\": 2,\n \"maxRoundTripCostBps\": 12,\n \"notionalUsd\": 500,\n \"categories\": [],\n \"maxHoldHours\": 24\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://perps-api.vooi.io/bots")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"exchanges\": [],\n \"leverage\": 2,\n \"maxRoundTripCostBps\": 12,\n \"notionalUsd\": 500,\n \"categories\": [],\n \"maxHoldHours\": 24\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://perps-api.vooi.io/bots")
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 \"exchanges\": [],\n \"leverage\": 2,\n \"maxRoundTripCostBps\": 12,\n \"notionalUsd\": 500,\n \"categories\": [],\n \"maxHoldHours\": 24\n}"
response = http.request(request)
puts response.read_body{
"config": {
"exchanges": [
"aster"
],
"leverage": 2,
"maxRoundTripCostBps": 12,
"notionalUsd": 500,
"categories": [
"commodities"
],
"maxHoldHours": 24,
"requiredExchange": "aster"
},
"createdAt": "2023-11-07T05:31:56Z",
"id": "12",
"lastError": "<string>",
"state": {
"asset": "<string>",
"baseQuantity": "<string>",
"limitLeg": "long",
"long": {
"baseSymbol": "<string>",
"exchange": "aster",
"marketId": "<string>",
"quoteSymbol": "<string>"
},
"openedAt": "2023-11-07T05:31:56Z",
"orderId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"phase": "closing",
"short": {
"baseSymbol": "<string>",
"exchange": "aster",
"marketId": "<string>",
"quoteSymbol": "<string>"
},
"submittedAt": "2023-11-07T05:31:56Z",
"closeAttempts": 0
},
"status": "halted",
"updatedAt": "2023-11-07T05:31:56Z"
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
Venues the pair legs may be opened on; each pair uses two of them
2Supported perpetual exchange
aster, binance, bybit, extended, gate, hyperliquid, lighter, mexc, ondo, robinhood Leverage per leg, capped by each market limit
x <= 90071992547409912
Most a round trip (entry plus exit, fees and book crossing) may cost, in basis points of the notional; pairs are ranked by expected funding over the hold minus this cost
12
Notional per leg in USD
500
Asset classes to trade; omit to trade every category
1Asset class of the market
commodities, crypto, etf-index, forex, pre-ipo, stocks-asia, stocks-us Longest time to hold a pair before closing it even while the spread still pays; low for frequent rolls, high for carry
Venue one leg of every pair must be on; omit to let any two venues pair up
aster, binance, bybit, extended, gate, hyperliquid, lighter, mexc, ondo, robinhood Response
Bot configuration, applied by the runner on its next poll; null when the stored configuration no longer matches the current schema and the bot must be recreated.
Show child attributes
Show child attributes
When the bot was created.
^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$Bot identifier.
^[1-9]\d*$"12"
Why the bot halted, or null when it has never halted.
What the runner is doing with this bot: the pair it holds and the order it waits on; null while the bot is flat, has never run, or its stored state no longer matches the current schema.
Show child attributes
Show child attributes
running — the bot executes scheduled ticks. not_started — it has never been started, or you stopped it. halted — it hit a state it will not act on and stopped itself; lastError says which.
halted, not_started, running When the bot last changed.
^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$