API Documentation
Trade programmatically using real-time Binance prices with virtual funds.
Authentication
All API endpoints require an X-API-Key header.
Create a portfolio from the dashboard to get your API key.
X-API-Key: your-api-key-here
POST
/api/v1/order
Execute a market order at the current Binance price.
Slippage: +0.05% (buy) / -0.05% (sell). Fee: 0.1% of notional. Quantity rounded to symbol lot size.
Request Body
{
"symbol": "BTCUSDT",
"side": "BUY",
"quantity": 0.001
}
Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"symbol": "BTCUSDT",
"side": "BUY",
"quantity": 0.001,
"price": 67235.50,
"quote_amount": 67.2355,
"fee": 0.0672355,
"timestamp": "2026-03-15T12:00:00"
}
curl -X POST https://exchange.gibby.uk/api/v1/order \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"symbol": "BTCUSDT", "side": "BUY", "quantity": 0.001}'
const res = await fetch("https://exchange.gibby.uk/api/v1/order", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "YOUR_API_KEY",
},
body: JSON.stringify({
symbol: "BTCUSDT",
side: "BUY",
quantity: 0.001,
}),
});
const order = await res.json();
console.log(order);
import requests
res = requests.post(
"https://exchange.gibby.uk/api/v1/order",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"symbol": "BTCUSDT", "side": "BUY", "quantity": 0.001},
)
print(res.json())
body := strings.NewReader(`{"symbol":"BTCUSDT","side":"BUY","quantity":0.001}`)
req, _ := http.NewRequest("POST", "https://exchange.gibby.uk/api/v1/order", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", "YOUR_API_KEY")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var order map[string]any
json.NewDecoder(resp.Body).Decode(&order)
fmt.Println(order)
let client = reqwest::Client::new();
let order: serde_json::Value = client
.post("https://exchange.gibby.uk/api/v1/order")
.header("X-API-Key", "YOUR_API_KEY")
.json(&serde_json::json!({
"symbol": "BTCUSDT",
"side": "BUY",
"quantity": 0.001
}))
.send()
.await?
.json()
.await?;
println!("{order:#?}");
POST
/api/v1/order/batch
Execute up to 20 orders sequentially. Each order can depend on balance changes from prior orders.
Request Body
{
"orders": [
{"symbol": "BTCUSDT", "side": "BUY", "quantity": 0.001},
{"symbol": "ETHUSDT", "side": "BUY", "quantity": 0.01}
]
}
Response
{
"results": [
{"success": true, "data": {"id": "...", "symbol": "BTCUSDT", ...}},
{"success": false, "error": "Insufficient USDT balance"}
]
}
curl -X POST https://exchange.gibby.uk/api/v1/order/batch \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"orders": [
{"symbol": "BTCUSDT", "side": "BUY", "quantity": 0.001},
{"symbol": "ETHUSDT", "side": "BUY", "quantity": 0.01}
]}'
const res = await fetch("https://exchange.gibby.uk/api/v1/order/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "YOUR_API_KEY",
},
body: JSON.stringify({
orders: [
{symbol: "BTCUSDT", side: "BUY", quantity: 0.001},
{symbol: "ETHUSDT", side: "BUY", quantity: 0.01},
],
}),
});
const results = await res.json();
console.log(results);
import requests
res = requests.post(
"https://exchange.gibby.uk/api/v1/order/batch",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"orders": [
{"symbol": "BTCUSDT", "side": "BUY", "quantity": 0.001},
{"symbol": "ETHUSDT", "side": "BUY", "quantity": 0.01},
]},
)
print(res.json())
body := strings.NewReader(`{
"orders": [
{"symbol":"BTCUSDT","side":"BUY","quantity":0.001},
{"symbol":"ETHUSDT","side":"BUY","quantity":0.01}
]
}`)
req, _ := http.NewRequest("POST", "https://exchange.gibby.uk/api/v1/order/batch", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", "YOUR_API_KEY")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var results map[string]any
json.NewDecoder(resp.Body).Decode(&results)
fmt.Println(results)
let client = reqwest::Client::new();
let results: serde_json::Value = client
.post("https://exchange.gibby.uk/api/v1/order/batch")
.header("X-API-Key", "YOUR_API_KEY")
.json(&serde_json::json!({
"orders": [
{"symbol": "BTCUSDT", "side": "BUY", "quantity": 0.001},
{"symbol": "ETHUSDT", "side": "BUY", "quantity": 0.01}
]
}))
.send()
.await?
.json()
.await?;
println!("{results:#?}");
GET
/api/v1/balances
Returns all non-zero asset balances for the portfolio.
Response
[
{"asset": "USDT", "free": 10000.0, "locked": 0.0},
{"asset": "BTC", "free": 0.5, "locked": 0.0}
]
curl https://exchange.gibby.uk/api/v1/balances \
-H "X-API-Key: YOUR_API_KEY"
const res = await fetch("https://exchange.gibby.uk/api/v1/balances", {
headers: {"X-API-Key": "YOUR_API_KEY"},
});
const balances = await res.json();
console.log(balances);
import requests
res = requests.get(
"https://exchange.gibby.uk/api/v1/balances",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json())
req, _ := http.NewRequest("GET", "https://exchange.gibby.uk/api/v1/balances", nil)
req.Header.Set("X-API-Key", "YOUR_API_KEY")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var balances []map[string]any
json.NewDecoder(resp.Body).Decode(&balances)
fmt.Println(balances)
let client = reqwest::Client::new();
let balances: serde_json::Value = client
.get("https://exchange.gibby.uk/api/v1/balances")
.header("X-API-Key", "YOUR_API_KEY")
.send()
.await?
.json()
.await?;
println!("{balances:#?}");
GET
/api/v1/trades
Returns paginated trade history, newest first.
| Param | Default | Description |
|---|---|---|
| limit | 50 | Results per page (1-200) |
| offset | 0 | Number of results to skip |
Response
{
"trades": [{"id": "...", "symbol": "BTCUSDT", "side": "BUY", ...}],
"total": 142,
"limit": 50,
"offset": 0
}
curl "https://exchange.gibby.uk/api/v1/trades?limit=10&offset=0" \
-H "X-API-Key: YOUR_API_KEY"
const res = await fetch("https://exchange.gibby.uk/api/v1/trades?limit=10", {
headers: {"X-API-Key": "YOUR_API_KEY"},
});
const data = await res.json();
console.log(data.trades);
import requests
res = requests.get(
"https://exchange.gibby.uk/api/v1/trades",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"limit": 10},
)
print(res.json())
req, _ := http.NewRequest("GET", "https://exchange.gibby.uk/api/v1/trades?limit=10", nil)
req.Header.Set("X-API-Key", "YOUR_API_KEY")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var data map[string]any
json.NewDecoder(resp.Body).Decode(&data)
fmt.Println(data)
let client = reqwest::Client::new();
let data: serde_json::Value = client
.get("https://exchange.gibby.uk/api/v1/trades")
.header("X-API-Key", "YOUR_API_KEY")
.query(&[("limit", "10")])
.send()
.await?
.json()
.await?;
println!("{data:#?}");
WS
/ws/{portfolio_id}
Real-time trade events. Authenticates via session cookie (browser-based).
Messages (server → client)
{
"type": "trade",
"data": {
"id": "...",
"symbol": "BTCUSDT",
"side": "BUY",
"quantity": 0.001,
"price": 67235.50,
"quote_amount": 67.2355,
"fee": 0.0672355,
"timestamp": "2026-03-15T12:00:00"
}
}
const ws = new WebSocket("wss://exchange.gibby.uk/ws/YOUR_PORTFOLIO_ID");
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === "trade") {
console.log("Trade executed:", msg.data);
}
};
ws.onopen = () => console.log("Connected");
ws.onclose = () => console.log("Disconnected");
import asyncio
import websockets
import json
async def listen():
uri = "wss://exchange.gibby.uk/ws/YOUR_PORTFOLIO_ID"
async with websockets.connect(uri) as ws:
async for message in ws:
msg = json.loads(message)
if msg["type"] == "trade":
print("Trade executed:", msg["data"])
asyncio.run(listen())
Errors
All errors return JSON:
{"error": "Description of what went wrong"}
| Status | Meaning |
|---|---|
| 400 | Bad request (invalid input, insufficient balance) |
| 401 | Missing or invalid API key |
| 500 | Internal server error |