Use Date Ranges
Always specify start_date and end_date to limit data transfer and improve performance.
Access historical OHLCV (Open, High, Low, Close, Volume) market data with automatic interval aggregation.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /stocks | No | List available symbols |
GET | /stocks/{symbol}/ohlcv | No | Get OHLCV data for symbol |
GET | /stocks/batch | No | Get data for multiple symbols |
Get all available stock symbols in the database.
GET /api/v1/stocks?limit=100["AAPL", "MSFT", "GOOGL", "AMZN", "META", "NVDA", "TSLA"]| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 100 | Max symbols to return (1-1000) |
Retrieve candlestick data for a specific symbol with automatic interval aggregation.
GET /api/v1/stocks/AAPL/ohlcv?interval=15m&start_date=2025-01-01&end_date=2025-01-31&limit=1000[ { "time": "2025-01-15T09:30:00Z", "symbol": "AAPL", "open": 185.50, "high": 186.25, "low": 185.10, "close": 186.00, "volume": 1250000, "vwap": 185.75 }, { "time": "2025-01-15T09:45:00Z", "symbol": "AAPL", "open": 186.00, "high": 186.80, "low": 185.90, "close": 186.50, "volume": 980000, "vwap": 186.25 }]| Parameter | Type | Description |
|---|---|---|
symbol | string | Stock ticker symbol (e.g., AAPL, MSFT) |
| Parameter | Type | Default | Description |
|---|---|---|---|
interval | string | 15m | Data interval (see below) |
start_date | date | - | Filter start date (YYYY-MM-DD) |
end_date | date | - | Filter end date (YYYY-MM-DD) |
limit | integer | 1000 | Max records (1-10,000) |
allow_aggregation | boolean | true | Auto-aggregate from finer intervals |
market_hours_only | boolean | false | Filter to 9:30 AM - 4:00 PM ET only |
| Interval | Description |
|---|---|
1m | 1 minute |
5m | 5 minutes |
15m | 15 minutes |
30m | 30 minutes |
1h | 1 hour |
1d | 1 day (daily) |
| Field | Type | Description |
|---|---|---|
time | datetime | Candle timestamp (UTC) |
symbol | string | Stock symbol |
open | float | Opening price |
high | float | Highest price |
low | float | Lowest price |
close | float | Closing price |
volume | integer | Trading volume |
vwap | float | Volume-weighted average price |
| Header | Values | Description |
|---|---|---|
X-Data-Source | stored, aggregated | Whether data was stored or aggregated |
X-Market-Hours | true, false | Whether market hours filter was applied |
When the requested interval doesn’t exist in the database, the API automatically aggregates from finer-grained data using TimescaleDB’s time_bucket() function.
1m ──► 5m ──► 15m ──► 30m ──► 1h│ │ │ │└──────┴───────┴───────┴──── Can aggregate to| Source Interval | Can Aggregate To |
|---|---|
1m | 5m, 15m, 30m, 1h |
5m | 15m, 30m, 1h |
15m | 30m, 1h |
30m | 1h |
| Field | Aggregation Method |
|---|---|
open | First value in bucket |
high | Maximum in bucket |
low | Minimum in bucket |
close | Last value in bucket |
volume | Sum of all values |
vwap | Volume-weighted average |
// Request 15-minute dataconst response = await fetch('/api/v1/stocks/AAPL/ohlcv?interval=15m');
// Check if data was aggregatedconsole.log(response.headers.get('X-Data-Source'));// Output: "aggregated" (if 15m data didn't exist but 1m did)
const data = await response.json();Retrieve OHLCV data for multiple symbols in a single request.
GET /api/v1/stocks/batch?symbols=AAPL,MSFT,GOOGL&start_date=2025-01-01&end_date=2025-01-31&limit=100[ { "time": "2025-01-15T00:00:00Z", "symbol": "AAPL", "open": 185.50, "high": 188.25, "low": 184.10, "close": 187.00, "volume": 52000000, "vwap": 186.15 }, { "time": "2025-01-15T00:00:00Z", "symbol": "MSFT", "open": 420.00, "high": 425.50, "low": 418.25, "close": 424.00, "volume": 28000000, "vwap": 422.50 }]| Parameter | Type | Description |
|---|---|---|
symbols | string | Required. Comma-separated symbols (max 20) |
start_date | date | Start date filter |
end_date | date | End date filter |
limit | integer | Max records per symbol (default: 1000) |
import axios from 'axios';
const api = axios.create({ baseURL: 'http://localhost:8501/api/v1'});
// Get daily AAPL data for 2024async function getStockData() { const { data, headers } = await api.get('/stocks/AAPL/ohlcv', { params: { interval: '1d', start_date: '2024-01-01', end_date: '2024-12-31', limit: 365 } });
console.log(`Fetched ${data.length} candles`); console.log(`Data source: ${headers['x-data-source']}`);
// Calculate simple statistics const closes = data.map(d => d.close); const avgClose = closes.reduce((a, b) => a + b, 0) / closes.length; const maxClose = Math.max(...closes); const minClose = Math.min(...closes);
console.log(`Average close: $${avgClose.toFixed(2)}`); console.log(`High: $${maxClose.toFixed(2)}, Low: $${minClose.toFixed(2)}`);
return data;}
// Get intraday data with market hours filterasync function getIntradayData(symbol) { const { data } = await api.get(`/stocks/${symbol}/ohlcv`, { params: { interval: '15m', market_hours_only: true, limit: 2000 } });
return data;}
// Batch request for portfolioasync function getPortfolioData(symbols) { const { data } = await api.get('/stocks/batch', { params: { symbols: symbols.join(','), start_date: '2025-01-01', limit: 100 } });
// Group by symbol const bySymbol = data.reduce((acc, candle) => { if (!acc[candle.symbol]) acc[candle.symbol] = []; acc[candle.symbol].push(candle); return acc; }, {});
return bySymbol;}
// Usageconst appleData = await getStockData();const portfolio = await getPortfolioData(['AAPL', 'MSFT', 'GOOGL']);import requestsimport pandas as pdfrom datetime import datetime, timedelta
BASE_URL = "http://localhost:8501/api/v1"
def get_stock_data( symbol: str, interval: str = "1d", start_date: str = None, end_date: str = None, limit: int = 1000) -> pd.DataFrame: """Fetch OHLCV data and return as DataFrame.""" params = { "interval": interval, "limit": limit } if start_date: params["start_date"] = start_date if end_date: params["end_date"] = end_date
response = requests.get( f"{BASE_URL}/stocks/{symbol}/ohlcv", params=params ) response.raise_for_status()
# Check data source data_source = response.headers.get("X-Data-Source", "unknown") print(f"Data source: {data_source}")
# Convert to DataFrame data = response.json() df = pd.DataFrame(data) df["time"] = pd.to_datetime(df["time"]) df.set_index("time", inplace=True)
return df
def get_batch_data(symbols: list, **kwargs) -> dict: """Fetch data for multiple symbols.""" response = requests.get( f"{BASE_URL}/stocks/batch", params={ "symbols": ",".join(symbols), **kwargs } ) response.raise_for_status()
data = response.json() df = pd.DataFrame(data) df["time"] = pd.to_datetime(df["time"])
# Group by symbol return {symbol: group for symbol, group in df.groupby("symbol")}
# Usage examplesif __name__ == "__main__": # Get AAPL daily data aapl = get_stock_data("AAPL", interval="1d", limit=252) print(f"AAPL: {len(aapl)} trading days") print(aapl.tail())
# Calculate returns aapl["returns"] = aapl["close"].pct_change() print(f"Average daily return: {aapl['returns'].mean():.4%}") print(f"Volatility (std): {aapl['returns'].std():.4%}")
# Get 15-minute data aapl_intraday = get_stock_data( "AAPL", interval="15m", start_date="2025-01-15", end_date="2025-01-15" ) print(f"Intraday candles: {len(aapl_intraday)}")
# Batch request portfolio = get_batch_data( ["AAPL", "MSFT", "GOOGL"], start_date="2025-01-01", limit=50 ) for symbol, df in portfolio.items(): print(f"{symbol}: {len(df)} records")# Get daily AAPL datacurl "http://localhost:8501/api/v1/stocks/AAPL/ohlcv?interval=1d&limit=100"
# Get 15-minute data with date rangecurl "http://localhost:8501/api/v1/stocks/AAPL/ohlcv?\interval=15m&\start_date=2025-01-15&\end_date=2025-01-15&\market_hours_only=true"
# Batch requestcurl "http://localhost:8501/api/v1/stocks/batch?\symbols=AAPL,MSFT,GOOGL&\start_date=2025-01-01&\limit=50"
# Check response headerscurl -I "http://localhost:8501/api/v1/stocks/AAPL/ohlcv?interval=5m"# Look for: X-Data-Source: aggregatedUse Date Ranges
Always specify start_date and end_date to limit data transfer and improve performance.
Batch Requests
Use /stocks/batch for multiple symbols instead of making separate requests.
Check Headers
Check X-Data-Source header to know if data was aggregated from finer intervals.
Appropriate Intervals
Use daily data for long-term analysis, 15m or 1h for intraday strategies.
limit and date ranges to fetch only what you needmarket_hours_only=true to exclude pre/post market noise| Status | Cause | Response |
|---|---|---|
404 | Symbol not found or no data | {"detail": "No data found for symbol XXXX"} |
400 | Invalid interval | {"detail": "Invalid interval"} |
400 | Too many batch symbols | {"detail": "Maximum 20 symbols allowed per request"} |