Build a Stock Screener in Python with Free API [2026 Guide]

Tutorial March 14, 2026 15 min read 222+ endpoints Used by 500+ developers

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 BotAlpha Vantage AlternativePython SDKFull API Reference

Get your free API key — 500 calls/day

No credit card required. Use code FOUNDER50 for 50% off Pro ($14.50/mo).

Try It Free →

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:

bash
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.

python
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.

python
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.

python
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:

python
#!/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:

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.

python
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.

python
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.

python
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.

python
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)

Get Free API Key →

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.

python
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.

python
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 cron or APScheduler
  • Add crypto screening — use /api/v1/screener/crypto/gainers for 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 SDKMarketLens SDK wraps all API calls with type hints and retry logic
  • Explore the full API222+ 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.

FOUNDER50 — 50% OFF FOREVER

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.

Free
500 calls/day
$29/mo
$14.50/mo
Pro • 10K calls/day
$99/mo
$49.50/mo
Enterprise • 50K calls/day
Try It Free →

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.