Close Navigation
.
IBKR API Historical Data: How to Download Bars without Pacing Violations

IBKR API Historical Data: How to Download Bars without Pacing Violations

Posted November 2, 2026 at 11:40 am

Ankur Parikh
Trade Vectors LLP

The author of this article is not affiliated with Interactive Brokers.

Almost every automated trading system needs past prices. A backtest needs years of bars. A live system needs the last few hours to warm up its indicators before the open, and it needs to fill the gap after a restart. The Interactive Brokers TWS API gives you all of this through one call, reqHistoricalData, but the call comes with limits that are easy to break. Break them and requests fail with a pacing violation, and a downloader that keeps retrying can get the whole API session disconnected.

This article explains, in plain terms, how a historical data request works, which limits matter, what data you cannot get, and how to build a downloader that stays inside the rules. It applies to TWS and IB Gateway alike, and to any language the TWS API supports. The example is in Python.

One request, step by step

A historical data request asks for bars for one instrument. You tell IBKR:

  • The instrument, as a contract: symbol, security type, exchange and currency.
  • When the data should end (endDateTime). Leave it empty for “up to now”.
  • How far back to go (durationStr), such as “1 D”, “2 W” or “1 Y”.
  • The bar size (barSizeSetting), such as “1 min”, “1 hour” or “1 day”.
  • The type of data (whatToShow), such as TRADES or MIDPOINT.
  • Which hours to include (useRTH): 1 for regular trading hours only, 0 to include extended hours.

The bars arrive one at a time in historicalData, and historicalDataEnd tells you the request is complete. Here is a minimal example using IBKR’s own ibapi package for Python. It waits for nextValidId, the API’s signal that the connection is ready, before it asks for anything:

from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract


class App(EWrapper, EClient):
    def __init__(self):
        EClient.__init__(self, self)

    def nextValidId(self, orderId):
        contract = Contract()
        contract.symbol = "AAPL"
        contract.secType = "STK"
        contract.exchange = "SMART"
        contract.currency = "USD"

        # One day of 1 minute trade bars, regular hours only, times in epoch seconds
        self.reqHistoricalData(
            1, contract, "20260115 16:00:00 US/Eastern",
            "1 D", "1 min", "TRADES", 1, 2, False, []
        )

    def historicalData(self, reqId, bar):
        print(bar.date, bar.open, bar.high, bar.low, bar.close, bar.volume)

    def historicalDataEnd(self, reqId, start, end):
        print("Finished", start, end)
        self.disconnect()


app = App()
app.connect("127.0.0.1", 7497, clientId=1)
app.run()

Port 7497 is the default for TWS on a paper account; IB Gateway on a paper account uses 4002.

Ask for a sensible amount each time

IBKR expects each request to return no more than a few thousand bars. Its documentation lists which bar sizes are allowed for each duration: a request covering 1800 seconds can use bars from 1 second up to 30 minutes, while a request covering one day starts at 1 minute bars.

So to collect a long history of small bars, split it into many requests. The usual way is to walk backwards: ask for the most recent chunk, set endDateTime to the start of what you just received, and ask again. Every step is a separate request, which is why the next section matters.

The pacing rules for small bars

For bars of 30 seconds or less, IBKR treats any of these as a pacing violation:

  • making the same request again within 15 seconds;
  • making six or more requests for the same contract, exchange and data type within two seconds;
  • making more than 60 requests in any ten minute period.

Requests for BID_ASK data count twice. These limits cannot be raised.

For bars of 1 minute and larger, IBKR has lifted these hard limits, but it still slows down clients that ask for too much, and very heavy use can end in a disconnect. Separately, no more than 50 historical data requests can be open at the same time, and in practice keeping far fewer pending works better.

One easy trap: reqHeadTimestamp, which tells you where the available data begins, counts as a historical data request under the small bar rules, whatever bar size you are interested in. Cancel it with cancelHeadTimeStamp once you have the answer.

Know what you cannot get

Some data is not available through the API at all, and retrying will not change that:

  • bars of 30 seconds or less older than six months;
  • expired futures data more than two years after the contract expired;
  • expired options;
  • tick history (Time and Sales) older than three years.

Historical ticks, requested with reqHistoricalTicks, come back at most 1000 per request and never span more than one trading session, so a full day of ticks for a busy instrument takes many requests.

Before downloading years of data, call reqHeadTimestamp to learn where the history actually begins. It saves requests that would only come back empty.

Choose the right data type and hours

  • TRADES builds bars from actual trades and is the usual choice for stocks and futures.
  • MIDPOINT, BID and ASK build bars from quotes. Forex has no trade data at IBKR, so for currency pairs request MIDPOINT, BID or ASK instead of TRADES.
  • useRTH decides whether bars outside regular trading hours are included. A backtest and the live system it was built for should use the same setting, or their numbers will not match.

Historical data generally needs a market data subscription for the instrument, just like live data. If a request fails with a permissions message, retrying will not help: the subscription has to be added to the account first.

Always state the time zone

If endDateTime has no time zone, TWS returns warning 2174, which says requests without one will stop being supported. Write the zone every time, for example “20260115 16:00:00 US/Eastern”, or use UTC. Setting formatDate to 2 returns intraday bar times as seconds since 1970 in UTC, which removes any guessing when you store them.

Build a downloader that never trips the limits

The limits are easy to respect if every historical request goes through one place:

  1. One queue for all historical requests. Count requests in a rolling ten minute window and wait before you reach 60. Never repeat a request within 15 seconds.
  2. Store what you download. Keep bars in a local database or in files, keyed by instrument, bar size, data type and time. Next time, ask only for the bars after the last one you already have.
  3. Remove duplicates. Overlapping chunks return some bars twice. Keep one bar per timestamp.
  4. Check for gaps. Compare what you received with the exchange’s trading calendar, including holidays and half days. A missing hour is better found before a backtest than after it.
  5. Read error 162 before you retry. It is IBKR’s general historical data error, and its message says whether the cause is pacing, permissions or no data for that period. After a pacing message, wait and try again; for the other two, fix the request or the account instead.
  6. Let IBKR keep the latest bar current. In a live system, set keepUpToDate to true with an empty endDateTime. IBKR then keeps sending updates to the most recent bar every few seconds, so the system does not need to poll.

Summary

Historical data from the IBKR API is dependable once you work with its limits rather than against them: ask for a few thousand bars at a time, keep small bar requests under 60 in ten minutes, check what history exists before you ask for it, always name the time zone, and save what you download so you never ask twice. The downloader then becomes the dull, reliable part of the system, which is exactly what it should be.

This article is for information about software engineering only and is not investment advice.

Check out the first article in this series: Keeping an IBKR API System Running Unattended

Already an Interactive Brokers Client?

New to Interactive Brokers?

Disclosure: Interactive Brokers Third Party

Information posted on IBKR Campus that is provided by third-parties does NOT constitute a recommendation that you should contract for the services of that third party. Third-party participants who contribute to IBKR Campus are independent of Interactive Brokers and Interactive Brokers does not make any representations or warranties concerning the services offered, their past or future performance, or the accuracy of the information provided by the third party. Past performance is no guarantee of future results.

This material is from Trade Vectors LLP and is being posted with its permission. The views expressed in this material are solely those of the author and/or Trade Vectors LLP and Interactive Brokers is not endorsing or recommending any investment or trading discussed in the material. This material is not and should not be construed as an offer to buy or sell any security. It should not be construed as research or investment advice or a recommendation to buy, sell or hold any security or commodity. This material does not and is not intended to take into account the particular financial conditions, investment objectives or requirements of individual customers. Before acting on this material, you should consider whether it is suitable for your particular circumstances and, as necessary, seek professional advice.

Disclosure: API Proof-of-Concept Disclosure

The third-party code discussed within this article is not investment or trading advice, and is for proof-of-concept, educational, and illustrative purposes only. IBKR makes no representations or warranty regarding its accuracy or completeness. Users are solely responsible for conducting their own independent testing and due diligence before applying any code or concepts in a live or production environment

Disclosure: API Examples Discussed

Please keep in mind that the examples discussed in this material are purely for technical demonstration purposes, and do not constitute trading advice. Also, it is important to remember that placing trades in a paper account is recommended before any live trading.

Disclosure: Order Types / TWS

The order types available through Interactive Brokers LLC's trading platforms are designed to help you limit your loss and/or lock in a profit. Market conditions and other factors may affect execution. In general, orders guarantee a fill or guarantee a price, but not both. In extreme market conditions, an order may either be executed at a different price than anticipated or may not be filled in the marketplace.

Join The Conversation

For specific platform feedback and suggestions, please submit it directly to our team using these instructions.

If you have an account-specific question or concern, please reach out to Client Services.

We encourage you to look through our FAQs before posting. Your question may already be covered!

Leave a Reply

IBKR Campus Newsletters

This website uses cookies to collect usage information in order to offer a better browsing experience. By browsing this site or by clicking on the "ACCEPT COOKIES" button you accept our Cookie Policy.