Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 3

Fetching Crypto OHLCV Data with REST APIs

Recommended resources

Hello! Welcome back to your course on low-frequency crypto trading.

In our last lesson, we explored the calculation of arithmetic and logarithmic returns, establishing them as the fundamental inputs for any quantitative strategy. We concluded that to compute these returns, we first need a reliable source of historical price data.

Today, we will address that need directly. This lesson is designed to meet the learning outcome: Fetch historical OHLCV data for multiple crypto assets using REST APIs and handle exchange-specific rate limits and pagination. As a data developer and engineer, you'll find the core concepts familiar, but we'll focus on the specific challenges and patterns encountered when dealing with cryptocurrency exchange APIs.

We will cover:

  • The common structure of REST API endpoints for OHLCV data.
  • A practical, step-by-step guide to fetching data from a simple API (Bitstamp).
  • The critical techniques of pagination to overcome request limits and rate limiting to avoid being blocked by the exchange.
  • How API implementations can vary between exchanges (e.g., Coinbase).
  • The trade-offs between connecting directly to exchanges versus using a third-party data aggregator.

1. The Anatomy of a Crypto Data API Request

Before writing any code, it's essential to understand what we're asking for. The most common format for historical price data is OHLCV: Open, High, Low, Close, and Volume for a specific time interval (e.g., 1 minute, 1 hour, 1 day).

Building an Asynchronous OHLCV Data Downloader for ...

This brief excerpt from an article on Medium provides a standard definition of OHLCV data, which serves as a good starting point.

Read the first two sections, 'Why is OHLCV data necessary?' and 'Where do we get OHLCV data?'. This will confirm the terminology we'll be using throughout the lesson.

Exchanges provide this data through REST APIs. A typical request involves sending an HTTP GET request to a specific URL with several key parameters:

  • Symbol/Pair: The asset you want data for (e.g., BTCUSD, ETHUSDT).
  • Interval/Granularity: The duration of each candle (e.g., 60 for seconds, 1h for one hour).
  • Start/End Time: The time range for the data, often specified as Unix timestamps or ISO 8601 strings.
  • Limit: The maximum number of data points (candles) to return in a single request.

Let's see this in action with our first exchange.


2. Case Study 1: Fetching Data from Bitstamp

The Bitstamp API is a good starting point due to its clear and relatively simple structure. The following video walks through the entire process, from inspecting the API documentation to writing a Python script to download and clean the data.

Historical Crypto candlestick/OHLC data from Bitstamp in Python

This video by Chad Thackray provides a complete walkthrough for fetching historical data from Bitstamp. It covers making the request, handling parameters, and, most importantly, implementing a pagination loop to get large amounts of data.

Watch the video from 02:20 to 25:27. As you watch, focus on these key implementation steps: API Exploration (02:20): Understanding the endpoint and its parameters (step, limit). Initial Python Request (04:38): Using the requests library and parsing the JSON response into a pandas DataFrame. Pagination Logic (12:08): This is the most critical part. Pay close attention to how pandas.date_range is used to create time windows, and how the code iterates through them to make multiple API calls, overcoming the 1000-candle limit. Data Aggregation and Cleaning (18:11): Combining the data from multiple requests and handling duplicates and out-of-range timestamps.

The pagination strategy shown in the video is a robust and common pattern for downloading large historical datasets. By breaking a long time period (e.g., one year) into smaller chunks (e.g., days or 12-hour periods) and making a request for each, you can systematically work around the API's limit parameter.


3. API Variations: The Coinbase Example

It's crucial to understand that every exchange API is different. What works for Bitstamp won't work for Coinbase or Binance without modification. This is a core challenge in sourcing data from multiple venues.

Let's look at the Coinbase API to see some common variations.

Get OHLC/Candlestick data from Coinbase pro historical API in Python

This video, also from Chad Thackray, demonstrates fetching data from Coinbase. We will use it to highlight key differences compared to the Bitstamp API.

Watch the following segments: 01:00 - 02:36: Note the different endpoint structure, parameter names (granularity), and the much stricter candle limit (300 per request). 02:36 - 06:57: Observe the use of ISO 8601 format for timestamps and Python's datetime and timedelta for calculations, contrasting with Bitstamp's use of Unix timestamps. 13:35 - 14:46 & 18:37 - 19:05: These sections show how easily the code can be adapted for different assets and granularities, and reiterate the need for a loop to get more than 300 bars.

Key Differences Observed:

Feature Bitstamp API Coinbase API
Candle Limit 1000 per request 300 per request
Time Parameter step (in seconds) granularity (in seconds, from a fixed set)
Timestamp Format Unix timestamp (integer) ISO 8601 string
JSON Response List of dictionaries List of lists

These differences mean that a truly generic data downloader needs an adapter layer for each exchange to handle these variations in parameters and data parsing.


4. Handling Rate Limits: Staying in the Exchange's Good Graces

Beyond pagination, the most critical aspect of fetching data at scale is rate limiting. Exchanges restrict the number of requests you can make in a given time period (e.g., 1200 "request weights" per minute on Binance). Exceeding this limit will result in an HTTP 429 (Too Many Requests) error, and repeated offenses can lead to a temporary IP ban (HTTP 418).

A simple time.sleep(1) between requests is a naive but often effective starting point. However, a more sophisticated approach involves a dynamic, responsive system. The following article describes the design of a professional-grade data downloader and introduces concepts that are highly relevant to building robust data systems.

Building an Asynchronous OHLCV Data Downloader for ...

This article, 'Building an Asynchronous OHLCV Data Downloader for Binance...', details an advanced implementation for fetching data while respecting complex rate limits. While it uses asyncio, the design patterns are universally applicable.

Read the sections 'Understanding Binance’s API Rate Limits', 'Managing Rate Limits with RateLimiter', 'Preventing and Handling Bans with RateLimitManager', and 'The CircuitBreaker class'. Focus on the following concepts: Request Weights: Understand that not all requests are equal; some are more 'expensive' than others. Dynamic Throttling: Grasp the idea of using rate limit information from the API response headers (e.g., x-mbx-used-weight-1m) to adjust your request frequency dynamically, rather than using a fixed delay. Handling 429/418 Errors: Note the strategy of reading the Retry-After header in the error response to know exactly how long to wait. Circuit Breaker Pattern: This is a powerful pattern from distributed systems. Understand its role in pausing all requests when a high rate of failures is detected, preventing a cascade of errors.

These patterns—dynamic throttling based on response headers and the circuit breaker—are hallmarks of resilient, production-grade data ingestion systems. For your initial backtesting purposes, a simple, conservative time.sleep() is sufficient, but being aware of these advanced techniques is crucial for any future move towards live or large-scale data operations.


5. Alternative: Using a Data Aggregator

Building and maintaining connectors for multiple exchanges is a significant engineering effort. An alternative is to use a third-party data aggregator like CoinAPI, which provides a single, normalized API for data from hundreds of exchanges.

This approach presents its own set of trade-offs.

Market Data API FAQ | Help & Troubleshooting

This FAQ from CoinAPI provides insight into the value proposition and mechanics of a data aggregator. It's a good resource for understanding the pros and cons of this approach.

Skim through the 'OHLCV & bars' and 'Access & performance' sections. You don't need to read every question, but focus on the answers that address: Normalized Format: How they provide a consistent data structure regardless of the source exchange. Pagination: Their strategy for handling large result sets. Rate Limits and Credits: Their usage-based pricing model (e.g., 1 credit per 100 data points) instead of simple request-per-minute limits.

Direct Exchange Integration vs. Data Aggregator:

Aspect Direct Exchange Integration Data Aggregator (e.g., CoinAPI)
Development Effort High (must build and maintain a connector for each exchange) Low (integrate with one standardized API)
Data Normalization Manual (you must handle different formats) Automatic (data is delivered in a consistent format)
Cost "Free" (but incurs engineering and infrastructure costs) Explicit (paid subscription or credit-based system)
Control & Latency Full control; potentially lower latency if co-located Less control; potential for added latency through the middleman
Data Coverage Limited to the exchanges you integrate Broad coverage across many exchanges

For a project focused on a few specific assets on major exchanges, direct integration is often feasible. For strategies requiring broad market scanning, an aggregator can save significant development time.


Conclusion

In this lesson, we've transformed the abstract need for "price data" into a concrete engineering task. You now have a blueprint for building a data pipeline to fuel your trading strategy research.

Key Takeaways:

  • Fetching historical OHLCV data requires careful reading of each exchange's API documentation to understand its specific endpoints, parameters, and data formats.
  • Pagination is a mandatory technique for downloading large datasets, typically implemented by iterating through time-based chunks.
  • Rate limiting is a critical consideration. A simple time.sleep() is a basic solution, while production systems use dynamic throttling based on API response headers and patterns like the Circuit Breaker.
  • There is a fundamental trade-off between the high control of direct exchange integration and the convenience of a third-party data aggregator.

You've learned how to acquire the raw data. The next question is: what's the most effective way to store it for the rapid querying needed in backtesting?

In our next lesson, we will address the learning outcome: "Design a data storage schema (e.g., Parquet, TimescaleDB) optimized for efficient time-series queries in a backtesting context." This will draw heavily on your data engineering expertise as we explore solutions for managing time-series data efficiently.