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
limit50Results per page (1-200)
offset0Number 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
400Bad request (invalid input, insufficient balance)
401Missing or invalid API key
500Internal server error