Build a Stock Screener in Python with Free API [2026 Guide]
A stock screener lets you filter the entire market down to stocks that match your criteria — P/E ratios, revenue growth, RSI signals, dividend yields, or custom technical patterns. In this tutorial, you'll build one from scratch in Python using the MarketLens API, which gives you 222+ endpoints and real-time data from 5 sources.
By the end, you'll have working screeners for 4 different strategies: value investing, growth, momentum, and dividend. We'll also cover how MarketLens compares to alternatives like yfinance, Alpha Vantage, and Polygon.io.
Related: Build a Trading Bot • Alpha Vantage Alternative • Python SDK • Full API Reference
Get your free API key — 500 calls/day
No credit card required. Use code FOUNDER50 for 50% off Pro ($14.50/mo).
What you'll build in this guide:
- Basic stock screener with real-time data
- Value investing screener (P/E, P/B)
- Growth screener (revenue growth)
- Momentum screener (RSI, SMA)
- Dividend screener (yield, payout ratio)
- Live API demo you can try right now
Prerequisites
- Python 3.9 or later
- A free MarketLens API key (500 calls/day on the free tier)
- Basic Python knowledge (functions, dicts, loops)
Install the dependencies:
pip install httpx tabulate
Step 1: Fetch Stock Data from MarketLens
MarketLens provides a dedicated screener endpoint that returns top gainers and losers with price, volume, and change data -- perfect for building a screener.
import httpx
# Replace with your API key from https://marketlens.dev/signup.html
API_KEY = "ml_your_api_key_here"
BASE_URL = "https://marketlens.dev"
headers = {"X-API-Key": API_KEY}
def get_stock_gainers(limit=10):
"""Fetch today's top gaining stocks."""
resp = httpx.get(
f"{BASE_URL}/api/v1/screener/stocks/gainers",
headers=headers,
params={"limit": limit},
)
resp.raise_for_status()
return resp.json()["data"]["gainers"]
def get_stock_losers(limit=10):
"""Fetch today's top losing stocks."""
resp = httpx.get(
f"{BASE_URL}/api/v1/screener/stocks/losers",
headers=headers,
params={"limit": limit},
)
resp.raise_for_status()
return resp.json()["data"]["losers"]
def get_stock_quote(symbol):
"""Fetch a single stock quote."""
resp = httpx.get(
f"{BASE_URL}/api/v1/stocks/{symbol}",
headers=headers,
)
resp.raise_for_status()
return resp.json()["data"]
The /api/v1/screener/stocks/gainers and /api/v1/screener/stocks/losers endpoints return pre-sorted lists of stocks with price, change percentage, volume, and high/low data. Each call uses 2 API credits.
Step 2: Build Custom Screening Filters
The real power of a screener is custom filters. Let's write functions that filter stocks by minimum change, minimum volume, and price range.
def screen_stocks(stocks, min_change_pct=None, min_volume=None,
price_min=None, price_max=None):
"""Filter a list of stocks by custom criteria.
Args:
stocks: List of stock dicts from the screener API.
min_change_pct: Minimum absolute price change (e.g. 2.0 for 2%).
min_volume: Minimum daily volume (e.g. 10_000_000).
price_min: Minimum stock price in USD.
price_max: Maximum stock price in USD.
Returns:
Filtered list of stocks matching all criteria.
"""
results = []
for stock in stocks:
price = stock.get("price") or 0
change = abs(stock.get("change_pct") or 0)
volume = stock.get("volume") or 0
if min_change_pct and change < min_change_pct:
continue
if min_volume and volume < min_volume:
continue
if price_min and price < price_min:
continue
if price_max and price > price_max:
continue
results.append(stock)
return results
This filter function accepts any combination of criteria. Pass only the ones you care about -- the rest default to None and are skipped.
Step 3: Display Screener Results
Let's format the results into a clean table using tabulate.
from tabulate import tabulate
def display_results(stocks, title="Screener Results"):
"""Print stocks in a formatted table."""
rows = []
for s in stocks:
rows.append([
s.get("symbol", "?"),
f"${s.get('price', 0):,.2f}",
f"{s.get('change_pct', 0):+.2f}%",
f"{s.get('volume', 0):,.0f}",
])
print(f"\n{title}")
print("=" * len(title))
print(tabulate(
rows,
headers=["Symbol", "Price", "Change", "Volume"],
tablefmt="simple_grid",
))
Step 4: Put It All Together
Here's the complete screener that fetches today's top movers and filters them by your criteria:
#!/usr/bin/env python3
"""Stock screener using MarketLens API."""
import httpx
from tabulate import tabulate
API_KEY = "ml_your_api_key_here"
BASE = "https://marketlens.dev"
HEADERS = {"X-API-Key": API_KEY}
def fetch(path, params=None):
r = httpx.get(f"{BASE}{path}", headers=HEADERS, params=params)
r.raise_for_status()
return r.json()["data"]
def main():
# Fetch top gainers
gainers = fetch("/api/v1/screener/stocks/gainers", {"limit": 25})
gainers = gainers["gainers"]
# Filter: gains > 1%, volume > 5M, price $10-$500
filtered = [
s for s in gainers
if (s.get("change_pct") or 0) > 1.0
and (s.get("volume") or 0) > 5_000_000
and 10 <= (s.get("price") or 0) <= 500
]
# Display
rows = [
[s["symbol"], f"${s['price']:,.2f}",
f"{s['change_pct']:+.2f}%", f"{s['volume']:,.0f}"]
for s in filtered
]
print("Top Gainers (>1% change, >5M volume, $10-$500)")
print(tabulate(rows, headers=["Symbol", "Price", "Change", "Volume"],
tablefmt="simple_grid"))
if __name__ == "__main__":
main()
Sample output:
Top Gainers (>1% change, >5M volume, $10-$500)
+---------+---------+----------+---------------+
| Symbol | Price | Change | Volume |
+---------+---------+----------+---------------+
| NVDA | $148.90 | +3.21% | 41,500,000 |
| AMD | $162.40 | +2.87% | 28,300,000 |
| TSLA | $285.60 | +1.94% | 52,800,000 |
| NFLX | $438.20 | +1.52% | 8,900,000 |
| META | $412.30 | +1.28% | 15,800,000 |
+---------+---------+----------+---------------+
Step 5: Advanced Screening Strategies
Now that you have the basics, let's implement 4 real-world screening strategies that professional traders use. Each uses different MarketLens API endpoints.
Strategy 1: Value Investing Screener (P/E & P/B)
Value investors look for undervalued stocks — low P/E ratio, low P/B ratio, and strong fundamentals. Warren Buffett style. Use the /api/v1/stocks/{symbol}/profile endpoint for fundamental data.
import requests
API_KEY = "ml_your_api_key_here"
BASE = "https://marketlens.dev"
HEADERS = {"X-API-Key": API_KEY}
def value_screen(symbols, max_pe=15, max_pb=2.0):
"""Screen for undervalued stocks using P/E and P/B ratios.
Args:
symbols: List of stock symbols to screen.
max_pe: Maximum Price-to-Earnings ratio (lower = cheaper).
max_pb: Maximum Price-to-Book ratio (lower = cheaper).
Returns:
List of stocks passing the value screen.
"""
results = []
for sym in symbols:
resp = requests.get(
f"{BASE}/api/v1/stocks/{sym}/profile",
headers=HEADERS,
)
if resp.status_code != 200:
continue
data = resp.json().get("data", {})
pe = data.get("pe_ratio")
pb = data.get("pb_ratio")
if pe and pb and pe < max_pe and pb < max_pb:
results.append({
"symbol": sym,
"pe_ratio": pe,
"pb_ratio": pb,
"price": data.get("price"),
"name": data.get("name"),
})
return results
# Screen the S&P 500 heavyweights
watchlist = ["AAPL", "MSFT", "GOOG", "JPM", "BAC",
"JNJ", "XOM", "PFE", "VZ", "INTC"]
value_stocks = value_screen(watchlist, max_pe=15, max_pb=2.0)
for s in value_stocks:
print(f"{s['symbol']} - P/E: {s['pe_ratio']:.1f}, P/B: {s['pb_ratio']:.2f}, Price: ${s['price']:,.2f}")
Strategy 2: Growth Screener (Revenue Growth)
Growth investors want stocks with rapidly increasing revenue. Use the profile endpoint to check revenue growth rates and earnings trends.
def growth_screen(symbols, min_revenue_growth=20):
"""Screen for high-growth stocks.
Args:
symbols: List of stock symbols.
min_revenue_growth: Minimum YoY revenue growth % (e.g. 20 = 20%).
"""
results = []
for sym in symbols:
resp = requests.get(
f"{BASE}/api/v1/stocks/{sym}/profile",
headers=HEADERS,
)
if resp.status_code != 200:
continue
data = resp.json().get("data", {})
rev_growth = data.get("revenue_growth_yoy")
if rev_growth and rev_growth >= min_revenue_growth:
results.append({
"symbol": sym,
"revenue_growth": rev_growth,
"price": data.get("price"),
"market_cap": data.get("market_cap"),
})
return sorted(results, key=lambda x: x["revenue_growth"], reverse=True)
# Find high-growth tech stocks
tech = ["NVDA", "AMD", "SMCI", "PLTR", "CRWD", "NET", "SNOW"]
growers = growth_screen(tech, min_revenue_growth=25)
for s in growers:
print(f"{s['symbol']} - Revenue Growth: {s['revenue_growth']:.1f}%")
Strategy 3: Momentum Screener (RSI & Moving Averages)
Momentum traders buy stocks that are trending upward. This strategy uses RSI(14) and SMA(20) from the /api/v1/technical/{symbol}/summary endpoint.
def momentum_screen(symbols, min_rsi=50, max_rsi=70):
"""Screen for stocks with bullish momentum.
Looks for RSI between 50-70 (strong but not overbought)
and price above the 20-day SMA (uptrend).
"""
results = []
for sym in symbols:
# Get current price
quote = requests.get(
f"{BASE}/api/v1/stocks/{sym}",
headers=HEADERS,
).json().get("data", {})
# Get technical indicators
tech = requests.get(
f"{BASE}/api/v1/technical/{sym}/summary",
headers=HEADERS,
).json().get("data", {})
indicators = tech.get("indicators", {})
rsi = indicators.get("rsi_14")
sma20 = indicators.get("sma_20")
price = quote.get("price", 0)
if rsi and sma20 and min_rsi <= rsi <= max_rsi and price > sma20:
results.append({
"symbol": sym,
"price": price,
"rsi": rsi,
"sma_20": sma20,
"above_sma": f"{((price - sma20) / sma20 * 100):.1f}%",
"signals": tech.get("signals", []),
})
return results
# Run momentum scan
momentum = momentum_screen(
["AAPL", "MSFT", "NVDA", "TSLA", "META", "AMZN", "GOOG"]
)
for s in momentum:
print(f"{s['symbol']}: RSI={s['rsi']:.1f}, Above SMA20 by {s['above_sma']}")
Strategy 4: Dividend Screener (Yield & Payout Ratio)
Income investors want high-yield stocks with sustainable dividends. A payout ratio below 60% suggests the dividend is well-covered by earnings.
def dividend_screen(symbols, min_yield=3.0, max_payout=60):
"""Screen for sustainable high-dividend stocks.
Args:
symbols: List of stock symbols.
min_yield: Minimum dividend yield % (e.g. 3.0 = 3%).
max_payout: Maximum payout ratio % (lower = more sustainable).
"""
results = []
for sym in symbols:
resp = requests.get(
f"{BASE}/api/v1/stocks/{sym}/profile",
headers=HEADERS,
)
if resp.status_code != 200:
continue
data = resp.json().get("data", {})
div_yield = data.get("dividend_yield")
payout = data.get("payout_ratio")
if div_yield and payout and div_yield >= min_yield and payout <= max_payout:
results.append({
"symbol": sym,
"yield": div_yield,
"payout_ratio": payout,
"price": data.get("price"),
})
return sorted(results, key=lambda x: x["yield"], reverse=True)
# Screen dividend aristocrats
div_stocks = ["JNJ", "PG", "KO", "PEP", "XOM", "VZ", "T", "O"]
dividends = dividend_screen(div_stocks, min_yield=3.0, max_payout=60)
for s in dividends:
print(f"{s['symbol']}: Yield={s['yield']:.2f}%, Payout={s['payout_ratio']:.0f}%")
Try these strategies yourself
Free: 500 calls/day • Pro: $29/mo (10K calls/day) • Enterprise: $99/mo (50K calls/day)
Step 6: Add Technical Analysis Signals
MarketLens also provides technical indicators. Let's enhance the screener by adding RSI and SMA signals to each stock that passes our filter.
def enrich_with_technicals(symbols):
"""Fetch technical summary for each symbol."""
enriched = []
for sym in symbols:
data = fetch(f"/api/v1/technical/{sym}/summary")
indicators = data.get("indicators", {})
signals = data.get("signals", [])
enriched.append({
"symbol": sym,
"rsi": indicators.get("rsi_14"),
"sma_20": indicators.get("sma_20"),
"ema_20": indicators.get("ema_20"),
"signals": signals,
})
return enriched
# Usage: enrich stocks that passed the screener
symbols = [s["symbol"] for s in filtered]
technicals = enrich_with_technicals(symbols)
for t in technicals:
print(f"{t['symbol']}: RSI={t['rsi']:.1f}, SMA20={t['sma_20']:.2f}")
for signal in t["signals"]:
print(f" - {signal}")
The technical summary endpoint returns SMA(20), EMA(20), RSI(14), and human-readable signals like "Price below SMA(20) -- bearish" or "RSI oversold -- potential bounce". Each call costs 1 API credit.
Step 7: Set Up Price Alerts
Finally, let's set up automated alerts for stocks that meet your screening criteria. MarketLens supports webhook-based price alerts for crypto, with stock alerts planned for Pro users.
def create_alert(symbol, target_price, direction="above"):
"""Create a price alert. Triggers when price crosses the target."""
resp = httpx.post(
f"{BASE}/api/v1/alerts/",
headers=HEADERS,
json={
"symbol": symbol,
"target_price": target_price,
"direction": direction,
},
)
resp.raise_for_status()
return resp.json()
# Set an alert when BTC crosses $80,000
result = create_alert("BTC", 80000, "above")
print(f"Alert created: {result['data']['message']}")
Live API Demo
Try the MarketLens API right now — no API key needed for the demo. Click any button to see real data from the same endpoints used in this tutorial.
// Click a button above to see a live API response
// These are the same endpoints your Python screener would call
{
"status": "ready",
"message": "Select an endpoint to try"
}
Live data from MarketLens API — real-time from 5 sources. Response times shown after each request.
MarketLens vs. Alternatives for Stock Screening
Wondering why we use MarketLens instead of yfinance, Alpha Vantage, or Polygon? Here's how they compare for building a Python stock screener:
| Feature | yfinance | Alpha Vantage | Polygon.io | MarketLens |
|---|---|---|---|---|
| Free calls/day | Unlimited* | 25 | 5 | 500 |
| Reliability | Breaks often | Stable | Stable | Stable (5 sources) |
| Screener endpoints | None | None | Tickers only | Gainers/losers/movers |
| Technical indicators | DIY only | SMA/EMA | SMA/EMA/RSI | 50+ built-in |
| AI sentiment | ||||
| Pro pricing | Free (unstable) | $49.99/mo | $29-$199/mo | $29/mo ($14.50 w/ FOUNDER50) |
| Total endpoints | ~20 scraped | ~50 | ~80 | 222+ |
* yfinance is an unofficial Yahoo Finance scraper. It has no API key, no rate limits, and no uptime guarantees. It frequently breaks when Yahoo changes their HTML. For hobby projects it's fine; for anything production, use a real API.
Get the Free Stock Screener Python Template
All 4 strategies (value, growth, momentum, dividend) in one ready-to-run Python file. Plus a free API key.
Join 500+ Python developers. No spam, ever.
Next Steps
You now have 4 working screening strategies. Here are some ideas to extend them:
- Schedule it — run your screener every morning with
cronor APScheduler - Add crypto screening — use
/api/v1/screener/crypto/gainersfor crypto movers - Build a web UI — wrap the screener in Flask or Streamlit for a dashboard
- Build a trading bot — use screener results to automate trades (Trading Bot Guide)
- Combine with portfolio tracking — use the Portfolio API to track your screened picks
- Use the Python SDK — MarketLens SDK wraps all API calls with type hints and retry logic
- Explore the full API — 222+ endpoints covering stocks, crypto, forex, commodities, and more
Frequently Asked Questions
MarketLens API offers 222+ endpoints with 500 free calls per day — enough to build and run a stock screener without paying anything. It provides real-time quotes, dedicated screener endpoints (gainers, losers, movers), technical indicators, and company fundamentals. Pro is $29/mo for 10K calls/day, or $14.50/mo with FOUNDER50.
A basic screener running once daily uses about 30-50 API calls. The free tier (500 calls/day) covers this easily. If you run scans hourly or screen 1000+ stocks, Pro at $29/mo gives 10,000 calls/day. Enterprise ($99/mo) gives 50,000.
For production screeners, yes. yfinance is an unofficial Yahoo Finance scraper that breaks frequently when Yahoo changes their HTML. MarketLens provides a stable REST API with guaranteed uptime, built-in screener endpoints, 50+ technical indicators, and AI sentiment. yfinance is fine for hobby projects but unreliable for anything you depend on.
This guide covers 4 strategies: value investing (P/E and P/B ratios), growth (revenue growth), momentum (RSI and SMA crossovers), and dividend (yield and payout ratio). MarketLens API provides all the data needed for each strategy through 222+ endpoints.
Yes. MarketLens covers 8 data types: stocks, crypto (200+ assets), forex, commodities, economic indicators, news with AI sentiment, options, and alternative data. Use /api/v1/screener/crypto/gainers for crypto and /api/v1/forex/{pair} for forex.
Sign up at marketlens.dev/checkout.html — no credit card required. You get 500 API calls per day for free. Use coupon code FOUNDER50 for 50% off Pro ($14.50/mo) or Enterprise ($49.50/mo) forever via GitHub Sponsors.
Use the MarketLens /api/v1/stocks/{symbol}/profile endpoint to get fundamental data including P/E ratio. Then filter in Python: [s for s in stocks if s.get('pe_ratio', 999) < 15]. See the value investing strategy section above for the full implementation.
FOUNDER50 gives you 50% off any MarketLens paid plan forever. Pro drops from $29/mo to $14.50/mo, Enterprise from $99/mo to $49.50/mo. Available via GitHub Sponsors.
Build Your Screener for $14.50/mo
Start free (500 calls/day). Upgrade to Pro for 10K calls/day at just $14.50/mo with FOUNDER50.
Ready to Build Your Screener?
Sign up for a free MarketLens API key and start screening stocks in under 60 seconds. 500 calls/day, no credit card required. Use FOUNDER50 for 50% off Pro.