OHLC.dev editorialIDX

IDX Global Market Analysis with Global Market Overview and Global Indices Impact

This project demonstrates how to build an IDX Global Market Analysis Dashboard using Python and RapidAPI. The notebook retrieves Global Market Overview and Global Indices Impact data, normalizes different API response structures, displays detailed market information, and generates a final dashboard.

August 14, 20267 min readRafatar
IDX Global Market Analysis with Global Market Overview and Global Indices Impact

Global market data provides useful context for understanding broader market conditions and their potential influence on the Indonesian stock market.

In this project, we will use two IDX API endpoints:

  • Global Market Overview

  • Global Indices Impact

The project consists of five cells covering API configuration, data requests, response normalization, detailed data inspection, and the final IDX Global Market Analysis Dashboard.

Cell 1 — Import Libraries and Configure the API

# ============================================================
# CELL 1 — IMPORT LIBRARY & KONFIGURASI API
# ============================================================

import requests
import pandas as pd
import json
import time
from datetime import datetime

BASE_URL = "https://indonesia-stock-exchange-idx.p.rapidapi.com"

RAPIDAPI_KEY = "YOUR_RAPIDAPI_KEY"

HEADERS = {
    "Content-Type": "application/json",
    "x-rapidapi-host": "indonesia-stock-exchange-idx.p.rapidapi.com",
    "x-rapidapi-key": RAPIDAPI_KEY
}

print("=" * 90)
print("IDX GLOBAL MARKET ANALYSIS")
print("=" * 90)
print("Base URL :", BASE_URL)
print("Status   : Konfigurasi siap")

Cell 1 imports the required libraries and prepares the RapidAPI configuration. The original API key is replaced with YOUR_RAPIDAPI_KEY for security, while the remaining code follows the notebook.

Cell 2 — Request Global Market Overview and Indices Impact

# ============================================================
# CELL 2 — REQUEST DATA API
# ============================================================

def request_api(endpoint, timeout=30):
    url = BASE_URL + endpoint

    print("=" * 90)
    print("Endpoint :", endpoint)

    try:
        response = requests.get(
            url,
            headers=HEADERS,
            timeout=timeout
        )

        print("Status   :", response.status_code)

        if response.status_code == 200:
            try:
                data = response.json()
                print("Success  : True")
                return data
            except Exception:
                print("Success  : False")
                print("Pesan    : Response bukan JSON valid.")
                print(response.text[:500])
                return None

        print("Success  : False")

        if response.status_code == 429:
            print("Pesan    : Rate limit RapidAPI tercapai.")
        elif response.status_code == 403:
            print("Pesan    : Akses ditolak / API belum tersedia pada paket Anda.")
        elif response.status_code == 401:
            print("Pesan    : API Key tidak valid atau belum diisi.")
        elif response.status_code == 422:
            print("Pesan    : Parameter request tidak valid.")

        print("\nResponse API:")
        print(response.text[:1000])

        return None

    except requests.exceptions.Timeout:
        print("Success  : False")
        print("Pesan    : Request timeout.")
        return None

    except requests.exceptions.RequestException as e:
        print("Success  : False")
        print("Pesan    :", str(e))
        return None


# ------------------------------------------------------------
# 1. GLOBAL MARKET OVERVIEW
# ------------------------------------------------------------

global_market_raw = request_api(
    "/api/global/market-overview"
)

# Jeda untuk mengurangi risiko 429
print("\n⏳ Memberikan jeda 5 detik sebelum request berikutnya...\n")
time.sleep(5)


# ------------------------------------------------------------
# 2. INDICES IMPACT
# ------------------------------------------------------------

indices_impact_raw = request_api(
    "/api/global/indices-impact"
)

Cell 2 defines a reusable request_api() function with handling for invalid JSON, rate limits, access errors, invalid API keys, invalid parameters, timeouts, and other request errors.

The notebook retrieves Global Market Overview first, waits five seconds to reduce the risk of rate limiting, and then requests Global Indices Impact.

Cell 3 — Normalize and Debug the API Response

# ============================================================
# CELL 3 — NORMALISASI & DEBUG DATA
# ============================================================

def extract_data(response):
    """
    Mengambil bagian data utama dari berbagai kemungkinan
    struktur response API.
    """

    if response is None:
        return None

    if isinstance(response, list):
        return response

    if isinstance(response, dict):

        # Struktur umum: {"success": true, "data": ...}
        if "data" in response:
            return response["data"]

        # Kemungkinan struktur lainnya
        for key in ["results", "result", "items"]:
            if key in response:
                return response[key]

        return response

    return None


global_market_data = extract_data(global_market_raw)
indices_impact_data = extract_data(indices_impact_raw)


def safe_normalize(data):
    """
    Mengubah data menjadi DataFrame tanpa error
    jika response berupa dict/list/nested JSON.
    """

    if data is None:
        return pd.DataFrame()

    try:
        if isinstance(data, list):
            return pd.json_normalize(data)

        elif isinstance(data, dict):
            return pd.json_normalize(data)

        else:
            return pd.DataFrame([{"value": data}])

    except Exception as e:
        print("Normalisasi gagal :", e)
        return pd.DataFrame()


global_market_df = safe_normalize(global_market_data)
indices_impact_df = safe_normalize(indices_impact_data)


print("=" * 90)
print("HASIL NORMALISASI")
print("=" * 90)

print("Global Market Records :", len(global_market_df))
print("Indices Impact Records:", len(indices_impact_df))


print("\n" + "=" * 90)
print("DEBUG STRUKTUR RESPONSE")
print("=" * 90)

print("\nGLOBAL MARKET OVERVIEW")
print("-" * 90)

print("Raw Type :", type(global_market_raw).__name__)
print("Data Type:", type(global_market_data).__name__)

if isinstance(global_market_raw, dict):
    print("Raw Keys :", list(global_market_raw.keys()))

if isinstance(global_market_data, dict):
    print("Data Keys:", list(global_market_data.keys()))

if len(global_market_df):
    print("Columns  :", list(global_market_df.columns))


print("\nINDICES IMPACT")
print("-" * 90)

print("Raw Type :", type(indices_impact_raw).__name__)
print("Data Type:", type(indices_impact_data).__name__)

if isinstance(indices_impact_raw, dict):
    print("Raw Keys :", list(indices_impact_raw.keys()))

if isinstance(indices_impact_data, dict):
    print("Data Keys:", list(indices_impact_data.keys()))

if len(indices_impact_df):
    print("Columns  :", list(indices_impact_df.columns))

Cell 3 extracts the primary dataset from several possible JSON structures and safely converts the results into pandas DataFrames. It also displays record counts, response types, available keys, and columns for debugging.

Cell 4 — Display Global Market and Indices Impact Data

# ============================================================
# CELL 4 — DETAIL GLOBAL MARKET & INDICES IMPACT
# ============================================================

pd.set_option("display.max_columns", None)
pd.set_option("display.max_rows", 100)
pd.set_option("display.max_colwidth", 100)


print("=" * 100)
print("GLOBAL MARKET OVERVIEW")
print("=" * 100)

if len(global_market_df):

    display(global_market_df)

else:
    print("Tidak ada data Global Market Overview.")

    if global_market_raw is not None:
        print("\nRaw Response:")
        print(
            json.dumps(
                global_market_raw,
                indent=2,
                ensure_ascii=False
            )[:5000]
        )


print("\n\n" + "=" * 100)
print("INDICES IMPACT")
print("=" * 100)

if len(indices_impact_df):

    display(indices_impact_df)

else:
    print("Tidak ada data Indices Impact.")

    if indices_impact_raw is not None:
        print("\nRaw Response:")
        print(
            json.dumps(
                indices_impact_raw,
                indent=2,
                ensure_ascii=False
            )[:5000]
        )

Cell 4 displays the complete normalized datasets. If a DataFrame is empty but a raw response exists, the original JSON response is displayed as a fallback.

Cell 5 — IDX Global Market Analysis Dashboard

# ============================================================
# CELL 5 — IDX GLOBAL MARKET DASHBOARD
# ============================================================

def format_value(value):
    """Format value agar lebih nyaman dibaca."""

    if value is None:
        return "-"

    if isinstance(value, bool):
        return "Ya" if value else "Tidak"

    if isinstance(value, float):
        if abs(value) >= 1000:
            return f"{value:,.2f}"
        return f"{value:.2f}"

    if isinstance(value, int):
        return f"{value:,}"

    return str(value)


def flatten_nested(data, parent_key=""):
    """
    Mengubah nested dictionary menjadi daftar key-value
    tanpa kehilangan informasi penting.
    """

    rows = []

    if isinstance(data, dict):

        for key, value in data.items():

            full_key = f"{parent_key}.{key}" if parent_key else key

            if isinstance(value, dict):

                rows.extend(
                    flatten_nested(
                        value,
                        full_key
                    )
                )

            elif isinstance(value, list):

                if len(value) == 0:

                    rows.append(
                        (full_key, "[]")
                    )

                else:

                    for i, item in enumerate(value):

                        item_key = f"{full_key}[{i}]"

                        if isinstance(item, (dict, list)):

                            rows.extend(
                                flatten_nested(
                                    item,
                                    item_key
                                )
                            )

                        else:

                            rows.append(
                                (
                                    item_key,
                                    format_value(item)
                                )
                            )

            else:

                rows.append(
                    (
                        full_key,
                        format_value(value)
                    )
                )

    elif isinstance(data, list):

        for i, item in enumerate(data):

            rows.extend(
                flatten_nested(
                    item,
                    f"[{i}]"
                )
            )

    else:

        rows.append(
            (
                parent_key or "value",
                format_value(data)
            )
        )

    return rows


# ============================================================
# FLATTEN DATA
# ============================================================

global_flat = flatten_nested(global_market_data)

indices_flat = flatten_nested(indices_impact_data)


# ============================================================
# DASHBOARD
# ============================================================

print("=" * 110)
print("IDX GLOBAL MARKET ANALYSIS DASHBOARD")
print("=" * 110)


# ============================================================
# GLOBAL MARKET OVERVIEW
# ============================================================

print("\n🌎 GLOBAL MARKET OVERVIEW")
print("-" * 110)

if global_flat:

    print(
        f"Jumlah Informasi : "
        f"{len(global_flat)}"
    )

    print()

    for i, (key, value) in enumerate(global_flat[:40], 1):

        print(
            f"{i:02d}. "
            f"{key:<55} : "
            f"{value}"
        )

    if len(global_flat) > 40:

        print(
            f"\n... dan "
            f"{len(global_flat) - 40} "
            f"informasi lainnya."
        )

else:

    print("Tidak ada data Global Market Overview.")


# ============================================================
# INDICES IMPACT
# ============================================================

print("\n\n📊 GLOBAL INDICES IMPACT")
print("-" * 110)

if indices_flat:

    print(
        f"Jumlah Informasi : "
        f"{len(indices_flat)}"
    )

    print()

    for i, (key, value) in enumerate(indices_flat[:40], 1):

        print(
            f"{i:02d}. "
            f"{key:<55} : "
            f"{value}"
        )

    if len(indices_flat) > 40:

        print(
            f"\n... dan "
            f"{len(indices_flat) - 40} "
            f"informasi lainnya."
        )

else:

    print("Tidak ada data Indices Impact.")


# ============================================================
# RINGKASAN
# ============================================================

print("\n\n" + "=" * 110)
print("RINGKASAN")
print("=" * 110)

print(
    "🌎 Global Market Overview :",
    f"{len(global_flat)} informasi"
    if global_flat
    else "Tidak ada data"
)

print(
    "📊 Global Indices Impact  :",
    f"{len(indices_flat)} informasi"
    if indices_flat
    else "Tidak ada data"
)


print("\nStatus API")
print("-" * 110)

print(
    "Global Market Overview :",
    "✅ Berhasil"
    if global_market_raw is not None
    else "❌ Gagal"
)

print(
    "Indices Impact         :",
    "✅ Berhasil"
    if indices_impact_raw is not None
    else "❌ Gagal"
)


print("\n" + "=" * 110)

print(
    "Selesai diproses :",
    datetime.now().strftime(
        "%d-%m-%Y %H:%M:%S"
    )
)

print("=" * 110)

Cell 5 flattens nested Global Market Overview and Global Indices Impact responses into readable key-value information without discarding nested fields. Numeric and boolean values are also formatted for cleaner output.

The dashboard displays up to 40 pieces of information from each endpoint before generating a final summary with record counts, API status, and processing timestamp.

Final Result

result

After executing all five cells, this project can:

  • Retrieve Global Market Overview data.

  • Retrieve Global Indices Impact data.

  • Handle common RapidAPI errors and request timeouts.

  • Normalize different JSON response structures.

  • Display detailed DataFrames.

  • Preserve nested information through recursive flattening.

  • Generate a readable global market dashboard.

  • Display API processing status and completion time.

Conclusion

This project demonstrates how to build an IDX Global Market Analysis Dashboard using Python and RapidAPI.

By combining Global Market Overview with Global Indices Impact, the notebook provides a structured workflow for retrieving and exploring broader market information through the IDX API.

The project handles API requests, response validation, JSON normalization, nested data processing, detailed previews, and dashboard generation within five cells. This structure makes it a useful foundation for developing broader global market monitoring and automated financial analysis workflows.