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>
API key bisa di-generate di Settings → API Keys. Jaga keamanan API key Anda, jangan share di repository publik.

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.

TierPermintaan per jamSetara berkelanjutan
Free300~0,08 req/detik
Pro3.000~0,83 req/detik
Quant30.000~8,3 req/detik
API Pro10.000~2,8 req/detik
Yang tidak dicakup tabel ini. Tidak ada limiter burst per menit terpisah dan tidak ada batas koneksi bersamaan di API. Jendela per jam adalah satu-satunya kuota yang ada, jadi tidak ada angka burst atau concurrency yang dikutip di sini. Tidak ada pula response header 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

MethodEndpointDescriptionParameters
GET/volume-anomalyAnomali volume hari ini (semua saham)severity=warning|severe
GET/broker-accumulationSemua BASS scores terbarumin_score=0&sort=desc
GET/broker-accumulation/{ticker}BASS detail per ticker dengan komponen breakdowndays=30
GET/smart-money-v2Smart Money Score semua sahammin_score=0
GET/market-regimeStatus regime saat ini (Bull/Neutral/Bear) + probabilitas
GET/liquidity-stressLiquidity shift scores per saham
GET/sector-rotationSector rotation analysis + heatmap datadays=20
GET/convergenceSaham dengan multiple sinyal convergemin_signals=2
GET/multibagger-scanHasil multibagger scannermin_bass=65

Market Data

Prefix: /api/v1/data

MethodEndpointDescriptionParameters
GET/ohlcv/{ticker}Historical OHLCV datadays=30
GET/broker-summary/{ticker}Broker buy/sell per sahamdays=5
GET/earnings/{ticker}Earnings data dan analyst consensus
GET/fundamentals/{ticker}Data fundamental (P/E, P/B, ROE, margins)
GET/correlationCorrelation matrix antar sahamtickers=BBCA,BBRI&days=90
GET/sectorsList semua sektor dan subsector IDX

Brokers

Prefix: /api/v1/brokers

MethodEndpointDescriptionParameters
GET/List semua broker aktif (filterable by tier)tier=foreign|domestic|retail
GET/leaderboardTop brokers by total flowdays=20&limit=20
GET/{code}Profil broker detail + batting average
GET/{code}/holdingsTop holdings broker saat inilimit=10
GET/{code}/activityActivity timeline brokerdays=30

Portfolio & Tools

Prefix: /api/v1

MethodEndpointDescriptionParameters
GET/watchlistGet user watchlist
POST/watchlistAdd ticker ke watchlistbody: {ticker: string}
DELETE/watchlist/{ticker}Remove ticker dari watchlist
GET/portfolioGet user portfolio positions
POST/portfolioAdd posisi ke portfoliobody: {ticker, lots, price}
GET/risk-calculatorCalculate position sizing & risk-rewardentry=9200&sl=8900&tp=9800&capital=10000000
POST/backtestRun backtest strategibody: {strategy config}

AI Agents

Prefix: /api/v1/ai

MethodEndpointDescriptionParameters
POST/stock-reportGenerate AI Stock Report untuk 1 tickerbody: {ticker, timeframe, language}
GET/daily-briefRingkasan pasar harian terbaru
POST/generate-briefTrigger generate daily brief (admin)
POST/report-pdfDownload AI Stock Report sebagai PDFbody: {ticker, timeframe}
GET/brief-pdfDownload Daily Brief sebagai PDFdate=2026-04-18
POST/portfolio-riskAI analyze risiko portfolio userbody: {holdings: [{ticker, weight}]}
GET/quotaCek quota AI yang tersisa hari ini

Network (Broker Flow)

Prefix: /api/v1/network

MethodEndpointDescriptionParameters
GET/broker-graphBipartite graph broker↔tickerdays=20
GET/broker-bubblesData untuk visualisasi bubble (ukuran + warna)limit=30

Computed (Aggregates)

Prefix: /api/v1/computed

MethodEndpointDescriptionParameters
GET/enriched-listKey ratios + BASS + flow untuk semua 900+ saham
GET/broker-leaderboardTop broker berdasarkan aktivitasdays=1&limit=30 (max 200)
GET/broker-portfolio/{code}Komposisi portfolio broker
GET/correlationCorrelation matrix untuk beberapa tickertickers=BBCA,BBRI&days=60
GET/risk-reward/{ticker}Auto risk-reward analysis dengan scenariodays=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

MethodEndpointDescriptionParameters
GET/stock/{ticker}Jejak arus dana per saham (legacy)days=20
GET/broker/{code}Jejak arus dana per broker (legacy)days=20
GET/reversalsSinyal reversal dari tracking dana (legacy)

Smart Money Tracker v2 (IAS)

Prefix: /api/v1/trail/v2

MethodEndpointDescriptionParameters
GET/ticker/{ticker}Institutional Activity Score + breakdown 6 sinyal + konfirmasi konsentrasi brokeras_of=YYYY-MM-DD (optional)
GET/ticker/{ticker}/historyIAS history N hari (sparkline)days=30 (5-90)
GET/leaderboardTop tickers by IAS dengan filter signal/thresholdlimit=50&min_ias=60&signal=BUY
GET/reversalsRecent IAS signal direction transitionslookback_days=5&limit=50

Broker & Insider (Pro plan)

Prefix: /api/v1/dsh

MethodEndpointDescriptionParameters
GET/broker-summary/{ticker}Broker buy/sell per saham harian + top concentrationas_of=YYYY-MM-DD
GET/top-brokersMarket-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 haridays=20&top_holdings=30
GET/insider/recentInsider trades terbaru (direksi/komisaris, IDX disclosure)days=30&action=BUY|SELL&limit=50
GET/insider/{ticker}Insider trades per tickerdays=180
GET/calendar/upcomingCorporate calendar upcoming events (dividend/RUPS/IPO/split/dll)days=30&event_type=dividend&ticker=BBCA
GET/calendar/todayToday's corporate actions consolidated
GET/whales/{ticker}Whale transaction snapshot per ticker (Pro tier)

Backtest

Prefix: /api/v1/backtest

MethodEndpointDescriptionParameters
GET/runJalankan backtest strategisignal_type=bass&threshold=70&hold_days=10&ticker=BBCA

Watchlists

Prefix: /api/v1/watchlists

MethodEndpointDescriptionParameters
GET/List semua watchlist user
POST/Create/update watchlistbody: {name, tickers: [...]}

Alerts

Prefix: /api/v1/alerts

MethodEndpointDescriptionParameters
GET/Riwayat alert yang pernah dikirimlimit=50

Preferences

Prefix: /api/v1/preferences

MethodEndpointDescriptionParameters
GET/Get user preferences (bahasa, theme, indicator settings)
PUT/Update user preferencesbody: {language, theme, indicators}
GET/presetsList preset indicator yang tersedia
POST/apply-presetApply preset ke user settingsbody: {preset_id}
GET/impact-previewPreview dampak perubahan indikator ke BASS score

Auth

Prefix: /api/v1/auth

MethodEndpointDescriptionParameters
POST/registerRegister akun barubody: {email, password, display_name, referral_code?}
POST/loginLogin email + password, returns JWTbody: {email, password}
POST/googleLogin via Google OAuthbody: {id_token}
POST/forgot-passwordKirim link reset password ke emailbody: {email}
POST/reset-passwordReset password dengan token dari emailbody: {token, new_password}
PUT/passwordChange password (harus login)body: {current_password, new_password}
POST/refreshRefresh JWT access tokenbody: {refresh_token}
POST/logoutLogout + revoke refresh tokenbody: {refresh_token}
GET/meProfile user + tier info

Billing & Payments

Prefix: /api/v1/billing

MethodEndpointDescriptionParameters
GET/plansList subscription plans + harga
POST/subscribeBuat invoice subscribe + amount unik untuk QRISbody: {plan_id}
GET/invoicesRiwayat invoice userpage=1&limit=10
POST/upload-proofUpload bukti transfer QRIS (auto-verified by AI Vision)JSON: {payment_id, proof_base64} (atau multipart proof file)

Referral Program

Prefix: /api/v1/referral

MethodEndpointDescriptionParameters
GET/codeGet kode referral user + statistik
GET/creditsBalance kredit referral (20% first-sub, 5% renewal)
POST/applyApply kode referral saat checkout (diskon 10%)body: {referral_code}

Helpdesk

Prefix: /api/v1/helpdesk

MethodEndpointDescriptionParameters
POST/ticketsBuat tiket support (auto-forward ke Telegram admin)body: {category, subject, message, screenshot?}
GET/ticketsRiwayat tiket user
GET/tickets/{id}Detail tiket + replies
POST/tickets/{id}/replyReply tiketbody: {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.

Status pengiriman, apa adanya. Socket, autentikasinya, dan jabat tangan langganannya semua berfungsi. Yang belum ada adalah sisi produsen: hari ini satu-satunya peristiwa yang pernah diterbitkan ke bus sinyal adalah 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.
ChannelDescriptionMengirim hari ini
broker_accumulationPeristiwa skor BASSBelum ada produsen
volume_anomalyPeristiwa anomali volumeBelum ada produsen
liquidity_stressPeristiwa tekanan likuiditasBelum ada produsen
regime_changeTransisi regime pasarBelum ada produsen
sector_rotationPeristiwa rotasi sektorBelum ada produsen
smart_money_scorePeristiwa Smart Money ScoreBelum ada produsen
preference_updatedPengaturan 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);
};
Koneksi dan protokol: 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

HTTPCodeDescription
400invalid_request, bad_ticker, bad_date, bad_broker_codeParameter tidak valid atau kurang. Ticker, tanggal, atau kode broker gagal validasi.
401auth_required, invalid_tokenAPI key atau JWT token tidak ada, tidak valid, atau kedaluwarsa.
403tier_restricted, tier_required, tier_upgrade_required, forbidden, admin_requiredTier Anda tidak membuka endpoint ini.
404not_foundResource tidak ditemukan (ticker, kode broker, modul, dosir).
429rate_limit_exceededJendela per jam penuh. Tunggu sesuai header Retry-After.
500ai_generation_failedServer gagal saat membangkitkan. Coba lagi sebentar.
502upstream_errorSumber data hulu gagal menjawab.
Tidak semua kegagalan datang sebagai status non-2xx. Beberapa endpoint di grup ber-envelope memilih menurunkan mutu daripada melempar galat: kueri yang gagal mengembalikan HTTP 200 berisi {"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.
OpenAPI Spec: Spek terbaca mesin disajikan dalam format OpenAPI 3.1 di 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.