Sitemap
Level Up Coding

Coding tutorials and news. The developer homepage gitconnected.com && skilled.dev && levelup.dev

Fetch and Process Live Market Quotes Using Financial APIs in Python

Build a reliable market data pipeline using Python, financial APIs, and Pandas

9 min readOct 16, 2026

--

Press enter or click to view image in full size
Photo by Sean Pollock on Unsplash

Imagine building a stock market dashboard that tracks hundreds of companies. Your application fetches stock prices, calculates price movements, and highlights unusual trading activity. Everything works perfectly during development.

Then you deploy it.

Suddenly, some stocks show outdated prices, API requests occasionally fail, and your application starts producing inconsistent results. Sometimes, the dashboard displays a price from several minutes ago without indicating that the data is stale.

The problem isn’t necessarily your Python code. It’s how your application handles financial data.

Financial APIs have made stock market data much more accessible to developers. Today, you can retrieve stock quotes, trading volumes, historical prices, and financial information through straightforward HTTP requests.

Several providers offer financial market data through developer-friendly APIs. Some popular examples include:

  • Alpha Vantage: Provides APIs for stock prices, technical indicators, and historical market data.
  • Polygon.io (now Massive): Offers market data APIs for stocks, options, and other financial instruments, including streaming capabilities.
  • Finnhub: Provides stock market data, company financial information, and financial news.
  • EOD Historical Data (EODHD): Offers historical stock prices, intraday data, live and delayed quotes, financial fundamentals, and market data across global exchanges.
  • Twelve Data: Provides stock market prices, historical data, technical indicators, and WebSocket streaming APIs.
  • Financial Modeling Prep (FMP): Offers stock quotes, financial statements, historical prices, and other market datasets.

Each provider differs in its market coverage, update frequency, pricing, rate limits, and available features.

Choosing the right API depends on what you’re building. A personal portfolio dashboard might only need periodic stock price updates, while a more advanced trading application may require streaming data with much lower latency.

But regardless of the provider, retrieving data is only the beginning.

When building applications that depend on market prices, several questions become important:

How do you know whether a stock quote is fresh?

What happens when the API becomes temporarily unavailable?

How can you monitor multiple stocks efficiently?

How do you prevent incorrect data from affecting your calculations?

These questions might seem less exciting than building a stock prediction model, but they determine whether your application can be trusted.

Another important consideration is the meaning of live market data. A real-time quote, a delayed quote, and a streaming market feed aren’t necessarily the same thing. Data coverage and update frequency depend on the provider and subscription.

For a portfolio dashboard, periodic API requests may be sufficient. For an application that needs individual trades within milliseconds, a streaming feed might be more suitable.

In this article, we’ll build a practical Python workflow for fetching market quotes, handling API failures, processing multiple stocks, validating data, and identifying important market movements.

For the implementation, we’ll use Financial Modeling Prep as our example data provider. The same engineering principles apply to other financial APIs, although their endpoints and response formats may differ.

The goal is to build a workflow that other parts of your application can safely depend on.

Fetching Live Stock Quotes Using Python

Let’s start with a practical scenario.

Suppose you’re building a dashboard for an investment research team. The application needs to monitor Apple, Microsoft, NVIDIA, and several other companies throughout the trading day.

First, install the required Python libraries:

pip install requests pandas python-dotenv

Next, store your API key in a .env file:

API_KEY=your_api_key_here

Avoid hardcoding API keys directly into source code. Add .env to your .gitignore file to prevent accidentally committing credentials.

Now, let’s retrieve our first stock quote.

import os
import requests

from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("API_KEY")

if not API_KEY:
raise RuntimeError("API_KEY is missing")

BASE_URL = "https://financialmodelingprep.com/stable"


def fetch_stock_quote(symbol: str) -> dict:
response = requests.get(
f"{BASE_URL}/quote",
params={
"symbol": symbol,
"apikey": API_KEY
},
timeout=(3, 10)
)

response.raise_for_status()

data = response.json()

if not isinstance(data, list) or not data:
raise ValueError(f"No quote found for {symbol}")

return data[0]


quote = fetch_stock_quote("AAPL")

print("Symbol:", quote["symbol"])
print("Price:", quote["price"])
print("Volume:", quote["volume"])
print("Change (%):", quote["changePercentage"])

The code sends an HTTP request, converts the JSON response into a Python object, and extracts the required information.

Notice that we’ve specified a timeout.

This is a small but important detail. Without a timeout, an application can spend an unexpectedly long time waiting for an unavailable service.

Another important field returned by the API is timestamp.

A stock price without its timestamp can be misleading. If your application receives a price of $200, you still need to know when that value was recorded.

Handling API Failures Without Breaking Your Application

A successful API request during development doesn’t guarantee that your integration will always work.

Financial APIs may occasionally return server errors, encounter network issues, or reject requests when usage limits are exceeded.

Instead of handling every failure manually, we can configure a reusable HTTP session with automatic retries.

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry


def create_session():
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET"],
respect_retry_after_header=True
)

session = requests.Session()

adapter = HTTPAdapter(
max_retries=retry_strategy
)

session.mount("https://", adapter)

return session


session = create_session()

This configuration retries certain failed requests rather than immediately terminating the workflow.

For example, HTTP 429 indicates rate limiting, while 503 typically indicates that the service is temporarily unavailable.

The backoff_factor introduces increasing delays between retry attempts.

Now, replace requests.get() in our earlier fetch_stock_quote() function with session.get() to use this behavior.

However, retries should always be limited.

If your application monitors hundreds of stocks, repeatedly retrying failed requests can consume your API quota quickly.

I would prefer designing data workflows to fail visibly rather than silently return outdated values. If a fresh quote cannot be retrieved, the application should report the failure or explicitly label any cached data as stale.

This makes troubleshooting easier and prevents incorrect information from being treated as current market data.

Fetching and Processing Multiple Stock Quotes

Let’s make our application more useful.

Get Pralabh Saxena’s stories in your inbox

Join Medium for free to get updates from this writer.

Suppose you need to monitor 50 stocks. One approach is to loop through every symbol and send an individual API request.

It works, but it creates unnecessary network overhead.

A better approach is to retrieve multiple quotes in a single request.

Many financial API providers support batch requests. For example, Alpha Vantage offers a bulk quotes API, while FMP provides a dedicated Stock Batch Quote endpoint. The exact batch limits and access requirements vary by provider.

Let’s implement batch retrieval using our selected provider.

def fetch_batch_quotes(symbols: list[str]) -> list[dict]:
response = session.get(
f"{BASE_URL}/batch-quote",
params={
"symbols": ",".join(symbols),
"apikey": API_KEY
},
timeout=(3, 15)
)

response.raise_for_status()

data = response.json()

if not isinstance(data, list):
raise ValueError("Unexpected API response")

returned_symbols = {
item["symbol"]
for item in data
if isinstance(item, dict) and "symbol" in item
}

missing = set(symbols) - returned_symbols

if missing:
print("Missing quotes:", sorted(missing))

return data


symbols = ["AAPL", "MSFT", "NVDA"]

quotes = fetch_batch_quotes(symbols)

This implementation is more efficient because multiple stock quotes can be retrieved with fewer HTTP requests.

It also handles an important scenario: missing records.

Sometimes, an API request succeeds but doesn’t return data for every requested symbol. By checking the returned symbols, we can detect incomplete responses before passing them to the next stage.

Now, let’s convert the response into a Pandas DataFrame.

import pandas as pd

df = pd.DataFrame(quotes)

columns = [
"symbol",
"price",
"previousClose",
"changePercentage",
"volume",
"timestamp"
]

df = df[columns].copy()

numeric_columns = [
"price",
"previousClose",
"changePercentage",
"volume"
]

for column in numeric_columns:
df[column] = pd.to_numeric(
df[column],
errors="coerce"
)

df["timestamp"] = pd.to_datetime(
df["timestamp"],
unit="s",
utc=True,
errors="coerce"
)

print(df)

Pandas makes it easier to clean, transform, and analyze financial datasets.

In this example, we’re converting numerical columns into appropriate types and normalizing timestamps to UTC.

The Pandas documentation explains how Unix timestamps can be converted into timezone-aware datetime values.

Using UTC consistently is especially useful when working with stocks traded across different exchanges.

You can convert timestamps into local market time when displaying them, while keeping the internal representation consistent.

Validating Market Data Before Using It

One lesson that applies across data-driven applications is simple: never assume that an API response contains perfectly clean data.

A response can have missing prices, unexpected values, or invalid timestamps even when the HTTP request succeeds.

Let’s add basic validation.

required = [
"symbol",
"price",
"previousClose",
"timestamp"
]

clean_df = df.dropna(subset=required).copy()

clean_df = clean_df[
(clean_df["price"] > 0)
& (clean_df["previousClose"] > 0)
].copy()

clean_df["calculated_change_pct"] = (
(
clean_df["price"]
- clean_df["previousClose"]
)
/ clean_df["previousClose"]
) * 100

clean_df["change_difference"] = (
clean_df["calculated_change_pct"]
- clean_df["changePercentage"]
).abs()

clean_df["requires_review"] = (
clean_df["change_difference"] > 0.1
)

Here, we’re rejecting invalid prices and comparing the reported percentage change against our own calculation.

A difference exceeding 0.1 percentage points is flagged for review. This is an illustrative tolerance, not a universal financial-data rule.

Such validation becomes particularly important when the output feeds portfolio calculations, automated alerts, or machine learning models.

Detecting Stale Quotes and Market Movements

Now comes an interesting problem.

Imagine your application retrieves a quote at 10:30 AM, but the quote’s timestamp indicates that the underlying market record is from 10:22 AM.

The API request succeeded, but the price might not be suitable for an application that requires fresh market information.

Let’s calculate how old each quote is.

from datetime import datetime, timezone

now = datetime.now(timezone.utc)

clean_df["quote_age_seconds"] = (
now - clean_df["timestamp"]
).dt.total_seconds()

MAX_QUOTE_AGE = 120

clean_df["is_stale"] = (
(clean_df["quote_age_seconds"] > MAX_QUOTE_AGE)
| (clean_df["quote_age_seconds"] < -5)
)

print(
clean_df[
["symbol", "price", "quote_age_seconds", "is_stale"]
]
)

This example flags quotes older than two minutes and timestamps more than five seconds in the future.

However, there’s an important catch.

An unchanged stock price isn’t necessarily stale simply because the market is closed. Weekends, holidays, trading halts, and extended trading sessions all affect when price updates should be expected.

Quote-age checks should therefore be combined with an exchange calendar and an understanding of the provider’s data-delivery rules.

Once we’ve identified usable quotes, we can begin extracting meaningful information.

Suppose we want to classify stocks based on their daily price movements.

def classify_movement(change_pct: float) -> str:
if change_pct >= 3:
return "Strong Gain"

if change_pct <= -3:
return "Strong Decline"

if change_pct >= 1:
return "Moderate Gain"

if change_pct <= -1:
return "Moderate Decline"

return "Relatively Stable"


usable_quotes = clean_df[
~clean_df["is_stale"]
& ~clean_df["requires_review"]
].copy()

usable_quotes["movement"] = (
usable_quotes["calculated_change_pct"]
.apply(classify_movement)
)

print(
usable_quotes[
[
"symbol",
"price",
"calculated_change_pct",
"movement"
]
]
)

The thresholds are illustrative, but the approach is practical.

A market monitoring dashboard could highlight companies with strong daily gains or declines, helping analysts identify stocks worth investigating further.

For more advanced applications, you could compare these movements against historical volatility or average trading volume.

For example, a two-percent movement may be unusual for a historically stable utility company but relatively common for a volatile technology stock.

This is where a basic market data pipeline starts becoming a useful analytical tool.

Press enter or click to view image in full size
Financial data pipeline reference architecture. Source: AWS Big Data Blog.

A Few Production Practices Worth Remembering

Before deploying this workflow, there are a few additional improvements worth considering.

First, monitor API latency, missing symbols, quote freshness, and validation failures. A successful HTTP response doesn’t necessarily mean your application has received usable market data.

Second, store historical observations when you need trend analysis. SQLite can work for smaller applications, while PostgreSQL or a time-series database may be more suitable as data volume grows. Remember that periodic quote snapshots aren’t a replacement for complete historical intraday data.

Third, keep the ingestion layer separate from analytical calculations. If you eventually switch providers or move from REST polling to WebSocket streaming, this separation can reduce the amount of code that needs changing.

Finally, test your calculations using saved sample responses instead of depending on a live API for every test.

A production-ready system should handle unexpected data, temporary network failures, and missing information without producing misleading results.

These practices may add a little development effort initially, but they make financial applications considerably easier to maintain and scale.

Conclusion

We explored how financial APIs make market data accessible and then built a practical workflow using Python. Starting with a basic API integration, we improved it through retry handling, batch requests, data validation, timestamp checks, and market movement analysis.

The most important lesson is that successfully retrieving data doesn’t automatically make it reliable.

A stock price without a trustworthy timestamp can be misleading. An API response without validation can introduce errors into downstream calculations.

As your requirements grow, you can introduce historical storage, asynchronous processing, streaming connections, and more advanced analytical models.

Reliable financial applications start with reliable data pipelines. Build that foundation properly, and the applications you develop on top of it become easier to trust and scale.

--

--