Build a Stock Dashboard in Python with Free API [2026 Guide]
A complete, step-by-step tutorial. Build a real-time stock dashboard with Python using Streamlit, Flask+Chart.js, or Dash by Plotly — fetching live data from the MarketLens API. Portfolio tracking, watchlists, technical indicators, and news feed included. Everything runs on the free tier (500 calls/day).
Related guides: Quickstart Guide · API Reference (222+ endpoints) · Build a Stock Screener · Build a Trading Bot · Python SDK
Prerequisites
- Python 3.8+ installed
- pip (comes with Python)
- A free MarketLens API key (500 calls/day, no credit card)
- Basic knowledge of Python and HTML
What you'll build in this tutorial:
Free stock data with 500 calls/day
No credit card required. Use code FOUNDER50 for 50% off Pro when you need higher limits.
Project Setup
Create a project folder and install the dependencies:
mkdir stock-dashboard && cd stock-dashboard
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install flask requests python-dotenv
Create a .env file for your API key:
MARKETLENS_API_KEY=your_api_key_here
Get your free key at marketlens.dev/checkout. See our API reference for all available endpoints.
Fetch Stock Data
Let's create a helper module to fetch data from the MarketLens API. We'll use two endpoints: real-time quotes and historical data.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
BASE_URL = "https://marketlens.dev/api/v1"
API_KEY = os.getenv("MARKETLENS_API_KEY")
HEADERS = {"X-API-Key": API_KEY}
def get_quote(symbol: str) -> dict:
"""Fetch real-time stock quote."""
resp = requests.get(
f"{BASE_URL}/stocks/quote",
headers=HEADERS,
params={"symbol": symbol},
timeout=10,
)
resp.raise_for_status()
return resp.json()
def get_history(symbol: str, range: str = "3mo") -> list:
"""Fetch historical OHLCV data."""
resp = requests.get(
f"{BASE_URL}/stocks/history",
headers=HEADERS,
params={"symbol": symbol, "range": range},
timeout=10,
)
resp.raise_for_status()
return resp.json().get("history", [])
def get_multiple_quotes(symbols: list[str]) -> list[dict]:
"""Fetch quotes for multiple symbols."""
return [get_quote(s) for s in symbols]
This uses the /stocks/quote and /stocks/history endpoints. Check our docs for the full response schemas.
Build the Flask Application
Now let's create the Flask app with routes for the dashboard page and a JSON API endpoint for AJAX updates.
from flask import Flask, render_template, jsonify
from api_client import get_quote, get_history, get_multiple_quotes
app = Flask(__name__)
# Watchlist — customize these symbols
WATCHLIST = ["AAPL", "GOOGL", "MSFT", "TSLA", "AMZN", "NVDA"]
@app.route("/")
def dashboard():
"""Render the main dashboard page."""
quotes = get_multiple_quotes(WATCHLIST)
return render_template("dashboard.html", quotes=quotes, symbols=WATCHLIST)
@app.route("/api/quotes")
def api_quotes():
"""JSON endpoint for AJAX auto-refresh."""
quotes = get_multiple_quotes(WATCHLIST)
return jsonify(quotes)
@app.route("/api/history/<symbol>")
def api_history(symbol):
"""JSON endpoint for chart data."""
history = get_history(symbol, range="3mo")
return jsonify(history)
if __name__ == "__main__":
app.run(debug=True, port=5000)
The dashboard route fetches all quotes on page load. The two API routes serve JSON for the auto-refresh and chart updates via JavaScript.
Add Chart.js Visualizations
Create the HTML template at templates/dashboard.html. This uses Chart.js for line charts and a card grid for the watchlist.
<!DOCTYPE html>
<html>
<head>
<title>Stock Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body class="bg-gray-900 text-white p-8">
<h1 class="text-3xl font-bold mb-6">Stock Dashboard</h1>
<!-- Watchlist Cards -->
<div id="watchlist" class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mb-8">
{% for q in quotes %}
<div class="bg-gray-800 rounded-lg p-4 cursor-pointer hover:ring-2 hover:ring-green-500"
onclick="loadChart('{{ q.symbol }}')">
<div class="text-sm text-gray-400">{{ q.symbol }}</div>
<div class="text-xl font-bold">${{ "%.2f"|format(q.price) }}</div>
<div class="text-sm {{ 'text-green-400' if q.change >= 0 else 'text-red-400' }}">
{{ "%+.2f"|format(q.change) }} ({{ "%+.1f"|format(q.change_percent) }}%)
</div>
</div>
{% endfor %}
</div>
<!-- Price Chart -->
<div class="bg-gray-800 rounded-lg p-6">
<h2 id="chart-title" class="text-xl font-semibold mb-4">AAPL — 3 Month</h2>
<canvas id="priceChart" height="100"></canvas>
</div>
<script>
let chart = null;
async function loadChart(symbol) {
const resp = await fetch(`/api/history/${symbol}`);
const data = await resp.json();
const labels = data.map(d => d.date);
const prices = data.map(d => d.close);
document.getElementById('chart-title').textContent =
`${symbol} — 3 Month`;
if (chart) chart.destroy();
chart = new Chart(document.getElementById('priceChart'), {
type: 'line',
data: {
labels,
datasets: [{
label: symbol,
data: prices,
borderColor: '#10b981',
backgroundColor: 'rgba(16,185,129,0.1)',
fill: true, tension: 0.3,
}],
},
options: {
responsive: true,
plugins: { legend: { display: false } },
scales: {
x: { ticks: { color: '#94a3b8' }, grid: { color: '#1e293b' } },
y: { ticks: { color: '#94a3b8', callback: v => '$' + v },
grid: { color: '#1e293b' } },
},
},
});
}
// Load AAPL chart on page load
loadChart('AAPL');
</script>
</body>
</html>
Click any stock card to load its 3-month chart. The Chart.js line chart auto-scales and displays the green MarketLens brand color.
Add Auto-Refresh
Add this script to your dashboard template to poll for new quotes every 30 seconds. This keeps prices live without a full page reload.
// Auto-refresh quotes every 30 seconds
async function refreshQuotes() {
try {
const resp = await fetch('/api/quotes');
const quotes = await resp.json();
const cards = document.getElementById('watchlist').children;
quotes.forEach((q, i) => {
if (cards[i]) {
const priceEl = cards[i].querySelector('.text-xl');
const changeEl = cards[i].querySelector('.text-sm:last-child');
priceEl.textContent = `$${q.price.toFixed(2)}`;
const color = q.change >= 0 ? 'text-green-400' : 'text-red-400';
changeEl.className = `text-sm ${color}`;
changeEl.textContent =
`${q.change >= 0 ? '+' : ''}${q.change.toFixed(2)} ` +
`(${q.change_percent >= 0 ? '+' : ''}${q.change_percent.toFixed(1)}%)`;
}
});
} catch (err) {
console.error('Refresh failed:', err);
}
}
// Poll every 30 seconds (uses ~2 API calls/min = ~120/hour)
setInterval(refreshQuotes, 30000);
With 6 stocks refreshing every 30 seconds, you'll use about 12 API calls per minute — well within the free 500/day limit during a standard work session. See our pricing for higher-volume needs.
Why MarketLens vs Yahoo Finance Scraping
Many tutorials suggest scraping Yahoo Finance with yfinance. Here's why that's a bad idea for production dashboards:
| Aspect | Yahoo Finance (yfinance) | MarketLens API |
|---|---|---|
| Stability | Breaks when Yahoo changes HTML | Stable REST API, versioned |
| Rate limits | 429 errors, IP bans | 500 calls/day free, clear limits |
| Legal | Violates Yahoo ToS | Fully licensed data |
| Data scope | Stocks only | Stocks + crypto + forex + economic + predictions |
| Response time | 1-5 seconds | <200ms (cached) |
| Documentation | Undocumented scraping | 222+ documented endpoints |
Deploy with Gunicorn
For production, use gunicorn instead of Flask's built-in server:
pip install gunicorn
# Run with 2 workers
gunicorn app:app --bind 0.0.0.0:8000 --workers 2
# Or create a Procfile for Render.com / Railway
echo "web: gunicorn app:app --bind 0.0.0.0:\$PORT --workers 2" > Procfile
Deploy to Render.com free tier, Railway, or any VPS. Set MARKETLENS_API_KEY as an environment variable. Need more calls? Pro plans start at $29/mo.
Build a Streamlit Dashboard (Fastest Approach)
Streamlit is the fastest way to build a stock dashboard in Python — you can have a full-featured dashboard in under 100 lines. No HTML, no CSS, no JavaScript required.
pip install streamlit requests plotly pandas
import streamlit as st
import requests
import plotly.graph_objects as go
import pandas as pd
API_KEY = st.secrets.get("MARKETLENS_API_KEY", "your_api_key_here")
BASE = "https://marketlens.dev/api/v1"
HEADERS = {"X-API-Key": API_KEY}
st.set_page_config(page_title="Stock Dashboard", layout="wide")
st.title("📈 Real-Time Stock Dashboard")
# Sidebar — Watchlist
st.sidebar.header("Watchlist")
symbols = st.sidebar.text_input("Symbols (comma-separated)", "AAPL,GOOGL,MSFT,TSLA,NVDA")
symbol_list = [s.strip().upper() for s in symbols.split(",")]
# Fetch quotes for all symbols
col1, col2, col3, col4, col5 = st.columns(5)
cols = [col1, col2, col3, col4, col5]
for i, sym in enumerate(symbol_list[:5]):
resp = requests.get(f"{BASE}/stocks/{sym}/quote", headers=HEADERS, timeout=10)
if resp.ok:
q = resp.json().get("data", {})
price = q.get("price", 0)
change = q.get("change_percent", 0)
with cols[i]:
st.metric(sym, f"${price:.2f}", f"{change:+.2f}%")
# Chart — select a symbol
selected = st.selectbox("Select stock for chart:", symbol_list)
# Fetch historical data
resp = requests.get(f"{BASE}/stocks/{selected}/historical",
headers=HEADERS,
params={"range": "3mo", "interval": "1d"},
timeout=10)
if resp.ok:
data = resp.json().get("data", {}).get("prices", [])
df = pd.DataFrame(data)
fig = go.Figure(data=[go.Candlestick(
x=df["date"], open=df["open"], high=df["high"],
low=df["low"], close=df["close"]
)])
fig.update_layout(title=f"{selected} — 3 Month",
template="plotly_dark",
xaxis_rangeslider_visible=False)
st.plotly_chart(fig, use_container_width=True)
# Auto-refresh every 30 seconds
if st.button("🔄 Refresh Data"):
st.rerun()
Run it:
streamlit run streamlit_dashboard.py
Deploy to Streamlit Cloud for free — just connect your GitHub repo and add MARKETLENS_API_KEY to secrets.
Alternative: Dash by Plotly
Dash is ideal if you want interactive Plotly charts with callbacks. Here's a minimal dashboard:
pip install dash requests pandas
from dash import Dash, html, dcc, callback, Output, Input
import plotly.graph_objects as go
import requests
import pandas as pd
API_KEY = "your_api_key_here"
BASE = "https://marketlens.dev/api/v1"
HEADERS = {"X-API-Key": API_KEY}
app = Dash(__name__)
app.layout = html.Div([
html.H1("Stock Dashboard", style={"color": "white"}),
dcc.Dropdown(
id="symbol-dropdown",
options=[{"label": s, "value": s}
for s in ["AAPL", "GOOGL", "MSFT", "TSLA", "NVDA"]],
value="AAPL",
style={"width": "200px", "marginBottom": "20px"}
),
dcc.Graph(id="price-chart"),
dcc.Interval(id="refresh", interval=30*1000, n_intervals=0)
], style={"backgroundColor": "#0f172a", "padding": "2rem"})
@callback(Output("price-chart", "figure"),
Input("symbol-dropdown", "value"),
Input("refresh", "n_intervals"))
def update_chart(symbol, _):
resp = requests.get(f"{BASE}/stocks/{symbol}/historical",
headers=HEADERS,
params={"range": "3mo", "interval": "1d"})
data = resp.json().get("data", {}).get("prices", [])
df = pd.DataFrame(data)
fig = go.Figure(data=[go.Candlestick(
x=df["date"], open=df["open"], high=df["high"],
low=df["low"], close=df["close"]
)])
fig.update_layout(title=f"{symbol} — 3 Month",
template="plotly_dark",
xaxis_rangeslider_visible=False)
return fig
if __name__ == "__main__":
app.run(debug=True, port=8050)
Dash auto-refreshes every 30 seconds via dcc.Interval. The callback pattern makes it easy to add more panels and controls.
Need more API calls for your dashboard?
Add Portfolio, Watchlist, News & Technical Indicators
Here are the key features to make your dashboard production-ready. Each uses a different MarketLens API endpoint:
Portfolio Tracking
def get_portfolio_value(holdings):
"""Calculate portfolio value and P&L.
holdings = [{"symbol": "AAPL", "shares": 10, "cost_basis": 150.00}, ...]
"""
total_value = 0
total_cost = 0
results = []
for h in holdings:
resp = requests.get(f"{BASE}/stocks/{h['symbol']}/quote",
headers=HEADERS, timeout=10)
quote = resp.json().get("data", {})
price = quote.get("price", 0)
value = price * h["shares"]
cost = h["cost_basis"] * h["shares"]
pnl = value - cost
pnl_pct = (pnl / cost * 100) if cost else 0
results.append({
"symbol": h["symbol"],
"shares": h["shares"],
"price": price,
"value": value,
"pnl": pnl,
"pnl_pct": pnl_pct
})
total_value += value
total_cost += cost
return {
"positions": results,
"total_value": total_value,
"total_pnl": total_value - total_cost,
"total_pnl_pct": ((total_value - total_cost) / total_cost * 100) if total_cost else 0
}
Technical Indicators
def get_technical_indicators(symbol):
"""Fetch RSI, MACD, SMA, EMA, Bollinger Bands, ATR in one call."""
resp = requests.get(f"{BASE}/technical/{symbol}/summary",
headers=HEADERS, timeout=10)
data = resp.json().get("data", {})
# Display key signals
rsi = data.get("rsi", {}).get("value", "N/A")
macd = data.get("macd", {})
signal = "Bullish" if macd.get("histogram", 0) > 0 else "Bearish"
sma_20 = data.get("sma", {}).get("sma_20", "N/A")
bb = data.get("bollinger_bands", {})
return {
"rsi": rsi,
"rsi_signal": "Oversold" if rsi < 30 else "Overbought" if rsi > 70 else "Neutral",
"macd_signal": signal,
"sma_20": sma_20,
"bb_upper": bb.get("upper", "N/A"),
"bb_lower": bb.get("lower", "N/A")
}
# In Streamlit:
# indicators = get_technical_indicators("AAPL")
# st.metric("RSI", f"{indicators['rsi']:.1f}", indicators['rsi_signal'])
News Feed
def get_stock_news(symbol, limit=5):
"""Fetch latest news for a stock."""
resp = requests.get(f"{BASE}/news/{symbol}",
headers=HEADERS,
params={"limit": limit},
timeout=10)
articles = resp.json().get("data", {}).get("articles", [])
return articles
# In Streamlit:
# news = get_stock_news("AAPL")
# for article in news:
# st.markdown(f"**[{article['title']}]({article['url']})**")
# st.caption(f"{article['source']} — {article['published_at']}")
Dashboard Mockup — What You'll Build
This mockup shows the completed dashboard with watchlist cards, candlestick chart, portfolio tracking, and technical indicators — all powered by the MarketLens API.
Live API Demo — Try It Now
Click any button to fetch real data from the MarketLens API. See the endpoints your dashboard will call:
// Click a button above to see live API response
Why MarketLens vs yfinance / Alpha Vantage for Dashboards
Here's how MarketLens compares for building Python stock dashboards:
| Feature | MarketLens | yfinance | Alpha Vantage |
|---|---|---|---|
| Total Endpoints | 222+ | ~15 methods | ~25 endpoints |
| Stability | Stable REST API, versioned | Breaks when Yahoo changes HTML | Stable API |
| Free Tier | 500 calls/day | Unlimited (unofficial) | 25 calls/day |
| Real-time Data | Yes — 5 data sources | Delayed 15min | Yes (premium) |
| Technical Indicators | Built-in (RSI, MACD, BB, ATR) | None — build your own | Yes (limited free) |
| Backtesting | Built-in — single API call | None | None |
| Response Time | <200ms (cached) | 1-5 seconds | ~500ms |
| Pro Pricing | $29/mo (10K/day) | Free (may break) | $49.99/mo |
Get the Free Stock Dashboard Starter Kit
Complete starter project with Flask, Streamlit, and Dash dashboards. Plug in your API key and run.
Check your inbox — starter kit is on its way!
Start Building Your Dashboard for Free
Use code FOUNDER50 for 50% off forever via GitHub Sponsors
Ready to Build Your Stock Dashboard?
Get your API key in 10 seconds. 500 calls/day, 222+ endpoints for stocks, forex, crypto, and more.
Frequently Asked Questions
Yes. The free tier gives you 500 API calls per day — enough to update 10 stocks every 5 minutes for the entire trading day. No credit card required. Pro is $29/month for 10,000 calls/day.
Streamlit is fastest to build (under 100 lines). Flask+Chart.js gives you full frontend control. Dash by Plotly is best for interactive financial charts with callbacks. This tutorial covers all three.
Yes. This tutorial shows three approaches: Flask with Chart.js auto-refresh (polling every 30s), Streamlit with st.rerun() for live updates, and Dash with dcc.Interval callbacks. All use the MarketLens API for real-time data.
yfinance scrapes Yahoo Finance, which breaks frequently, returns 429 errors, and violates Yahoo's ToS. MarketLens provides a stable REST API with 222+ endpoints, consistent JSON, built-in backtesting, technical indicators, and portfolio tracking.
Use the MarketLens /api/v1/stocks/{symbol}/quote endpoint to fetch real-time prices for your holdings, then calculate total value, daily P&L, and allocation. This tutorial includes complete code for portfolio tracking.
MarketLens provides a /api/v1/technical/{symbol}/summary endpoint that returns SMA, EMA, RSI, MACD, Bollinger Bands, and ATR in a single call. Display these as metrics in your Streamlit sidebar or Flask template.
For Flask: use gunicorn with 2-4 workers on Render.com or Railway. For Streamlit: deploy to Streamlit Cloud (free) with one click. Set MARKETLENS_API_KEY as an environment variable.
Free tier: 500 calls/day (no credit card). Pro: $29/month for 10,000 calls/day. Enterprise: $99/month for 50,000 calls/day. Use code FOUNDER50 for 50% off via GitHub Sponsors.
Related Articles
Build a Stock Screener in Python
Value, growth, momentum, dividend screening strategies.
TutorialBacktest Trading Strategies in Python
SMA, RSI, MACD, Bollinger Bands backtesting.
ComparisonAlpha Vantage vs MarketLens
Complete 2026 comparison guide.
TutorialBuild a Trading Bot with Python
Automated trading strategies in 2026.