Understanding market activity requires more than simply observing stock prices. Monitoring broker transactions together with potential multibagger candidates can provide additional insight into market participation and identify stocks that deserve further analysis.
The Indonesia Stock Exchange API offers endpoints that provide Broker Activity information and Multibagger Scan results. Using Python, these datasets can be retrieved automatically, transformed into structured tables, and summarized without manually inspecting raw API responses.
In this tutorial, we build a Broker Activity and Multibagger Scan Dashboard using Python and the Indonesia Stock Exchange API. The notebook retrieves Broker Activity records and Multibagger Scan candidates, safely handles API requests, converts JSON responses into pandas DataFrames, and summarizes the retrieved information in a structured format.
The notebook consists of five sections covering project setup, secure API requests, Broker Activity retrieval, Multibagger Scan retrieval, and data analysis.
What This Project Builds
This notebook builds a lightweight dashboard for monitoring broker transactions together with potential multibagger candidates.
The completed workflow includes:
Broker Activity retrieval
Multibagger Scan retrieval
RapidAPI integration
Safe API request handling
JSON parsing
pandas DataFrame conversion
Data normalization
Market analysis summary
Rather than building predictive trading models, the notebook focuses on retrieving, organizing, and presenting market screening information from the Indonesia Stock Exchange API.
CELL 1 — Setup
import requests
import pandas as pd
import json
from datetime import datetime
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://indonesia-stock-exchange-idx.p.rapidapi.com"
HEADERS = {
"Content-Type": "application/json",
"x-rapidapi-host": "indonesia-stock-exchange-idx.p.rapidapi.com",
"x-rapidapi-key": API_KEY
}
pd.set_option("display.max_columns", None)
pd.set_option("display.width", 1000)Note: Replace the original API key with
YOUR_API_KEYbefore publishing the notebook.
Explanation
The notebook begins by importing the Python libraries required throughout the project.
The requests library is used to communicate with the Indonesia Stock Exchange API through RapidAPI. Every API request performed later in the notebook depends on this library to retrieve Broker Activity and Multibagger Scan information.
The notebook imports pandas to transform API responses into structured DataFrames, allowing market information to be displayed in a clean tabular format. The json library is used to display formatted JSON output during debugging, while datetime is imported for handling date-related values when required.
The second part of the cell prepares the RapidAPI configuration. The authentication key is stored inside the API_KEY variable, followed by the API base URL and the HTTP request headers required for every request.
Finally, the notebook adjusts several pandas display settings. By increasing the maximum number of visible columns and expanding the display width, the notebook ensures that large DataFrames remain readable during analysis.
CELL 2 — Safe Request Function
def safe_get(endpoint, params=None):
url = BASE_URL + endpoint
try:
response = requests.get(url, headers=HEADERS, params=params, timeout=30)
print("Endpoint:", endpoint)
print("Status Code:", response.status_code)
if response.status_code == 200:
return response.json()
elif response.status_code == 403:
print("⚠️ API menolak akses. Cek subscription RapidAPI.")
print(response.text)
return None
elif response.status_code == 429:
print("⚠️ Rate limit tercapai. Tunggu beberapa menit.")
print(response.text)
return None
else:
print("❌ Gagal mengambil data")
print(response.text)
return None
except Exception as e:
print("❌ Error:", e)
return NoneExplanation
The second section creates a reusable helper function named safe_get() that manages communication with the Indonesia Stock Exchange API.
Instead of writing request logic repeatedly for every endpoint, the notebook centralizes the entire communication process inside one function. This keeps the project organized while making future maintenance much easier.
The function starts by combining the predefined BASE_URL with the endpoint passed as an argument. It then sends an HTTP GET request using the configured authentication headers together with optional query parameters.
During execution, the notebook prints both the requested endpoint and the returned HTTP status code. These messages provide immediate feedback and simplify troubleshooting whenever an API request encounters a problem.
The helper function also includes several layers of error handling. When the API returns HTTP 200, the JSON response is returned immediately. If the response is HTTP 403, the notebook informs the user that the RapidAPI subscription does not permit access to the selected endpoint. When HTTP 429 is returned, indicating that the request limit has been exceeded, the notebook displays a warning message and returns None.
Unexpected HTTP responses are printed directly so that users can inspect the returned message, while any network-related exceptions are handled gracefully without terminating the notebook.
By centralizing API communication in a single reusable function, the remaining cells can focus entirely on retrieving and analyzing Broker Activity and Multibagger Scan data without duplicating request logic.
CELL 3 — Retrieve Broker Activity Data
broker_params = {
"limit": 50,
"marketBoard": "MARKET_BOARD_ALL",
"page": 1,
"investorType": "INVESTOR_TYPE_ALL",
"from": "2026-01-02",
"transactionType": "TRANSACTION_TYPE_NET",
"to": "2026-01-02"
}
broker_json = safe_get(
"/api/market-detector/broker-activity/DH",
params=broker_params
)
print("\nDEBUG BROKER:")
print(json.dumps(broker_json, indent=2)[:2000] if broker_json else None)Explanation
After defining the reusable request function, the notebook retrieves Broker Activity data from the Indonesia Stock Exchange API.
The request parameters are stored inside the broker_params dictionary, making the query easier to read and modify. These parameters specify the number of records to retrieve, the market board, page number, investor type, transaction type, and the date range used for the analysis.
Rather than embedding these values directly into the request URL, the notebook passes them separately through the params argument. This keeps the request more organized while allowing the query parameters to be adjusted independently whenever different broker activity data is needed.
The notebook then calls the previously created safe_get() function to request Broker Activity information for the broker code DH.
After receiving the response, the notebook prints a formatted preview of the returned JSON object. Instead of displaying the complete response, only the first 2,000 characters are printed using json.dumps(). This provides enough information to inspect the API structure while preventing the notebook output from becoming excessively long.
Displaying this JSON preview is particularly useful during development because it allows users to understand the structure of the returned data before converting it into a pandas DataFrame.
CELL 4 — Retrieve Multibagger Scan Data
import time
def safe_get(endpoint, params=None, retry=3):
url = BASE_URL + endpoint
for i in range(retry):
try:
response = requests.get(
url,
headers=HEADERS,
params=params,
timeout=120
)
print("Status:", response.status_code)
if response.status_code == 200:
return response.json()
if response.status_code == 429:
print("Rate limit...")
time.sleep(5)
continue
return None
except requests.exceptions.Timeout:
print(f"Timeout ({i+1}/{retry})")
time.sleep(5)
except Exception as e:
print(e)
time.sleep(3)
return NoneExplanation
The fourth section prepares a second version of the safe_get() function specifically for retrieving Multibagger Scan data.
Unlike the Broker Activity request, this implementation introduces a retry mechanism to improve reliability when communicating with the API. Some endpoints may occasionally return timeouts or temporary rate-limit responses, so the notebook attempts the request multiple times before giving up.
The notebook first imports the time module, which is used to pause execution between retry attempts.
The updated safe_get() function accepts an additional argument named retry, which determines how many times the request should be attempted. By default, the notebook retries the request up to three times.
Each attempt sends an HTTP GET request using the same authentication headers configured earlier. The timeout value is increased to 120 seconds, providing the API with additional time to return larger responses.
If the request succeeds with HTTP 200, the JSON response is returned immediately.
When the API returns HTTP 429, indicating that the request limit has been reached, the notebook pauses for five seconds before automatically retrying the request. This simple retry strategy increases the chance of obtaining a successful response without requiring manual intervention.
The function also handles timeout exceptions separately. If a timeout occurs, the notebook reports the current retry attempt, waits for five seconds, and then tries again.
Any other unexpected exceptions are printed to the notebook before waiting briefly and continuing to the next retry cycle.
If all retry attempts fail, the function returns None, allowing the remaining workflow to determine how unavailable data should be handled.
Compared with the request function introduced earlier, this version provides additional resilience for endpoints that may require longer processing times or that are temporarily affected by API rate limits.
CELL 5 — Clean Data and Analysis
def extract_data(json_data):
if json_data is None:
return []
if isinstance(json_data, list):
return json_data
if isinstance(json_data, dict):
for key in ["data", "result", "results", "items"]:
if key in json_data:
value = json_data[key]
if isinstance(value, list):
return value
if isinstance(value, dict):
for subkey in ["data", "items", "results"]:
if subkey in value and isinstance(value[subkey], list):
return value[subkey]
return [value]
return [json_data]
return []
broker_data = extract_data(broker_json)
multibagger_data = extract_data(multibagger_json)
df_broker = pd.json_normalize(broker_data)
df_multi = pd.json_normalize(multibagger_data)
print("=" * 70)
print("DATA BROKER ACTIVITY")
print("=" * 70)
if not df_broker.empty:
display(df_broker.head(20))
else:
print("Tidak ada data broker activity.")
print("\n" + "=" * 70)
print("DATA SCAN MULTIBAGGER")
print("=" * 70)
if not df_multi.empty:
display(df_multi.head(20))
else:
print("Tidak ada data multibagger.")
print("\n" + "=" * 70)
print("RINGKASAN ANALISIS")
print("=" * 70)
print("\nBROKER ACTIVITY - DH")
if not df_broker.empty:
print("Jumlah data :", len(df_broker))
possible_cols = df_broker.columns.tolist()
print("Kolom tersedia:", possible_cols)
for col in ["symbol", "stockCode", "code", "ticker"]:
if col in df_broker.columns:
print("\nTop saham berdasarkan data broker:")
display(df_broker[col].value_counts().head(10).reset_index())
break
else:
print("Data broker tidak tersedia.")
print("\nSCAN MULTIBAGGER - ENERGY")
if not df_multi.empty:
print("Jumlah kandidat :", len(df_multi))
score_col = None
for col in ["score", "multibagger_score", "total_score", "final_score"]:
if col in df_multi.columns:
score_col = col
break
symbol_col = None
for col in ["symbol", "stockCode", "code", "ticker"]:
if col in df_multi.columns:
symbol_col = col
break
if score_col:
df_multi_sorted = df_multi.sort_values(by=score_col, ascending=False)
display(df_multi_sorted.head(10))
else:
display(df_multi.head(10))
if symbol_col:
print("\nTop kandidat multibagger:")
for i, row in df_multi.head(10).iterrows():
symbol = row.get(symbol_col, "-")
score = row.get(score_col, "-") if score_col else "-"
print(f"{i+1}. {symbol} | Score: {score}")
else:
print("Data multibagger tidak tersedia.")Explanation
The final cell cleans, normalizes, displays, and summarizes both datasets retrieved in the previous sections.
The notebook begins by defining a helper function named extract_data(). This function is designed to handle multiple JSON response formats. Since API responses may be returned as lists, dictionaries, nested dictionaries, or empty values, the function checks the structure step by step before extracting the actual records.
If the response is already a list, it is returned directly. If the response is a dictionary, the function searches common keys such as data, result, results, and items. When one of those keys contains a list, that list becomes the final dataset. If the value is another dictionary, the function checks deeper keys such as data, items, and results. This approach allows the notebook to work with different API response structures without needing manual changes.
After extracting the records, the notebook converts both datasets into pandas DataFrames using pd.json_normalize(). The Broker Activity records are stored inside df_broker, while the Multibagger Scan records are stored inside df_multi.
The first display section prints DATA BROKER ACTIVITY and shows the first twenty rows of the broker DataFrame when data is available. If the DataFrame is empty, the notebook prints a message indicating that Broker Activity data is unavailable.
The second display section performs the same process for DATA SCAN MULTIBAGGER. When Multibagger data exists, the notebook displays the first twenty rows. Otherwise, it reports that no Multibagger data is available.
The final part of the cell generates a concise analysis summary. For Broker Activity, the notebook prints the total number of records and lists the available columns. It then searches for a suitable stock symbol column, checking possible names such as symbol, stockCode, code, and ticker. When a matching column is found, the notebook displays the most frequent stock symbols based on the broker data.
For Multibagger Scan, the notebook prints the total number of candidates and searches for a score column using several possible names, including score, multibagger_score, total_score, and final_score. It also searches for a symbol column using the same flexible approach used for Broker Activity.
If a score column is found, the DataFrame is sorted from the highest score to the lowest score before displaying the top ten candidates. If no score column is available, the notebook simply displays the first ten rows.
Finally, if a symbol column exists, the notebook prints a short list of top Multibagger candidates together with their scores. This makes the final output easier to read and gives users a quick summary of the most relevant candidates returned by the API.
Result
The notebook produces two main outputs: Broker Activity and Scan Multibagger.
Broker Activity Output
The Broker Activity section displays transaction-related data retrieved from the broker activity endpoint.
The notebook uses the broker code DH together with parameters such as market board, investor type, transaction type, date range, page number, and record limit. After the API response is received, the data is extracted from the JSON structure and converted into a pandas DataFrame.
If Broker Activity data is available, the notebook displays the first twenty records and prints the total number of rows. It also lists the available columns and attempts to identify the stock symbol column so the most frequently appearing stocks can be shown.
If the dataset is unavailable, the notebook prints:
Tidak ada data broker activity.Scan Multibagger Output
The Scan Multibagger section displays potential Multibagger candidates from the retrieved API response.
The notebook converts the extracted records into a pandas DataFrame, displays the first twenty rows, and then performs a simple ranking process when a score column is available.
If one of the supported score columns exists, the candidates are sorted in descending order so that the highest-scoring records appear first. If a stock symbol column is available, the notebook prints the top candidates together with their scores.
If the dataset is unavailable, the notebook prints:
Tidak ada data multibagger.Analysis Summary
The final summary combines both datasets into a readable notebook output.
For Broker Activity, it reports the number of retrieved records, available columns, and top stock counts when a symbol column exists.
For Scan Multibagger, it reports the number of candidates, sorts candidates by score when possible, and prints a compact list of top candidates.
This gives users a quick overview of both broker activity and potential growth candidates without manually inspecting the raw JSON response.
Overall Workflow
The notebook follows a five-step workflow:
Import the required Python libraries and configure RapidAPI access.
Create a safe API request function with error handling.
Retrieve Broker Activity data using broker code DH and selected query parameters.
Define a retry-capable request function for Multibagger Scan retrieval.
Extract records from flexible JSON structures.
Convert Broker Activity and Multibagger Scan data into pandas DataFrames.
Display the first twenty records from each dataset.
Generate a concise analysis summary for both datasets.
Why This Project Matters
Broker Activity and Multibagger Scan provide two different perspectives on stock market analysis.
Broker Activity helps users inspect transaction participation from a selected broker, while Multibagger Scan focuses on identifying stocks that match predefined growth-oriented screening criteria.
By combining both datasets into one notebook, users can review market participation and potential stock candidates in a single workflow. The project also demonstrates practical API handling techniques, including safe requests, status-code checks, retry handling, timeout management, JSON extraction, and pandas normalization.
The notebook is intentionally lightweight and does not add prediction models or trading recommendations. Its main purpose is to retrieve data, organize responses, and summarize available information in a clean and reproducible way.
Conclusion
The Broker Activity and Multibagger Scan Dashboard demonstrates how Python can be used to retrieve, structure, and analyze market data from the Indonesia Stock Exchange API.
Using requests, pandas, and json, the notebook retrieves Broker Activity data, prepares a retry-capable request function for Multibagger Scan, extracts records from flexible JSON structures, converts both responses into DataFrames, and summarizes the results.
Although simple, this project provides a useful foundation for building more advanced stock monitoring tools, broker activity dashboards, and screening workflows using Python and the Indonesia Stock Exchange API.
