Skip to content
Hero Background Light

Stock Prediction APIs: A Developer's Guide

Stock Prediction APIs: A Developer's Guide

Building trading systems or research tools that incorporate price predictions requires reliable API access to forecast data. This guide covers what to look for in a stock prediction API, how to evaluate providers, and practical integration patterns.

What to Look for in a Prediction API

Not all prediction APIs are equal. Before integrating, evaluate these criteria:

Data Quality

CriterionQuestions to Ask
CoverageHow many assets? Which markets?
Forecast horizonDays ahead? Months ahead?
Update frequencyReal-time? Daily? Weekly?
Uncertainty quantificationPoint estimate only or confidence intervals?
Track recordHow long has the model been running?

A prediction without uncertainty is incomplete. APIs that only provide single-point estimates hide crucial information about model confidence.

Technical Requirements

CriterionWhat to Check
AuthenticationAPI key? OAuth? Rate limits?
Response formatJSON? CSV? Consistent schemas?
LatencyResponse time for single/bulk requests
ReliabilityUptime SLA? Error handling?
DocumentationComplete? Accurate? Code examples?

Integration Options

OptionBest For
REST APIMost applications, flexibility
Python SDKData science workflows, rapid prototyping
WebhooksEvent-driven systems, alerts
Bulk exportsBatch processing, data warehouses

Common Prediction API Architectures

Point Estimate APIs

The simplest APIs return a single predicted price:

{
"ticker": "AAPL",
"predicted_price": 200.50,
"date": "2024-11-15"
}

Limitation: No uncertainty information. You don’t know if this is a high-confidence or low-confidence prediction.

Directional Signal APIs

Some APIs return buy/sell/hold signals:

{
"ticker": "AAPL",
"signal": "BUY",
"confidence": 0.75
}

Limitation: Loses price target information. A “buy” at $100 means something different than a “buy” at $200.

Full Distribution APIs

The most complete APIs provide the full prediction distribution:

{
"ticker": "AAPL",
"prediction": {
"2024-11-04": "201.33,197.21,205.45",
"2024-11-05": "202.77,196.92,208.61",
"expectedShort": "0.22",
"expectedMid": "0.58",
"expectedLong": "0.25"
}
}

This format includes:

  • Point estimate (mean prediction)
  • Confidence interval (lower and upper bounds)
  • Movement probabilities (expected magnitude)

This is what you should look for—prediction APIs that quantify uncertainty.

Integrating a Prediction API

Basic Integration Pattern

The standard pattern for prediction API integration:

import requests
class PredictionClient:
def __init__(self, api_key: str, base_url: str):
self.api_key = api_key
self.base_url = base_url
def get_prediction(self, ticker: str) -> dict:
"""Fetch prediction for a single ticker."""
response = requests.get(
f"{self.base_url}/predictions/{ticker}",
params={"token": self.api_key}
)
response.raise_for_status()
return response.json()
def get_bulk_predictions(self, tickers: list) -> dict:
"""Fetch predictions for multiple tickers."""
results = {}
for ticker in tickers:
try:
results[ticker] = self.get_prediction(ticker)
except Exception as e:
results[ticker] = {"error": str(e)}
return results

Error Handling

Robust integrations handle common API errors:

import time
from requests.exceptions import RequestException
def fetch_with_retry(client, ticker, max_retries=3, backoff=1.0):
"""Fetch prediction with exponential backoff retry."""
for attempt in range(max_retries):
try:
return client.get_prediction(ticker)
except RequestException as e:
if attempt == max_retries - 1:
raise
wait_time = backoff * (2 ** attempt)
print(f"Retry {attempt + 1}/{max_retries} after {wait_time}s: {e}")
time.sleep(wait_time)

Caching Strategy

Predictions don’t change intraday—cache appropriately:

from functools import lru_cache
from datetime import date
@lru_cache(maxsize=1000)
def get_cached_prediction(ticker: str, cache_date: str) -> dict:
"""
Cache predictions by date.
Cache invalidates when date changes.
"""
return client.get_prediction(ticker)
# Usage - cache_date ensures daily refresh
prediction = get_cached_prediction("AAPL", str(date.today()))

Using the FinBrain Prediction API

FinBrain provides AI-powered price predictions with confidence intervals for 25,000+ assets across 20+ global markets.

Quick Start

Install the Python SDK:

Terminal window
pip install finbrain-python

Basic usage:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
# Get predictions as DataFrame (daily is default)
df = fb.predictions.ticker("AAPL", as_dataframe=True)
print(df)
# main lower upper
# date
# 2024-11-04 201.33 197.21 205.45
# 2024-11-05 202.77 196.92 208.61
# ...

What the API Returns

Daily predictions: 10 trading days forward, each with:

  • Predicted price (main)
  • Lower bound (lower)
  • Upper bound (upper)

Monthly predictions: 12 months forward, same structure.

Movement probabilities:

  • expectedShort — probability of short-term move
  • expectedMid — probability of medium-term move
  • expectedLong — probability of longer-term move

Important: Predictions are forward-looking only. You cannot retrieve historical predictions—what the model predicted last week for today. Forecasts are generated fresh each day.

Getting Raw JSON

For custom processing:

# Get raw JSON response
data = fb.predictions.ticker("AAPL")
# Access prediction object
pred = data["prediction"]
print(f"Last updated: {pred['lastUpdate']}")
print(f"Expected short-term move: {pred['expectedShort']}%")
# Parse individual date forecasts
for date_key, value in pred.items():
if ',' in str(value): # Date entries contain comma-separated values
main, lower, upper = value.split(',')
print(f"{date_key}: ${main} (range: ${lower} - ${upper})")

Monthly Predictions

For longer-term forecasting:

# Get 12-month predictions
monthly = fb.predictions.ticker("AAPL", prediction_type="monthly", as_dataframe=True)
print(monthly)

Market-Wide Predictions

Get predictions for all tickers in a market:

# All S&P 500 predictions
sp500 = fb.predictions.market("S&P 500", as_dataframe=True)
print(f"Predictions for {len(sp500)} tickers")

Built-in Visualization

The SDK includes plotting functionality:

# Plot prediction cone
fb.plot.predictions("AAPL")
# Monthly view
fb.plot.predictions("AAPL", prediction_type="monthly")

Practical Integration Examples

Building a Screener

Screen for high-conviction bullish predictions:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
def screen_bullish_predictions(market: str, min_upside: float = 0.05):
"""
Find tickers where the lower bound is above current price.
This indicates high conviction in upward movement.
"""
df = fb.predictions.market(market, as_dataframe=True)
bullish = []
for ticker in df.index.get_level_values('ticker').unique():
ticker_data = df.loc[ticker]
first_day = ticker_data.iloc[0]
# If lower bound > current implies strong conviction
# (We'd need current price from another source)
# For now, check if predicted move is positive
if first_day['main'] > first_day['lower'] * 1.02:
upside = (first_day['upper'] - first_day['main']) / first_day['main']
if upside >= min_upside:
bullish.append({
'ticker': ticker,
'predicted': first_day['main'],
'lower': first_day['lower'],
'upper': first_day['upper'],
'upside_potential': upside
})
return sorted(bullish, key=lambda x: x['upside_potential'], reverse=True)
# Find bullish opportunities in S&P 500
opportunities = screen_bullish_predictions("S&P 500", min_upside=0.10)
for opp in opportunities[:10]:
print(f"{opp['ticker']}: {opp['upside_potential']:.1%} upside potential")

Alert System

Send alerts when predictions show significant moves:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
def check_prediction_alerts(watchlist: list, threshold: float = 0.05):
"""
Check predictions and generate alerts for significant moves.
"""
alerts = []
for ticker in watchlist:
data = fb.predictions.ticker(ticker)
pred = data["prediction"]
# Get expected moves
expected_short = float(pred.get("expectedShort", 0))
expected_mid = float(pred.get("expectedMid", 0))
expected_long = float(pred.get("expectedLong", 0))
# Alert if any expected move exceeds threshold
max_expected = max(expected_short, expected_mid, expected_long)
if max_expected > threshold:
alerts.append({
'ticker': ticker,
'expected_short': expected_short,
'expected_mid': expected_mid,
'expected_long': expected_long,
'max_move': max_expected
})
return alerts
# Check watchlist
watchlist = ["AAPL", "NVDA", "TSLA", "META", "AMZN"]
alerts = check_prediction_alerts(watchlist, threshold=0.10)
for alert in alerts:
print(f"ALERT: {alert['ticker']} - Expected move: {alert['max_move']:.1%}")

Portfolio Overlay

Add prediction data to existing portfolio positions:

from finbrain import FinBrainClient
import pandas as pd
fb = FinBrainClient(api_key="YOUR_API_KEY")
def overlay_predictions(portfolio: pd.DataFrame) -> pd.DataFrame:
"""
Add prediction data to portfolio DataFrame.
Expects portfolio to have 'ticker' and 'current_price' columns.
"""
predictions = []
for _, row in portfolio.iterrows():
ticker = row['ticker']
current_price = row['current_price']
try:
data = fb.predictions.ticker(ticker)
pred = data["prediction"]
# Get first day's prediction
for key, value in pred.items():
if ',' in str(value):
main, lower, upper = map(float, value.split(','))
predictions.append({
'ticker': ticker,
'predicted': main,
'lower': lower,
'upper': upper,
'expected_return': (main - current_price) / current_price,
'downside_risk': (current_price - lower) / current_price,
'upside_potential': (upper - current_price) / current_price
})
break
except Exception as e:
predictions.append({
'ticker': ticker,
'error': str(e)
})
pred_df = pd.DataFrame(predictions)
return portfolio.merge(pred_df, on='ticker', how='left')
# Example usage
portfolio = pd.DataFrame({
'ticker': ['AAPL', 'MSFT', 'GOOGL'],
'current_price': [198.50, 420.00, 175.25],
'shares': [100, 50, 75]
})
enriched = overlay_predictions(portfolio)
print(enriched[['ticker', 'expected_return', 'downside_risk', 'upside_potential']])

Combining with Sentiment

Merge predictions with sentiment data for richer signals:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
def get_combined_signal(ticker: str) -> dict:
"""
Combine price prediction with sentiment for a composite signal.
"""
# Get prediction
pred_data = fb.predictions.ticker(ticker)
pred = pred_data["prediction"]
# Get sentiment
sent_data = fb.sentiments.ticker("S&P 500", ticker, as_dataframe=True)
latest_sentiment = float(sent_data.iloc[0]['score']) if len(sent_data) > 0 else 0
# Parse first prediction
for key, value in pred.items():
if ',' in str(value):
main, lower, upper = map(float, value.split(','))
break
# Expected move from prediction
expected_short = float(pred.get("expectedShort", 0))
# Composite signal
# Positive sentiment + positive expected move = strong bullish
# Negative sentiment + negative expected move = strong bearish
# Divergence = uncertain
sentiment_signal = 1 if latest_sentiment > 0.3 else (-1 if latest_sentiment < -0.3 else 0)
prediction_signal = 1 if expected_short > 0.02 else (-1 if expected_short < -0.02 else 0)
if sentiment_signal == prediction_signal == 1:
composite = "STRONG_BULLISH"
elif sentiment_signal == prediction_signal == -1:
composite = "STRONG_BEARISH"
elif sentiment_signal != prediction_signal and sentiment_signal != 0 and prediction_signal != 0:
composite = "DIVERGENCE"
else:
composite = "NEUTRAL"
return {
'ticker': ticker,
'predicted_price': main,
'prediction_range': f"${lower:.2f} - ${upper:.2f}",
'expected_move': f"{expected_short:.1%}",
'sentiment': latest_sentiment,
'composite_signal': composite
}
# Check a ticker
signal = get_combined_signal("NVDA")
print(f"{signal['ticker']}: {signal['composite_signal']}")
print(f" Predicted: ${signal['predicted_price']:.2f} ({signal['prediction_range']})")
print(f" Expected move: {signal['expected_move']}")
print(f" Sentiment: {signal['sentiment']:.3f}")

API Best Practices

Rate Limiting

Respect API limits to avoid throttling:

import time
from collections import deque
class RateLimiter:
def __init__(self, max_calls: int, period: float):
self.max_calls = max_calls
self.period = period
self.calls = deque()
def wait_if_needed(self):
now = time.time()
# Remove calls outside the window
while self.calls and self.calls[0] < now - self.period:
self.calls.popleft()
# Wait if at limit
if len(self.calls) >= self.max_calls:
sleep_time = self.calls[0] - (now - self.period)
if sleep_time > 0:
time.sleep(sleep_time)
self.calls.append(time.time())
# Usage
limiter = RateLimiter(max_calls=100, period=60) # 100 calls per minute
for ticker in large_ticker_list:
limiter.wait_if_needed()
prediction = fb.predictions.ticker(ticker)

Error Handling

Handle API errors gracefully:

from requests.exceptions import HTTPError
def safe_get_prediction(fb, ticker: str) -> dict | None:
"""Safely fetch prediction with error handling."""
try:
return fb.predictions.ticker(ticker)
except HTTPError as e:
if e.response.status_code == 404:
print(f"{ticker}: Not found")
elif e.response.status_code == 401:
print("Authentication failed - check API key")
elif e.response.status_code == 429:
print("Rate limited - slow down requests")
else:
print(f"{ticker}: HTTP {e.response.status_code}")
return None
except Exception as e:
print(f"{ticker}: Unexpected error - {e}")
return None

Data Validation

Validate API responses before using:

def validate_prediction(data: dict) -> bool:
"""Validate prediction response structure."""
required_fields = ["ticker", "prediction"]
if not all(field in data for field in required_fields):
return False
pred = data["prediction"]
if "lastUpdate" not in pred:
return False
# Check for at least one date-keyed prediction
has_prediction = any(',' in str(v) for v in pred.values())
return has_prediction

Coverage and Availability

FinBrain predictions cover:

MarketExamples
US EquitiesS&P 500, NASDAQ, NYSE, DOW 30
InternationalFTSE 100, DAX, TSX, ASX, Hang Seng
ETFsMajor US ETFs
CommoditiesGold, Oil, Natural Gas
CurrenciesMajor forex pairs
CryptoBitcoin, Ethereum, major altcoins
FuturesIndex futures

Total coverage: 25,000+ assets across 20+ markets.

Check available tickers:

# Get all available tickers for a market
tickers = fb.available.tickers("daily")
print(f"Available for daily predictions: {len(tickers)} tickers")

Key Takeaways

  1. Look for prediction APIs that provide uncertainty quantification, not just point estimates
  2. Use SDKs when available—they handle authentication, parsing, and common patterns
  3. Cache predictions appropriately since they typically update daily, not intraday
  4. Combine predictions with other signals (sentiment, fundamentals) for robust strategies
  5. Implement proper error handling and rate limiting for production systems
  6. Validate API responses before using in trading logic

A prediction API is only as useful as your ability to integrate it reliably. Choose providers with clear documentation, consistent schemas, and proper uncertainty quantification—then build robust error handling around them.