S&P 500
7,764.25
+14.75 (+0.19%)
Nasdaq
29,508.00
-107.00 (-0.36%)
Dow
54,654.00
+160.00 (+0.29%)
Bitcoin
64,786.20
+679.45 (+1.06%)
simul8or trading simulator logo

Massive.com API +
Stock Charts

Massive.com is formerly Polygon.io

Fetch real-time and historical stock data from Massive.com and render interactive candlestick charts in the browser. 100% client-side, zero backend required.

Try It Live

Interactive Demo

Enter your Massive.com API key and a ticker symbol to fetch real market data. Use the chart's built-in timeframe selector to switch between intraday, daily, weekly, and monthly views — new data is automatically fetched from Massive.com when you change timeframes.

Chart loaded with sample AAPL daily data. Enter your API key above to fetch live data from Massive.com. Use the chart's built-in timeframe selector to switch views.
Don't have an API key? Sign up for a free Massive.com account at massive.com.
Step by Step

Implementation Guide

Everything you need to get Massive.com data into a chart

01 Get Your Massive.com API Key

Head to massive.com and create a free account. Once logged in, your API key is displayed on the dashboard.

02 Include the Chart Library

Add the JavaScript Stock Charts library to your HTML page via CDN. It's zero-dependency — just three script tags and one CSS link. No downloads or installs required.

HTML
<!-- CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/day-trading-simulator/javascript-stock-charts@main/stock-chart.css">

<!-- Chart container -->
<div id="chart" style="width: 100%; height: 500px;"></div>

<!-- JS (order matters) -->
<script src="https://cdn.jsdelivr.net/gh/day-trading-simulator/javascript-stock-charts@main/indicators.js"></script>
<script src="https://cdn.jsdelivr.net/gh/day-trading-simulator/javascript-stock-charts@main/patterns.js"></script>
<script src="https://cdn.jsdelivr.net/gh/day-trading-simulator/javascript-stock-charts@main/stock-chart.js"></script>
No downloads needed — the library is served via jsDelivr CDN directly from GitHub. Just include the script tags and you're ready to go.
03 Fetch Data from Massive.com

Use the Aggregates (Bars) endpoint to fetch OHLCV data. This works directly in the browser — Massive.com supports CORS on their REST API.

JavaScript
const API_KEY = 'YOUR_MASSIVE_API_KEY';
const ticker  = 'AAPL';
const from    = '2024-01-01';
const to      = '2025-01-01';

const url = `https://api.massive.com/v2/aggs/ticker/${ticker}/range/1/day/${from}/${to}?adjusted=true&sort=asc&apiKey=${API_KEY}`;

const response = await fetch(url);
const json = await response.json();

Massive.com returns data in this format:

Massive.com Response
{
  "ticker": "AAPL",
  "resultsCount": 251,
  "results": [
    {
      "o": 185.33,         // open
      "h": 186.10,         // high
      "l": 183.79,         // low
      "c": 185.56,         // close
      "v": 45123456,       // volume
      "t": 1704067200000   // timestamp (ms)
    },
    ...
  ]
}
Massive Field Description Chart Field
oOpen priceopen
hHigh pricehigh
lLow pricelow
cClose priceclose
vVolumevolume
tTimestamp (ms)t
04 Transform the API Response

Map Massive.com's shorthand field names to the chart library's expected format. This is a simple one-liner with .map():

JavaScript
function transformPolygonData(response) {
    return response.results.map(bar => {
        var d = new Date(bar.t);
        var date = d.toLocaleString('en-US', {
            timeZone: 'America/New_York',
            year: 'numeric', month: 'numeric', day: 'numeric',
            hour: '2-digit', minute: '2-digit', hour12: false
        }).replace(',', '');
        return { t: bar.t, date: date, open: bar.o, high: bar.h,
                 low: bar.l, close: bar.c, volume: bar.v };
    });
}

const chartData = transformPolygonData(json);
The Massive.com API returns data sorted by timestamp when you pass sort=asc in the URL, which is exactly what the chart library expects.
05 Render the Chart with Timeframe Switching & Infinite Scroll

Create a StockChart instance with an onReachingStart callback for infinite scroll. Then monitor the chart's built-in timeframe selector to re-fetch data when the user switches timeframes:

JavaScript
var chart = null;
var previousTimeframe = '1day';
var isLoadingMore = false;
var earliestTimestamp = null;
var hasMoreHistory = true;
var timeframeCheckInterval = null;

// Map chart timeframes to Massive.com API parameters
function mapTimeframeToPolygonParams(tf) {
    var map = {
        '1min':  { mult: 1,  span: 'minute', days: 5 },
        '5min':  { mult: 5,  span: 'minute', days: 5 },
        '15min': { mult: 15, span: 'minute', days: 10 },
        '1hour': { mult: 1,  span: 'hour',   days: 30 },
        '1day':  { mult: 1,  span: 'day',    days: 365 },
        '1week': { mult: 1,  span: 'week',   days: 1095 },
        '1month':{ mult: 1,  span: 'month',  days: 1825 }
    };
    return map[tf] || map['1day'];
}

// Initialize chart
chart = new StockChart('chart', {
    data:      chartData,
    ticker:    ticker,
    chartType: 'candlestick',
    darkMode:  true,
    timeframe: '1day',
    useAfterHoursStyling: true,
    onReachingStart: handleReachingStart
});
earliestTimestamp = chartData[0].t;

// Monitor chart's built-in timeframe selector
setupTimeframeMonitor();

function setupTimeframeMonitor() {
    if (timeframeCheckInterval) clearInterval(timeframeCheckInterval);
    timeframeCheckInterval = setInterval(function() {
        if (chart && chart.timeframe !== previousTimeframe) {
            previousTimeframe = chart.timeframe;
            loadChart(); // re-fetch with new timeframe
        }
    }, 500);
}

// Infinite scroll - fetch older data when user pans left
async function handleReachingStart() {
    if (isLoadingMore || !hasMoreHistory || !earliestTimestamp) return;
    isLoadingMore = true;
    try {
        var p = mapTimeframeToPolygonParams(chart.timeframe || '1day');
        var to = new Date(earliestTimestamp - 1);
        var from = new Date(to); from.setDate(from.getDate() - p.days);
        var url = 'https://api.massive.com/v2/aggs/ticker/' + ticker
            + '/range/' + p.mult + '/' + p.span
            + '/' + fmt(from) + '/' + fmt(to)
            + '?adjusted=true&sort=asc&apiKey=' + API_KEY;
        var resp = await fetch(url);
        var json = await resp.json();
        if (json.results && json.results.length > 0) {
            var older = transformPolygonData(json);
            earliestTimestamp = older[0].t;
            chart.prependHistoricalData(older);
        } else {
            hasMoreHistory = false;
            chart.setLoadingHistoricalData(false);
        }
    } catch(e) {
        chart.setLoadingHistoricalData(false);
    } finally {
        isLoadingMore = false;
    }
}

The chart includes a built-in timeframe dropdown — when the user clicks it, we detect the change via setInterval polling on chart.timeframe and automatically re-fetch data from Massive.com. The onReachingStart callback fires when the user pans to the beginning of loaded data, triggering a fetch of older candles that get prepended seamlessly. Technical indicators, pan, zoom, crosshair, volume, and more are all built-in.

Full Example

Complete Working Code

Copy-paste this entire HTML file. Replace YOUR_MASSIVE_API_KEY with your key, open it in a browser, and you'll have a working stock chart in seconds.

massive-stock-chart.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Massive.com + Stock Charts</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/day-trading-simulator/javascript-stock-charts@main/stock-chart.css">
    <style>
        * { box-sizing: border-box; margin: 0; padding: 0; }
        body {
            padding: 20px;
            background: #0a0a0f;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            color: #fff;
        }
        h1 { font-size: 1.4rem; margin-bottom: 4px; }
        .subtitle { color: #888; margin-bottom: 16px; }
        .subtitle a { color: #22d3ee; }
        .controls {
            display: flex; gap: 10px; margin-bottom: 12px;
            flex-wrap: wrap; align-items: flex-end;
        }
        .controls label {
            display: block; font-size: 0.7rem;
            text-transform: uppercase; letter-spacing: 1px;
            color: #888; margin-bottom: 4px;
        }
        .controls input {
            background: #16161f; border: 1px solid #333;
            color: #fff; padding: 8px 12px; font-size: 0.9rem;
            border-radius: 2px;
        }
        .controls button {
            background: #10b981; border: none; color: #fff;
            padding: 8px 24px; font-weight: 700; cursor: pointer;
            border-radius: 2px;
        }
        .controls button:hover { background: #059669; }
        .sc-chart-type-button.d-none { display: inline-flex !important; }
        #status { font-size: 0.85rem; color: #888; margin-bottom: 8px; }
        #status.error { color: #ef4444; }
        #status.success { color: #10b981; }
    </style>
</head>
<body>
    <h1>Massive.com + Stock Charts</h1>
    <p class="subtitle">
        Powered by <a href="https://simul8or.com/Javascript-Stock-Chart.php">JavaScript Stock Charts</a>
    </p>

    <div class="controls">
        <div>
            <label>API Key</label>
            <input type="text" id="apiKey" placeholder="Your Massive.com API key" style="width:280px">
        </div>
        <div>
            <label>Ticker</label>
            <input type="text" id="ticker" value="AAPL" style="width:100px">
        </div>
        <button onclick="loadChart()">Load Chart</button>
    </div>

    <div id="status"></div>
    <div id="chart" style="width:100%; height:500px;"></div>

    <script src="https://cdn.jsdelivr.net/gh/day-trading-simulator/javascript-stock-charts@main/indicators.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/day-trading-simulator/javascript-stock-charts@main/patterns.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/day-trading-simulator/javascript-stock-charts@main/stock-chart.js"></script>
    <script>
        var chart = null;
        var currentTicker = 'AAPL';
        var previousTimeframe = '1day';
        var timeframeCheckInterval = null;
        var isLoadingMore = false;
        var earliestTimestamp = null;
        var hasMoreHistory = true;

        // Map chart timeframes to Massive.com API parameters
        function mapTimeframeToPolygonParams(tf) {
            var map = {
                '1min':   { mult: 1,  span: 'minute', days: 5 },
                '2min':   { mult: 2,  span: 'minute', days: 5 },
                '5min':   { mult: 5,  span: 'minute', days: 5 },
                '15min':  { mult: 15, span: 'minute', days: 10 },
                '30min':  { mult: 30, span: 'minute', days: 15 },
                '1hour':  { mult: 1,  span: 'hour',   days: 30 },
                '60min':  { mult: 1,  span: 'hour',   days: 30 },
                '4hour':  { mult: 4,  span: 'hour',   days: 90 },
                '1day':   { mult: 1,  span: 'day',    days: 365 },
                '1week':  { mult: 1,  span: 'week',   days: 1095 },
                '1W':     { mult: 1,  span: 'week',   days: 1095 },
                '1month': { mult: 1,  span: 'month',  days: 1825 },
                '1M':     { mult: 1,  span: 'month',  days: 1825 }
            };
            return map[tf] || map['1day'];
        }

        function fmt(d) { return d.toISOString().split('T')[0]; }

        function transformPolygonData(json) {
            return json.results.map(function(bar) {
                var d = new Date(bar.t);
                var date = d.toLocaleString('en-US', {
                    timeZone: 'America/New_York',
                    year: 'numeric', month: 'numeric', day: 'numeric',
                    hour: '2-digit', minute: '2-digit', hour12: false
                }).replace(',', '');
                return { t: bar.t, date: date, open: bar.o, high: bar.h,
                         low: bar.l, close: bar.c, volume: bar.v };
            });
        }

        // Fetch data from Massive.com and render chart
        async function loadChart() {
            var apiKey = document.getElementById('apiKey').value.trim();
            var ticker = document.getElementById('ticker').value.trim().toUpperCase();
            var status = document.getElementById('status');

            if (!apiKey) { status.textContent = 'Enter your Massive.com API key.'; status.className = 'error'; return; }
            if (!ticker) { status.textContent = 'Enter a ticker symbol.'; status.className = 'error'; return; }

            var timeframe = (chart && chart.timeframe) ? chart.timeframe : '1day';
            var p = mapTimeframeToPolygonParams(timeframe);

            status.textContent = 'Fetching ' + timeframe + ' data...';
            status.className = '';

            var to = new Date();
            var from = new Date();
            from.setDate(from.getDate() - p.days);

            var url = 'https://api.massive.com/v2/aggs/ticker/' + ticker
                + '/range/' + p.mult + '/' + p.span
                + '/' + fmt(from) + '/' + fmt(to)
                + '?adjusted=true&sort=asc&apiKey=' + apiKey;

            try {
                var resp = await fetch(url);
                var json = await resp.json();
                if (json.status === 'ERROR' || json.status === 'NOT_FOUND') {
                    status.textContent = json.error || json.message || 'Error fetching data.';
                    status.className = 'error'; return;
                }
                if (!json.results || json.results.length === 0) {
                    status.textContent = 'No data for ' + ticker + '.';
                    status.className = 'error'; return;
                }

                var chartData = transformPolygonData(json);
                if (chart) chart.destroy();
                if (timeframeCheckInterval) clearInterval(timeframeCheckInterval);

                currentTicker = ticker;
                chart = new StockChart('chart', {
                    data: chartData, ticker: ticker,
                    chartType: 'candlestick', darkMode: true,
                    timeframe: timeframe,
                    useAfterHoursStyling: true,
                    onReachingStart: handleReachingStart
                });

                isLoadingMore = false;
                hasMoreHistory = true;
                earliestTimestamp = chartData[0].t;
                previousTimeframe = chart.timeframe || timeframe;
                setupTimeframeMonitor();

                status.textContent = '';
                status.className = '';
            } catch (err) {
                status.textContent = 'Network error: ' + err.message;
                status.className = 'error';
            }
        }

        // Monitor chart's built-in timeframe selector for changes
        function setupTimeframeMonitor() {
            if (timeframeCheckInterval) clearInterval(timeframeCheckInterval);
            timeframeCheckInterval = setInterval(function() {
                if (chart && chart.timeframe !== previousTimeframe) {
                    previousTimeframe = chart.timeframe;
                    loadChart();
                }
            }, 500);
        }

        // Infinite scroll - fetch older data when user pans to start
        async function handleReachingStart() {
            if (isLoadingMore || !hasMoreHistory || !earliestTimestamp) return;
            isLoadingMore = true;
            var apiKey = document.getElementById('apiKey').value.trim();
            if (!apiKey) { isLoadingMore = false; return; }
            try {
                var p = mapTimeframeToPolygonParams(chart.timeframe || '1day');
                var to = new Date(earliestTimestamp - 1);
                var from = new Date(to); from.setDate(from.getDate() - p.days);
                var url = 'https://api.massive.com/v2/aggs/ticker/' + currentTicker
                    + '/range/' + p.mult + '/' + p.span
                    + '/' + fmt(from) + '/' + fmt(to)
                    + '?adjusted=true&sort=asc&apiKey=' + apiKey;
                var resp = await fetch(url);
                var json = await resp.json();
                if (json.results && json.results.length > 0) {
                    var older = transformPolygonData(json);
                    earliestTimestamp = older[0].t;
                    chart.prependHistoricalData(older);
                } else {
                    hasMoreHistory = false;
                    chart.setLoadingHistoricalData(false);
                }
            } catch(e) {
                chart.setLoadingHistoricalData(false);
            } finally {
                isLoadingMore = false;
            }
        }
    </script>
</body>
</html>
Important: Never commit your API key to a public repository. For production apps, use environment variables or a server-side proxy.
Get Started

Ready to build your own chart?

Download the library, grab your Massive.com API key, and start charting.

See more tutorials: JavaScript Stock Charts Documentation

Wait, here's a special offer

Complete your purchase now and save over 20%.

$19 $15
One-time payment · Unlimited forever
One-time offer — won't be shown again
Secure checkout · Instant access · 30-Day Money Back Guarantee
Stripe
PayPal
No thanks, maybe later