API Reference
REST API & WebSocket
80+ endpoints tersedia untuk tier Quant dan API Pro. Base URL: https://bandaralert.com/api/v1
Authentication
Dua metode autentikasi tersedia (dicek berurutan). Gunakan salah satu:
# Option 1: API Key (recommended) X-API-Key: ba_live_xxxx_xxxx
# Option 2: JWT Bearer Token Authorization: Bearer <JWT_TOKEN>
Rate Limits
Satu jendela geser 1 jam, dihitung per akun, bukan per API key. Beberapa API key pada akun yang sama berbagi satu jatah. Saat batas tercapai, API menjawab 429 dengan header Retry-After berisi lama tunggu dalam detik.
| Tier | Permintaan per jam | Setara berkelanjutan |
|---|---|---|
| Free | 300 | ~0,08 req/detik |
| Pro | 3.000 | ~0,83 req/detik |
| Quant | 30.000 | ~8,3 req/detik |
| API Pro | 60.000 | ~16,7 req/detik |
X-RateLimit-Remaining; hitung sisa jatah dari jumlah permintaan Anda sendiri, dan baca Retry-After saat 429. Setiap tier berbayar bisa memanggil API; yang membedakan tier adalah KELAPANGAN, bukan izin. API Pro berada di dua kali plafon Quant. Jendelanya per AKUN, jadi menerbitkan lebih banyak kunci pada satu akun tidak menaikkannya. Bila Anda butuh lebih dari 60.000 per jam, tanyakan sebelum membeli: itu percakapan kapasitas, bukan naik paket.Signals
Market Data
Brokers
Portfolio & Tools
AI Agents
Network (Broker Flow)
Computed (Aggregates)
Smart Money Trail (Legacy)
Smart Money Tracker v2 (IAS)
Broker & Insider (Pro plan)
Backtest
Watchlists
Alerts
Preferences
Auth
Billing & Payments
Referral Program
Helpdesk
Response Format
Envelope sukses TIDAK seragam di seluruh API. Tulis klien Anda mengikuti bentuk grup yang Anda panggil, dan perlakukan envelope sebagai per-grup, bukan global:
// Wrapped: Computed, Broker & Insider (/dsh), Holders, Macro, News, Track Record
{
"success": true,
"data": { ... }
}
// Bare: Signals, Market Data, Brokers, AI Agents, Network, and the rest.
// The payload object is returned directly, with no "success" wrapper.
{
"ticker": "BBCA",
"data": [ ... ]
}
// Error (all groups: the FastAPI shape, nested under "detail")
{
"detail": {
"error": "rate_limit_exceeded",
"retry_after_seconds": 60
}
}Tabel Error Codes di bawah memuat status HTTP dan nilai detail.error yang terbaca mesin. Tidak ada blok meta berisi penghitung paginasi; endpoint yang memakai paginasi melakukannya lewat query parameter masing-masing, terdokumentasi per endpoint di atas.
Contoh Penggunaan
import requests
API_KEY = "ba_live_xxxx_xxxx"
BASE = "https://bandaralert.com/api/v1"
headers = {"X-API-Key": API_KEY}
# 1. BASS scores. This group wraps its payload as {"data": [...], "meta": {...}}
resp = requests.get(f"{BASE}/signals/broker-accumulation", headers=headers)
signals = resp.json()["data"]
for s in signals[:5]:
# The field is signal_confidence, not confidence.
print(f"{s['ticker']}: BASS={s['bass_score']:.0f}, {s['signal_confidence']}")
# 2. OHLCV for BBCA (30 days) -> {"ticker", "interval", "data": [...]}
ohlcv = requests.get(f"{BASE}/data/ohlcv/BBCA?days=30", headers=headers).json()
print(f"BBCA: {len(ohlcv['data'])} data points")
# 3. Market regime. The state field is current_state, and its values are
# foreign_inflow_bull | domestic_rotation_neutral | risk_off_bear.
# Probabilities are an object, one float per state, not a single number.
regime = requests.get(f"{BASE}/signals/market-regime", headers=headers).json()
probs = regime["probabilities"]
print(f"Regime: {regime['current_state']} for {regime['days_in_state']} days")
print(f" bull={probs['bull']:.0%} neutral={probs['neutral']:.0%} bear={probs['bear']:.0%}")
# 4. Ekspor seluruh papan ke Excel. SATU permintaan, sekitar 830 emiten yang
# punya data broker EOD. Ini jalur termurah; jangan looping per emiten kalau
# yang Anda butuhkan ringkasannya.
import pandas as pd
rows = requests.get(
f"{BASE}/signals/broker-accumulation",
params={"min_score": 0, "limit": 1000},
headers=headers, timeout=60,
).json()["data"]
pd.DataFrame(rows).to_excel("bandarmologi_eod.xlsx", index=False)
# 5. Rincian PER KODE BROKER, satu emiten per permintaan. Beri jeda: di tepi ada
# batas 10 permintaan per detik per alamat IP, terpisah dari kuota per-jam
# akun Anda. Menyapu ~830 emiten memakan sekitar dua menit.
import time
detail = []
for r in rows:
d = requests.get(f"{BASE}/dsh/broker-summary/{r['ticker']}",
headers=headers, timeout=30)
if d.status_code == 429:
time.sleep(int(d.headers.get("Retry-After", 5)))
continue
detail.append({"ticker": r["ticker"], **d.json()})
time.sleep(0.15)WebSocket API
Socket langganan untuk peristiwa sinyal. Baca catatan status di bawah sebelum Anda membangun di atasnya: sebagian besar kanal menerima langganan tetapi belum ada produsen yang menerbitkan ke sana.
preference_updated, terpancar saat Anda mengubah pengaturan sendiri. Kanal sinyal pasar di bawah menerima langganan lalu diam, karena sinyal pasar diterbitkan lewat jalur lain yang tidak dibaca socket ini. Perlakukan kanal pasar sebagai belum mengirim dan gunakan endpoint REST untuk sinyal tersebut. Catatan ini akan dicabut begitu produsen tersambung.| Channel | Description | Mengirim hari ini |
|---|---|---|
| broker_accumulation | Peristiwa skor BASS | Belum ada produsen |
| volume_anomaly | Peristiwa anomali volume | Belum ada produsen |
| liquidity_stress | Peristiwa tekanan likuiditas | Belum ada produsen |
| regime_change | Transisi regime pasar | Belum ada produsen |
| sector_rotation | Peristiwa rotasi sektor | Belum ada produsen |
| smart_money_score | Peristiwa Smart Money Score | Belum ada produsen |
| preference_updated | Pengaturan Anda sendiri berubah (privat, hanya pemilik) | Ya |
Contoh WebSocket
// The JWT goes in the query string. There is no in-band auth message,
// and an API key does not open this socket.
const ws = new WebSocket(
"wss://bandaralert.com/api/v1/ws/signals?token=" + JWT_TOKEN
);
ws.onopen = () => {
// Subscribe. Note the key is "action", not "type".
ws.send(JSON.stringify({
action: "subscribe",
channels: ["broker_accumulation", "volume_anomaly", "regime_change"],
tickers: ["BBCA", "BBRI", "TLKM"], // optional; omit for all tickers
}));
// Heartbeat is client-initiated. Nothing pings you automatically.
setInterval(() => ws.send(JSON.stringify({ action: "ping" })), 30000);
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
// Handshake and heartbeat replies
if (msg.type === "subscribed") return console.log("Subscribed:", msg.channels);
if (msg.type === "pong") return;
if (msg.error) return console.error(msg.error, msg.message);
// Signal frame. msg.type is the channel name; the payload is msg.data.
// Reminder: the market channels have no producer yet, so in practice
// only "preference_updated" arrives today.
console.log(msg.type, msg.ticker, msg.timestamp, msg.data);
};
// Close code 4001 means the token was missing, invalid, revoked, or expired.
ws.onclose = (e) => {
if (e.code === 4001) console.error("Auth rejected:", e.reason);
};wss://bandaralert.com/api/v1/ws/signals?token=<JWT>. Autentikasi memakai JWT di query string; API key tidak membuka socket ini. Token yang hilang, tidak valid, dicabut, atau kedaluwarsa menutup koneksi dengan kode 4001 sebelum diterima. Heartbeat dimulai klien: kirim {"action": "ping"} dan server menjawab {"type": "pong"}. Tidak ada heartbeat berkala dari sisi server dan tidak ada auto-reconnect dari sisi server, jadi logika sambung ulang ada di klien Anda.Error Codes
{"success": false, "error": "query_failed", "data": []}. Klien yang hanya bercabang pada status HTTP akan membacanya sebagai hasil kosong, bukan kegagalan, jadi periksa juga field success pada grup tersebut.https://bandaralert.com/openapi.json. Di produksi rute ini terautentikasi dan bergerbang tier, bukan publik: sertakan kredensial Anda pada permintaan, atau Anda akan menerima 401 secara anonim dan 403 pada tier yang tidak membukanya. Setelah diambil, gunakan untuk membangkitkan SDK di bahasa apa pun dengan alat seperti openapi-generator. Penjelajah interaktif Swagger dan ReDoc sengaja dimatikan di produksi.