29 Jun 2026

Talking to Interactive Brokers from Python: the API, the libraries, and how to actually use it

Photo: Chris Ried / Unsplash

Interactive Brokers has one of the most capable APIs in retail brokerage, and one of the most confusing on-ramps. There are two completely different APIs, a couple of competing Python libraries, and a running application you have to keep alive in the background. Here's the map, so you don't waste a weekend on the wrong path.

First: there are two different IBKR APIs

This trips up almost everyone.

  • The TWS API (also called the "native" API). Your Python code talks over a socket to a running copy of Trader Workstation (TWS) or the lighter, headless IB Gateway. That local app holds the authenticated session and relays everything to IB's servers. This is the mature, full-featured path, and what most retail automation uses.
  • The Client Portal Web API (the "Web API"). A REST and WebSocket API. You run a small Client Portal Gateway (a Java process) that you log into, or, for third-party apps serving other users, you use OAuth. No TWS required. Better suited to web apps and multi-user services.

For a solo investor scripting their own account, the TWS API is almost always the right choice. The rest of this guide focuses there.

The moving parts you have to set up

The TWS API only works while TWS or IB Gateway is running and logged in. Your script is a client that connects to it.

  1. Install and log into TWS or IB Gateway. Gateway is the better choice for automation: lighter, no charting UI, meant to run unattended.
  2. Enable the API. In Settings > API > Settings, tick Enable ActiveX and Socket Clients, confirm the socket port, and add 127.0.0.1 to Trusted IPs. Consider ticking Read-Only API while you're learning, so a bug can't fire a live order.
  3. Know your port. The defaults are worth memorizing:
    • TWS live: 7496, TWS paper: 7497
    • IB Gateway live: 4001, IB Gateway paper: 4002
  4. Always start on the paper account. Same API, fake money. There is no reason to debug against real cash.

Which Python library to use in 2026

This is the part with a landmine.

  • ibapi (official). IBKR's own library, bundled with the TWS API download. It's low-level and callback-driven: you subclass EWrapper and EClient, run a network thread, and handle every response as an incoming event. It's complete and it's what the docs are written against, but it's verbose and awkward for simple tasks.
  • ib_insync (legacy). For years this was the library: a clean, high-level layer over ibapi that made everything readable. Its author, Ewald de Wit, sadly passed away, and the original project is no longer maintained. You'll still find it in countless tutorials, but don't start a new project on it.
  • ib_async (recommended). A community-maintained fork of ib_insync, kept alive and updated. It's essentially a drop-in replacement with the same ergonomics. For new work, this is the one to install.

There's also ibind, a third-party client if you decide to go the Client Portal Web API route instead. Handy to know it exists.

A working example with ib_async

Install it, make sure IB Gateway (paper) is running with the API enabled, and:

from ib_async import IB, Stock

ib = IB()
ib.connect('127.0.0.1', 7497, clientId=1)  # 7497 = TWS paper

# Resolve an ambiguous ticker into a fully-specified contract
apple = Stock('AAPL', 'SMART', 'USD')
ib.qualifyContracts(apple)

# What do I hold?
for pos in ib.positions():
    print(pos.contract.symbol, pos.position, pos.avgCost)

# Daily bars for the last 30 days
bars = ib.reqHistoricalData(
    apple,
    endDateTime='',
    durationStr='30 D',
    barSizeSetting='1 day',
    whatToShow='TRADES',
    useRTH=True,
)
for b in bars[-5:]:
    print(b.date, b.close)

ib.disconnect()

A few things this snippet quietly teaches:

  • clientId matters. Each connected client needs a distinct id. Reuse one that's already connected and you'll be rejected.
  • Contracts must be qualified. AAPL alone is ambiguous (which exchange, which currency); qualifyContracts fills in the conId, IB's unique instrument identifier. Keying everything on the conId rather than the ticker is the single best habit you can build, because tickers are not unique across exchanges.
  • ib_async hides the event loop. Under the hood it's all async, but for scripts you can call things synchronously as shown.

The two things that will bite you

  • Market data needs subscriptions. Live quotes require the right market-data subscriptions on your account. Without them you'll get nothing or an error. For testing, ask for delayed data with ib.reqMarketDataType(3) (delayed) or 4 (delayed-frozen) before requesting a quote.
  • Pacing limits are real. IB caps you at roughly 50 messages per second, and historical-data requests have their own throttles (hammer the same contract and you'll get pacing violations). Batch your requests, add small delays, and cache what you've already pulled. Respecting the pacing window is the difference between a script that runs for months and one that gets throttled or disconnected.

What this has to do with Cresori

Cresori connects to the same Interactive Brokers API to pull your trades, positions, dividends, and cash automatically, then turns them into performance attribution, dividend forecasts, and tax reporting, without you writing or maintaining any of the code above. Think of this guide as the do-it-yourself path: great for a custom script or a personal robot, but a lot of plumbing (contract resolution, pacing, reconnection, corporate actions, split adjustments) to own yourself. If you'd rather skip straight to the analysis, that's the part Cresori handles.

Sources and further reading: the IBKR TWS API documentation, the ib_async repository, and the Client Portal Web API docs.

More from Learn

Comments