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

Financial Modeling Prep API +
Stock Charts

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

Try It Live

Interactive Demo

Enter your FMP 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 FMP when you change timeframes.

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

Implementation Guide

Everything you need to get FMP data into a chart

01 Get Your FMP API Key

Head to financialmodelingprep.com/register and create a free account. Once logged in, your API key is displayed on the dashboard. The free tier gives you 250 API calls per day with access to historical stock data, company fundamentals, and more. See the full API docs for all available endpoints.

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 FMP

FMP uses two different endpoints depending on the timeframe you need. For intraday data (1min through 4hour), use the historical chart endpoint. For daily and longer timeframes, use the historical price full endpoint. Both work directly in the browser — FMP supports CORS on their REST API.

JavaScript — Intraday Endpoint
const API_KEY = 'YOUR_FMP_API_KEY';
const ticker  = 'AAPL';
const from    = '2026-03-06';   // YYYY-MM-DD
const to      = '2026-03-11';

// Intraday: 1min, 5min, 15min, 30min, 1hour, 4hour
const url = `https://financialmodelingprep.com/stable/historical-chart/5min?symbol=${ticker}&from=${from}&to=${to}&apikey=${API_KEY}`;

const response = await fetch(url);
const json = await response.json();
// json is a FLAT ARRAY: [ { date, open, high, low, close, volume }, ... ]
JavaScript — Daily Endpoint
// Daily / full history
const url = `https://financialmodelingprep.com/stable/historical-price-eod/full?symbol=${ticker}&from=${from}&to=${to}&apikey=${API_KEY}`;

const response = await fetch(url);
const json = await response.json();
// json is an OBJECT: { symbol: "AAPL", historical: [ { date, open, high, ... }, ... ] }

The intraday endpoint returns a flat JSON array:

FMP Intraday Response
[
  { "date": "2026-03-11 16:00:00", "open": 260.14, "high": 260.50, "low": 259.80, "close": 260.25, "volume": 14523 },
  { "date": "2026-03-11 15:55:00", "open": 260.00, "high": 260.30, "low": 259.70, "close": 260.10, "volume": 12345 },
  ...
]

The daily endpoint returns an object with a historical array:

FMP Daily Response
{
  "symbol": "AAPL",
  "historical": [
    { "date": "2026-03-11", "open": 260.14, "high": 262.48, "low": 258.90, "close": 261.35, "volume": 45123456, "adjClose": 261.35, ... },
    { "date": "2026-03-10", "open": 258.50, "high": 260.20, "low": 257.10, "close": 259.80, "volume": 38456789, ... },
    ...
  ]
}
FMP Field Description Chart Field
dateDate/time stringt (ms) / date
openOpen price (number)open
highHigh price (number)high
lowLow price (number)low
closeClose price (number)close
volumeTrading volume (number)volume
Two things to watch for: (1) Data is returned in reverse chronological order (newest first) — you must reverse it before passing to the chart. (2) Intraday and daily endpoints return different response formats (flat array vs. object with historical key) — your code must handle both.
Unlike Alpha Vantage or Twelve Data, FMP values are already numbers (not strings), so no parseFloat() is needed. FMP also has no weekly or monthly endpoints — for those timeframes, fetch daily data with a wider date range.
04 Transform the API Response

Use a single transform function that handles both intraday and daily response formats. The isIntraday flag determines whether to treat the response as a flat array or extract the historical property. In both cases, reverse the data to chronological order:

JavaScript
function transformFMPData(json, isIntraday) {
    var arr = isIntraday ? json : json.historical;
    if (!arr || arr.length === 0) return [];

    // Reverse from newest-first to oldest-first (chronological)
    return arr.slice().reverse().map(function(bar) {
        // Intraday dates include time: "2026-03-11 16:00:00"
        // Daily dates are date-only: "2026-03-11"
        var ts = bar.date.includes(' ')
            ? new Date(bar.date).getTime()
            : new Date(bar.date + 'T12:00:00').getTime();
        return {
            t: ts, date: bar.date,
            open: bar.open, high: bar.high,
            low: bar.low, close: bar.close, volume: bar.volume
        };
    });
}

var chartData = transformFMPData(json, true);  // for intraday
var chartData = transformFMPData(json, false); // for daily
FMP dates are already in Eastern Time format, so we use bar.date directly. The .slice().reverse() creates a new reversed array without mutating the original response data.
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. FMP requires building different URLs for intraday vs. daily timeframes:

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

// Helper: format Date as YYYY-MM-DD
function fmtDate(d) { return d.toISOString().split('T')[0]; }

// Map chart timeframes to FMP params
function mapTimeframeToFMPParams(tf) {
    var map = {
        '1min':   { type: 'intraday', interval: '1min',  days: 5 },
        '2min':   { type: 'intraday', interval: '5min',  days: 5 },
        '5min':   { type: 'intraday', interval: '5min',  days: 5 },
        '15min':  { type: 'intraday', interval: '15min', days: 10 },
        '30min':  { type: 'intraday', interval: '30min', days: 15 },
        '1hour':  { type: 'intraday', interval: '1hour', days: 30 },
        '60min':  { type: 'intraday', interval: '1hour', days: 30 },
        '4hour':  { type: 'intraday', interval: '4hour', days: 90 },
        '1day':   { type: 'daily', interval: null, days: 365 },
        '1week':  { type: 'daily', interval: null, days: 1095 },
        '1W':     { type: 'daily', interval: null, days: 1095 },
        '1month': { type: 'daily', interval: null, days: 1825 },
        '1M':     { type: 'daily', interval: null, days: 1825 }
    };
    return map[tf] || map['1day'];
}

// Build the correct URL based on timeframe type
function buildFMPUrl(ticker, p, apiKey, fromDate, toDate) {
    if (p.type === 'intraday') {
        return 'https://financialmodelingprep.com/stable/historical-chart/'
            + p.interval + '?symbol=' + ticker
            + '&from=' + fromDate + '&to=' + toDate + '&apikey=' + apiKey;
    } else {
        return 'https://financialmodelingprep.com/stable/historical-price-eod/full?symbol=' + ticker
            + '&from=' + fromDate + '&to=' + toDate + '&apikey=' + apiKey;
    }
}

// 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 = mapTimeframeToFMPParams(chart.timeframe || '1day');
        var earliest = new Date(earliestTimestamp);
        var toDate = fmtDate(new Date(earliest.getTime() - 86400000));
        var fromDate = fmtDate(new Date(earliest.getTime() - p.days * 86400000));
        var url = buildFMPUrl(ticker, p, API_KEY, fromDate, toDate);
        var resp = await fetch(url);
        var json = await resp.json();
        var isIntraday = p.type === 'intraday';
        var arr = isIntraday ? json : (json.historical || []);
        if (Array.isArray(arr) && arr.length > 0) {
            var older = transformFMPData(json, isIntraday);
            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 FMP. The onReachingStart callback fires when the user pans to the beginning of loaded data, triggering a fetch of older candles that get prepended seamlessly. Error handling checks for Array.isArray(json) on intraday responses and json.historical on daily responses. 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_FMP_API_KEY with your key, open it in a browser, and you'll have a working stock chart in seconds.

fmp-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>Financial Modeling Prep + JavaScript 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>Financial Modeling Prep + JavaScript 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 FMP 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;

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

        function mapTimeframeToFMPParams(tf) {
            var map = {
                '1min':   { type: 'intraday', interval: '1min',  days: 5 },
                '2min':   { type: 'intraday', interval: '5min',  days: 5 },
                '5min':   { type: 'intraday', interval: '5min',  days: 5 },
                '15min':  { type: 'intraday', interval: '15min', days: 10 },
                '30min':  { type: 'intraday', interval: '30min', days: 15 },
                '1hour':  { type: 'intraday', interval: '1hour', days: 30 },
                '60min':  { type: 'intraday', interval: '1hour', days: 30 },
                '4hour':  { type: 'intraday', interval: '4hour', days: 90 },
                '1day':   { type: 'daily', interval: null, days: 365 },
                '1week':  { type: 'daily', interval: null, days: 1095 },
                '1W':     { type: 'daily', interval: null, days: 1095 },
                '1month': { type: 'daily', interval: null, days: 1825 },
                '1M':     { type: 'daily', interval: null, days: 1825 }
            };
            return map[tf] || map['1day'];
        }

        function buildFMPUrl(ticker, p, apiKey, fromDate, toDate) {
            if (p.type === 'intraday') {
                return 'https://financialmodelingprep.com/stable/historical-chart/'
                    + p.interval + '?symbol=' + ticker
                    + '&from=' + fromDate + '&to=' + toDate + '&apikey=' + apiKey;
            } else {
                return 'https://financialmodelingprep.com/stable/historical-price-eod/full?symbol=' + ticker
                    + '&from=' + fromDate + '&to=' + toDate + '&apikey=' + apiKey;
            }
        }

        function transformFMPData(json, isIntraday) {
            var arr = isIntraday ? json : json.historical;
            if (!arr || arr.length === 0) return [];
            return arr.slice().reverse().map(function(bar) {
                var ts = bar.date.includes(' ')
                    ? new Date(bar.date).getTime()
                    : new Date(bar.date + 'T12:00:00').getTime();
                return {
                    t: ts, date: bar.date,
                    open: bar.open, high: bar.high,
                    low: bar.low, close: bar.close, volume: bar.volume
                };
            });
        }

        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 FMP 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 = mapTimeframeToFMPParams(timeframe);
            var isIntraday = p.type === 'intraday';

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

            var now = new Date();
            var toDate = fmtDate(now);
            var fromDate = fmtDate(new Date(now.getTime() - p.days * 86400000));
            var url = buildFMPUrl(ticker, p, apiKey, fromDate, toDate);

            try {
                var resp = await fetch(url);
                var json = await resp.json();

                // Error handling: check both response formats
                if (isIntraday) {
                    if (!Array.isArray(json) || json.length === 0) {
                        status.textContent = json['Error Message'] || typeof json === 'string'
                            ? 'Error: ' + (json['Error Message'] || json)
                            : 'No intraday data for ' + ticker + '.';
                        status.className = 'error'; return;
                    }
                } else {
                    if (!json.historical || json.historical.length === 0) {
                        status.textContent = json['Error Message'] || typeof json === 'string'
                            ? 'Error: ' + (json['Error Message'] || json)
                            : 'No daily data for ' + ticker + '.';
                        status.className = 'error'; return;
                    }
                }

                var chartData = transformFMPData(json, isIntraday);
                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';
            }
        }

        function setupTimeframeMonitor() {
            if (timeframeCheckInterval) clearInterval(timeframeCheckInterval);
            timeframeCheckInterval = setInterval(function() {
                if (chart && chart.timeframe !== previousTimeframe) {
                    previousTimeframe = chart.timeframe;
                    loadChart();
                }
            }, 500);
        }

        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 = mapTimeframeToFMPParams(chart.timeframe || '1day');
                var isIntraday = p.type === 'intraday';
                var earliest = new Date(earliestTimestamp);
                var toDate = fmtDate(new Date(earliest.getTime() - 86400000));
                var fromDate = fmtDate(new Date(earliest.getTime() - p.days * 86400000));
                var url = buildFMPUrl(currentTicker, p, apiKey, fromDate, toDate);
                var resp = await fetch(url);
                var json = await resp.json();
                var arr = isIntraday ? json : (json.historical || []);
                if (Array.isArray(arr) && arr.length > 0) {
                    var older = transformFMPData(json, isIntraday);
                    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 FMP API key, and start charting.

See more tutorials: JavaScript Stock Charts Documentation  |  Twelve Data API Tutorial  |  Alpha Vantage API Tutorial

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