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 | 10.000 | ~2,8 req/detik |
X-RateLimit-Remaining; hitung sisa jatah dari jumlah permintaan Anda sendiri, dan baca Retry-After saat 429. Quant berada di atas API Pro pada permintaan per jam karena API Pro dihargai atas keluasan akses (semua endpoint, satu tahun), bukan atas throughput. Bila Anda butuh plafon lebih tinggi dari 10.000 per jam, tanyakan sebelum membeli: itu percakapan kapasitas, bukan naik paket.Signals
Prefix: /api/v1/signals
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /volume-anomaly | Anomali volume hari ini (semua saham) | severity=warning|severe |
| GET | /broker-accumulation | Semua BASS scores terbaru | min_score=0&sort=desc |
| GET | /broker-accumulation/{ticker} | BASS detail per ticker dengan komponen breakdown | days=30 |
| GET | /smart-money-v2 | Smart Money Score semua saham | min_score=0 |
| GET | /market-regime | Status regime saat ini (Bull/Neutral/Bear) + probabilitas | |
| GET | /liquidity-stress | Liquidity shift scores per saham | |
| GET | /sector-rotation | Sector rotation analysis + heatmap data | days=20 |
| GET | /convergence | Saham dengan multiple sinyal converge | min_signals=2 |
| GET | /multibagger-scan | Hasil multibagger scanner | min_bass=65 |
Market Data
Prefix: /api/v1/data
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /ohlcv/{ticker} | Historical OHLCV data | days=30 |
| GET | /broker-summary/{ticker} | Broker buy/sell per saham | days=5 |
| GET | /earnings/{ticker} | Earnings data dan analyst consensus | |
| GET | /fundamentals/{ticker} | Data fundamental (P/E, P/B, ROE, margins) | |
| GET | /correlation | Correlation matrix antar saham | tickers=BBCA,BBRI&days=90 |
| GET | /sectors | List semua sektor dan subsector IDX |
Brokers
Prefix: /api/v1/brokers
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | / | List semua broker aktif (filterable by tier) | tier=foreign|domestic|retail |
| GET | /leaderboard | Top brokers by total flow | days=20&limit=20 |
| GET | /{code} | Profil broker detail + batting average | |
| GET | /{code}/holdings | Top holdings broker saat ini | limit=10 |
| GET | /{code}/activity | Activity timeline broker | days=30 |
Portfolio & Tools
Prefix: /api/v1
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /watchlist | Get user watchlist | |
| POST | /watchlist | Add ticker ke watchlist | body: {ticker: string} |
| DELETE | /watchlist/{ticker} | Remove ticker dari watchlist | |
| GET | /portfolio | Get user portfolio positions | |
| POST | /portfolio | Add posisi ke portfolio | body: {ticker, lots, price} |
| GET | /risk-calculator | Calculate position sizing & risk-reward | entry=9200&sl=8900&tp=9800&capital=10000000 |
| POST | /backtest | Run backtest strategi | body: {strategy config} |
AI Agents
Prefix: /api/v1/ai
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| POST | /stock-report | Generate AI Stock Report untuk 1 ticker | body: {ticker, timeframe, language} |
| GET | /daily-brief | Ringkasan pasar harian terbaru | |
| POST | /generate-brief | Trigger generate daily brief (admin) | |
| POST | /report-pdf | Download AI Stock Report sebagai PDF | body: {ticker, timeframe} |
| GET | /brief-pdf | Download Daily Brief sebagai PDF | date=2026-04-18 |
| POST | /portfolio-risk | AI analyze risiko portfolio user | body: {holdings: [{ticker, weight}]} |
| GET | /quota | Cek quota AI yang tersisa hari ini |
Network (Broker Flow)
Prefix: /api/v1/network
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /broker-graph | Bipartite graph broker↔ticker | days=20 |
| GET | /broker-bubbles | Data untuk visualisasi bubble (ukuran + warna) | limit=30 |
Computed (Aggregates)
Prefix: /api/v1/computed
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /enriched-list | Key ratios + BASS + flow untuk semua 900+ saham | |
| GET | /broker-leaderboard | Top broker berdasarkan aktivitas | days=1&limit=30 (max 200) |
| GET | /broker-portfolio/{code} | Komposisi portfolio broker | |
| GET | /correlation | Correlation matrix untuk beberapa ticker | tickers=BBCA,BBRI&days=60 |
| GET | /risk-reward/{ticker} | Auto risk-reward analysis dengan scenario | days=30&risk_percent=1 |
| GET | /stock-enrichment/{ticker} | Mega endpoint: semua data untuk 1 ticker | |
| GET | /financials/{ticker} | Laporan keuangan lengkap |
Smart Money Trail (Legacy)
Prefix: /api/v1/trail
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /stock/{ticker} | Jejak arus dana per saham (legacy) | days=20 |
| GET | /broker/{code} | Jejak arus dana per broker (legacy) | days=20 |
| GET | /reversals | Sinyal reversal dari tracking dana (legacy) |
Smart Money Tracker v2 (IAS)
Prefix: /api/v1/trail/v2
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /ticker/{ticker} | Institutional Activity Score + breakdown 6 sinyal + konfirmasi konsentrasi broker | as_of=YYYY-MM-DD (optional) |
| GET | /ticker/{ticker}/history | IAS history N hari (sparkline) | days=30 (5-90) |
| GET | /leaderboard | Top tickers by IAS dengan filter signal/threshold | limit=50&min_ias=60&signal=BUY |
| GET | /reversals | Recent IAS signal direction transitions | lookback_days=5&limit=50 |
Broker & Insider (Pro plan)
Prefix: /api/v1/dsh
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /broker-summary/{ticker} | Broker buy/sell per saham harian + top concentration | as_of=YYYY-MM-DD |
| GET | /top-brokers | Market-wide broker leaderboard (live) | limit=50&order=net|total&group=FOREIGN |
| GET | /broker/{broker_code} | Detail per broker: profile + KPI + top 30 holdings + aktivitas 20 hari | days=20&top_holdings=30 |
| GET | /insider/recent | Insider trades terbaru (direksi/komisaris, IDX disclosure) | days=30&action=BUY|SELL&limit=50 |
| GET | /insider/{ticker} | Insider trades per ticker | days=180 |
| GET | /calendar/upcoming | Corporate calendar upcoming events (dividend/RUPS/IPO/split/dll) | days=30&event_type=dividend&ticker=BBCA |
| GET | /calendar/today | Today's corporate actions consolidated | |
| GET | /whales/{ticker} | Whale transaction snapshot per ticker (Pro tier) |
Backtest
Prefix: /api/v1/backtest
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /run | Jalankan backtest strategi | signal_type=bass&threshold=70&hold_days=10&ticker=BBCA |
Watchlists
Prefix: /api/v1/watchlists
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | / | List semua watchlist user | |
| POST | / | Create/update watchlist | body: {name, tickers: [...]} |
Alerts
Prefix: /api/v1/alerts
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | / | Riwayat alert yang pernah dikirim | limit=50 |
Preferences
Prefix: /api/v1/preferences
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | / | Get user preferences (bahasa, theme, indicator settings) | |
| PUT | / | Update user preferences | body: {language, theme, indicators} |
| GET | /presets | List preset indicator yang tersedia | |
| POST | /apply-preset | Apply preset ke user settings | body: {preset_id} |
| GET | /impact-preview | Preview dampak perubahan indikator ke BASS score |
Auth
Prefix: /api/v1/auth
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| POST | /register | Register akun baru | body: {email, password, display_name, referral_code?} |
| POST | /login | Login email + password, returns JWT | body: {email, password} |
| POST | Login via Google OAuth | body: {id_token} | |
| POST | /forgot-password | Kirim link reset password ke email | body: {email} |
| POST | /reset-password | Reset password dengan token dari email | body: {token, new_password} |
| PUT | /password | Change password (harus login) | body: {current_password, new_password} |
| POST | /refresh | Refresh JWT access token | body: {refresh_token} |
| POST | /logout | Logout + revoke refresh token | body: {refresh_token} |
| GET | /me | Profile user + tier info |
Billing & Payments
Prefix: /api/v1/billing
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /plans | List subscription plans + harga | |
| POST | /subscribe | Buat invoice subscribe + amount unik untuk QRIS | body: {plan_id} |
| GET | /invoices | Riwayat invoice user | page=1&limit=10 |
| POST | /upload-proof | Upload bukti transfer QRIS (auto-verified by AI Vision) | JSON: {payment_id, proof_base64} (atau multipart proof file) |
Referral Program
Prefix: /api/v1/referral
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| GET | /code | Get kode referral user + statistik | |
| GET | /credits | Balance kredit referral (20% first-sub, 5% renewal) | |
| POST | /apply | Apply kode referral saat checkout (diskon 10%) | body: {referral_code} |
Helpdesk
Prefix: /api/v1/helpdesk
| Method | Endpoint | Description | Parameters |
|---|---|---|---|
| POST | /tickets | Buat tiket support (auto-forward ke Telegram admin) | body: {category, subject, message, screenshot?} |
| GET | /tickets | Riwayat tiket user | |
| GET | /tickets/{id} | Detail tiket + replies | |
| POST | /tickets/{id}/reply | Reply tiket | body: {message} |
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, Datasaham, 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%}")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 pipeline harian dan dispatcher alert menulis ke bus 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
| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_request, bad_ticker, bad_date, bad_broker_code | Parameter tidak valid atau kurang. Ticker, tanggal, atau kode broker gagal validasi. |
| 401 | auth_required, invalid_token | API key atau JWT token tidak ada, tidak valid, atau kedaluwarsa. |
| 403 | tier_restricted, tier_required, tier_upgrade_required, forbidden, admin_required | Tier Anda tidak membuka endpoint ini. |
| 404 | not_found | Resource tidak ditemukan (ticker, kode broker, modul, dosir). |
| 429 | rate_limit_exceeded | Jendela per jam penuh. Tunggu sesuai header Retry-After. |
| 500 | ai_generation_failed | Server gagal saat membangkitkan. Coba lagi sebentar. |
| 502 | upstream_error | Sumber data hulu gagal menjawab. |
{"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.