Prerequisites
You need three things:
- Python 3.8 or newer.
- Two libraries:
requestsfor HTTP andpandasfor the watchlist step. - A free API Ninjas API key. Signing up takes a minute and needs no credit card; if you already have an account, the key is on your profile page.
pip install requests pandasThe API authenticates every request with an X-Api-Key header — you'll see it in each example below.
Step 1: Make your first request
The endpoint is /v1/stockprice and it takes a single query parameter, ticker. Request a quote for Apple:
import requests
response = requests.get(
'https://api.api-ninjas.com/v1/stockprice',
params={'ticker': 'AAPL'},
headers={'X-Api-Key': 'YOUR_API_KEY'},
timeout=10,
)
response.raise_for_status()
quote = response.json()
print(f"{quote['ticker']}: ${quote['price']}")
# AAPL: $192.42Here is the same request on the wire, with the full JSON the API returns:
{
"ticker": "AAPL",
"name": "Apple Inc.",
"price": 192.42,
"exchange": "NASDAQ",
"updated": 1706302801,
"currency": "USD",
"volume": 44594000
}The same endpoint also quotes market indexes — pass ^DJI as the ticker and you get the Dow Jones Industrial Average. That's a complete, working integration. The next step is understanding what came back.
Step 2: Read the response
The response is a flat JSON object. Four fields arrive on every plan:
| Field | Type | Meaning |
|---|---|---|
ticker | string | The symbol you requested, echoed back. |
price | number | The latest price, in the listing exchange's currency. |
updated | number | Unix timestamp of when the quote was taken. |
volume | number | Shares traded in the current session. |
Three more — name, exchange, and currency — appear on premium plans, which is why the sample response above shows seven fields.
Pay attention to updated. Free keys return quotes delayed up to 15 minutes, while premium keys return live prices; the timestamp tells you exactly what you got. Code that reads the timestamp instead of assuming freshness behaves correctly on both plans.
Step 3: Harden the client
The Step 1 code is fine for a one-off script, but it has three weaknesses you'll hit as soon as it runs regularly. The API key is hard-coded, so it eventually gets committed to git. Each call opens a fresh TLS connection, which is slow across many tickers. And the first 429 Too Many Requests crashes the run. The fix for all three fits in twenty lines:
import os
import time
import requests
API_URL = 'https://api.api-ninjas.com/v1/stockprice'
session = requests.Session()
session.headers['X-Api-Key'] = os.environ.get('API_NINJAS_KEY', 'YOUR_API_KEY')
def get_quote(ticker: str, retries: int = 3) -> dict:
"""Return the latest quote for one ticker, retrying rate limits."""
for attempt in range(retries):
response = session.get(API_URL, params={'ticker': ticker}, timeout=10)
if response.status_code == 429:
time.sleep(int(response.headers.get('Retry-After', 2 ** attempt)))
continue
response.raise_for_status()
return response.json()
raise RuntimeError(f'Still rate-limited after {retries} attempts: {ticker}')
print(get_quote('AAPL')['price'])Three changes, one per weakness. The key now comes from an environment variable — run export API_NINJAS_KEY=your_key once and the code never contains a secret. A shared requests.Session reuses the same connection for every call. And when the API answers 429, the client waits — using the server's Retry-After header if present, otherwise backing off 1, 2, then 4 seconds — and tries again.
The get_quote() function is the foundation for everything that follows.
Step 4: Track a watchlist
With get_quote() defined, fetching a whole watchlist is a list comprehension. Loading the results into a pandas DataFrame gives you sorting, math, and CSV export for free:
import pandas as pd
watchlist = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'NVDA']
df = pd.DataFrame([get_quote(t) for t in watchlist])
df['updated'] = pd.to_datetime(df['updated'], unit='s', utc=True)
print(df[['ticker', 'price', 'updated']].to_string(index=False))
df.to_csv('watchlist.csv', index=False)ticker price updated
AAPL 192.42 2024-01-26 21:00:01+00:00
MSFT 404.87 2024-01-26 21:00:01+00:00
GOOGL 153.79 2024-01-26 21:00:02+00:00
AMZN 159.12 2024-01-26 21:00:02+00:00
NVDA 610.31 2024-01-26 21:00:03+00:00Schedule this with cron and append to the CSV instead of overwriting, and you are building your own price history — one row per ticker per run, ready for charting or analysis.
Step 5: Pull historical prices
Everything so far returns the latest quote. For charts and backtesting you want candles, and those come from a companion endpoint, /v1/stockpricehistorical. It uses the same API key and takes the same ticker parameter, plus two optional ones: period for the candle interval (1m up to 1d; the default is 1h) and start/end Unix timestamps to bound the range.
candles = session.get(
'https://api.api-ninjas.com/v1/stockpricehistorical',
params={'ticker': 'AAPL', 'period': '1d'},
timeout=10,
).json()
candles.sort(key=lambda c: c['time']) # API returns newest first
latest = candles[-1]
print(f"close={latest['close']} high={latest['high']} volume={latest['volume']}")Each candle contains open, high, low, close, volume, and time. The API returns candles newest-first, so sort by time — as the example does — before feeding them to a chart.
Step 6: Handle errors and rate limits
The client from Step 3 already retries rate limits. The remaining failures are worth handling explicitly, because each one means something different:
| Status | Cause | What to do |
|---|---|---|
400 | Unknown or malformed ticker | Validate input and show a clear message. |
401 | Missing or invalid API key | Check the X-Api-Key header and its value. |
429 | Rate limit exceeded | Back off and retry; cache quotes to reduce calls. |
5xx | Transient server error | Retry with backoff; alert only if it persists. |
In code, that means catching two exception types — HTTPError for API-level failures and RequestException for network problems:
from requests.exceptions import HTTPError, RequestException
try:
quote = get_quote('AAPL')
except HTTPError as error:
status = error.response.status_code
if status == 400:
print('Bad request — check the ticker symbol')
elif status == 401:
print('Missing or invalid API key')
else:
print(f'HTTP {status}: {error.response.text}')
except RequestException as error:
print(f'Network error: {error}')Resist the blanket except Exception. Market data that fails silently is how wrong prices end up in databases — let unexpected errors crash loudly.
Where to go from here
You now have a hardened quote client, a watchlist exporter, and historical data. The download below bundles it into one runnable script. When you're ready to build further, the same API key covers the rest of the equity workflow: the Earnings Calendar API tells you which of your tickers report this week, the Stock News API adds headlines, and the Market Cap API adds company size.