Skip to main content
This article is for educational purposes only and does not constitute financial advice. Trading involves risk of loss. Past performance does not guarantee future results. Consult a licensed financial advisor before making investment decisions.
AI & Automation9 min readUpdated September 17, 2026
TW

Interactive Brokers API in Python: Build a Trading Bot Step by Step

Interactive Brokers API Python tutorial: TWS vs IB Gateway, paper port 7497, ib_async setup, a working order script, and reconnect gotchas.

Put this into practice with a watchlist

Build a watchlist, then review each signal’s entry, stop, target, and reasoning. Broker access is optional.

Build a Watchlist

Interactive Brokers API in Python: The Short Version

The fastest way to use the Interactive Brokers API in Python is to run IB Gateway or TWS on a machine you control, enable the socket API, and connect with the ib_async library on the paper-trading port. The official ibapi package works too, but it forces you to write callback plumbing that ib_async handles for you.

Every path to IBKR shares one constraint. The TWS API is not a hosted web service. It is a socket into a Java desktop application that you must keep logged in, restarted daily, and re-authenticated weekly (Interactive Brokers, TWS API Initial Setup). Your bot's uptime is only as good as that Gateway session. The REST-based Web API can skip TWS, but retail users still log in through the local Client Portal Gateway; OAuth is for institutions and approved vendors, and Web API use requires a funded IBKR Pro account (Interactive Brokers, Web API docs).

This tutorial covers setup, the three integration options, a working script, and the failure modes that catch first-time IBKR API users. The general steps in how to build a trading bot apply too.

TWS vs IB Gateway: Which One to Run

From the API's point of view, Trader Workstation (TWS) and IB Gateway are the same server. Both accept a socket connection after you log in through their GUI, and IBKR states that headless operation without a GUI is not supported for either (Interactive Brokers, TWS API Initial Setup).

FactorTWSIB Gateway
PurposeFull trading platform with charts, Risk Navigator, OptionTraderLightweight API-only server
Resource useHeavierAbout 40% fewer resources, per IBKR docs
API enabled by defaultNo, must enable in settingsYes, accepts socket clients by default
Auto-updateYesOffline version only, upgrade manually
Default live port74964001
Default paper port74974002
Best forLearning the API, watching orders fillUnattended bots on a VPS

IBKR's guidance is to start with TWS, then move to Gateway once the bot is stable. The ib_async README adds one tip: raise Java memory allocation to at least 4096 MB to avoid crashes when pulling bulk historical data.

The 7496 and 7497 values come straight from IBKR's setup documentation. The Gateway values 4001 (live) and 4002 (paper) are the widely documented defaults, but they can be changed. Verify the port in the API settings dialog before you blame your code.

Enabling the API: Settings That Bite

Three settings in the API configuration dialog decide whether your bot can connect and trade.

  1. Enable ActiveX and Socket Clients. In TWS this lives at Edit, Global Configuration, API, Settings. Without it TWS refuses every connection. Gateway has it on by default.
  2. Read-Only API. IBKR ships this enabled. In read-only mode, order placement fails and order information is hidden from the API (Interactive Brokers, TWS API Initial Setup). Turn it off only when you are ready to submit paper orders.
  3. Trusted IPs. Add 127.0.0.1 if the bot runs on the same machine. If it runs elsewhere, add that machine's IP and uncheck "Allow connections from localhost only". Otherwise TWS pops a manual approval dialog on every connection.

Also check "Download open orders on connection" so your client sees orders placed from other sessions.

The client ID is a small integer you choose, and only one connection per ID is allowed. IBKR returns error 326 when the ID is in use, so a bot that crashes and restarts quickly can lock itself out until the old socket times out. Rotating to the next ID after a few failures is the usual fix.

Three Ways In: TWS API, ib_async, and the Web API

IBKR offers more than one API, and beginners often pick the wrong one.

Official TWS API (ibapi)ib_async (formerly ib_insync)Web API (Client Portal REST)
TransportSocket to TWS/GatewaySocket to TWS/GatewayHTTPS REST + WebSocket
Needs desktop app runningYesYesNo for OAuth; yes for the local Client Portal Gateway
Programming modelEClient + EWrapper callbacks, two threadsSync or asyncio, objects stay in sync automaticallyRequest/response, session cookie via /tickle
InstallDownload from IBKR, setup.py installpip install ib_asyncHTTP client of your choice
Maintained byInteractive Brokersib-api-reloaded community orgInteractive Brokers
LicenseIBKR termsBSD-2-ClauseIBKR terms
Good forFull protocol access, latest fields firstMost Python botsWeb apps, mobile, no VPS

On the ib_insync to ib_async rename. ib_insync was written by Ewald de Wit starting in 2017. After his death in early 2024, the community lost access to the original repo and packaging, so the project was renamed ib_async under the ib-api-reloaded GitHub organization, now maintained by Matt Stancliff (ib_async README, accessed September 2026). It shows about 1.7k stars, a BSD-2-Clause license, and a Python 3.10+ requirement. The pysystemtrade project switched from ib_insync to ib_async in April 2026 (pysystemtrade discussion #1577). Start fresh with ib_async; legacy ib_insync code migrates with mostly an import rename.

On the Web API. IBKR is merging the Client Portal Web API, Digital Account Management, and Flex Web Service into one Web API under OAuth 2.0, while keeping existing endpoints supported (Interactive Brokers, Web API Documentation). Third-party vendors may currently only seek approval for OAuth 1.0a. For an individual trading their own account, the local Client Portal Gateway plus a browser login is simpler; it removes TWS but not session maintenance. Web API access requires a fully opened and funded IBKR Pro account; IBKR Lite can still use the TWS API (Interactive Brokers, Web API docs).

Step-by-Step: Connect, Qualify a Contract, Place a Paper Order

This is the minimal happy path with ib_async against a paper account on the TWS paper port. The symbol is an example for a paper test, not a recommendation.

from ib_async import IB, Stock, MarketOrder

ib = IB()
# 7497 = TWS paper. Use 4002 for Gateway paper. Never hardcode a live port here.
ib.connect("127.0.0.1", 7497, clientId=7)

contract = Stock("AAPL", "SMART", "USD")
ib.qualifyContracts(contract)  # fills in conId, exchange details, etc.

order = MarketOrder("BUY", 1)
trade = ib.placeOrder(contract, order)

ib.sleep(2)  # let TWS send back order status events
print(trade.orderStatus.status, trade.orderStatus.avgFillPrice)

ib.disconnect()

What each step does:

  1. connect() opens the socket. If it hangs, the API is disabled, the port is wrong, or the IP is not trusted.
  2. qualifyContracts() resolves your partial description into one unambiguous contract with a conId. Skipping it is the most common cause of "no security definition found" rejections.
  3. placeOrder() returns a Trade object that stays updated as fills arrive. MarketOrder is the simplest type; for anything beyond a test, read market vs limit orders and use a limit.
  4. ib.sleep() is not time.sleep(). It runs the event loop so messages from TWS get processed.

Before running it, confirm "Paper" appears in the TWS title bar and that Read-Only API is unchecked.

Handling the Event Loop in an Async App

The sync style above secretly runs an asyncio loop behind ib.sleep() and ib.run(). Inside an app that already has a running loop, such as a FastAPI server or a Discord bot, you must use the async methods:

import asyncio
from ib_async import IB, Stock, MarketOrder

async def main():
    ib = IB()
    await ib.connectAsync("127.0.0.1", 7497, clientId=7)
    contract = Stock("AAPL", "SMART", "USD")
    await ib.qualifyContractsAsync(contract)
    trade = ib.placeOrder(contract, MarketOrder("BUY", 1))
    await asyncio.sleep(2)
    print(trade.orderStatus.status)
    ib.disconnect()

asyncio.run(main())

The sync methods work inside a running loop only because the library patches asyncio with nest_asyncio. That patch does not work on every loop implementation. Tradewink learned this in production: its IBKR client runs on uvloop, and calling sync methods like qualifyContracts() there raises ValueError: Can't patch loop of type uvloop.Loop. Worse, each failed patch attempt leaked a file descriptor, and the process accumulated tens of thousands of them before an emergency restart. The fix was to guard sync calls with a loop-type check and prefer qualifyContractsAsync() and connectAsync(), which are plain coroutines that work on any loop.

Rule of thumb: if your program has async def anywhere, use the *Async variants everywhere.

Put the setup on a watchlist first

Use the rules in this guide to evaluate a signal’s entry, stop, target, and reasoning before deciding what, if anything, to do.

Build a Watchlist

Reconnects, Daily Restarts, and Weekly Logins

IBKR designed both TWS and Gateway to be restarted every day so they can reload contract definitions (Interactive Brokers, TWS API Initial Setup). This surprises people who expect a broker API to behave like a cloud service.

  • Daily restart. Under Global Configuration, Lock and Exit, IBKR recommends API users choose "Never lock Trader Workstation" and "Auto restart" (Interactive Brokers, Daily and Weekly Reauthentication). The app then restarts at your chosen time without a fresh two-factor prompt.
  • Weekly re-login. IBKR's docs say the weekly authentication cycle starts every Monday, and the older setup guide describes re-entering credentials after the Saturday-night server reset. An error containing "Soft token=0 received instead of expected permanent" means it is time to log in by hand, including 2FA.
  • Your bot's job. The socket drops during the restart. Catch the disconnect event, wait, and reconnect with backoff. Do not trust in-memory order state afterward; re-request open orders and positions and reconcile.
  • One login per username. Two sessions cannot share a username, and logging into Client Portal with the API's username can stop that session from auto-reconnecting after the reset. IBKR's suggested fix is a second username for the API session.

A dedicated always-on machine, home box or VPS, is the standard solution, with a tool like IBC automating the login clicks. A bot on a laptop that sleeps at night is not a bot.

Market Data: What Is Free and What Costs Money

Every IBKR account gets delayed market data at no charge (Interactive Brokers, Market Data Pricing). Real-time streaming quotes require paid monthly subscriptions that vary by exchange and region, deducted from your brokerage account rather than billed to a card.

Specifics that matter for bots, from IBKR's own pages:

  • Paper accounts without subscriptions get delayed Level 1 top-of-book and historical data only. Delayed data is not available for tick-by-tick or Level 2 depth (IBKR Campus, Subscribing to Market Data).
  • Subscribing to real-time data requires a funded account with at least $500 USD in most cases, on top of the subscription cost (IBKR Campus, Python API Requesting Market Data).
  • Regulatory snapshot quotes for US-listed stocks and ETFs cost USD 0.01 per request, with a USD 1.00 monthly waiver. If snapshot fees reach the cost of the streaming service in a month, IBKR upgrades you to streaming for that month (Interactive Brokers, Market Data Pricing).
  • Subscriptions are set up per live username, so a second API username does not inherit them.

For development, call ib.reqMarketDataType(3) to request delayed data explicitly and avoid the "no market data permissions" error. Historical bars via reqHistoricalData work on delayed data too, enough to paper trade most daily and intraday strategies.

A Worked Example: Sizing the Bot's First Paper Trade

Assume a hypothetical $10,000 paper account and a rule that no single trade risks more than 1% of equity. These numbers are invented for illustration.

  • Risk budget per trade: $10,000 times 1% = $100
  • Entry price from a delayed quote: $50.00
  • Stop-loss level chosen by the strategy: $49.50, so risk per share is $0.50
  • Shares = $100 divided by $0.50 = 200 shares
  • Notional position: 200 times $50.00 = $10,000, which is 100% of equity

That last line is the point. Fixed-fractional risk sizing can produce a position larger than the account when the stop is tight. A second rule, say a 25% cap on notional exposure, trims this to 50 shares. Encode both checks before placeOrder() runs, and compare the order to ib.accountSummary() buying power first. The position sizing glossary entry and the position size calculator show the same math interactively.

Safety Checklist Before Live

Each item corresponds to a real way IBKR bots lose money.

  1. Paper first, for weeks. Run the full bot on port 7497 or 4002 until it has survived a daily restart, a weekend, and at least one disconnect. The paper trading guide covers how to evaluate the results.
  2. Assert the port at startup. Refuse to run on a live port unless an environment variable such as ALLOW_LIVE=1 is set.
  3. Keep Read-Only API on until orders are tested. It is IBKR's default for a reason.
  4. Hard caps in code. Max notional per order, max open positions, max daily loss.
  5. Reconcile on reconnect. Re-request positions and open orders after every reconnect. Cancel anything the bot does not recognize.
  6. Use limit orders with a time-in-force. A market order on stale data after a reconnect is how a bot pays the whole spread.
  7. Log every message. Enable TWS API logging and write structured logs. Errors 326 (client ID in use), 502 (cannot connect), and 10197 (competing live session) each mean something specific.
  8. Separate usernames. One for the Gateway session, one for Client Portal, so a browser login does not knock the bot offline.
  9. Alert on silence. If no heartbeat arrives for a few minutes, page yourself. A dead socket looks exactly like a quiet market.

Alpaca and Tradier offer cloud REST APIs with no desktop app to babysit; the tradeoff is IBKR's much wider market access. How to choose a broker for algorithmic trading lays out the comparison, and the Alpaca AI agent tutorial shows the REST-only route.

How Tradewink Connects to IBKR

Tradewink's open-source IBKR integration uses ib_insync (pinned to the 0.9 series) over the same socket API. It defaults to the paper ports, 7497 for TWS and 4002 for Gateway, treats 7496 and 4001 as live, supports a read-only mode, rotates the client ID when IBKR reports error 326, and guards sync calls behind the uvloop check described earlier.

Because the agent runs in the cloud, it cannot reach a Gateway on 127.0.0.1. A self-hosted Gateway must be stored as a public host:port endpoint with the agent's egress IP added to the Gateway's Trusted IPs. A dedicated hosted Gateway machine is also available, which removes the VPS step but still requires the user to approve the IBKR login and 2FA. Details are on the IBKR broker page.

Automating orders through any broker API carries real risk of loss, including from software bugs, stale data, and disconnects at the wrong moment. This article is educational and is not financial advice.

Frequently Asked Questions

Which port does the Interactive Brokers API use for paper trading?

By default TWS listens on 7497 for a paper account and 7496 for a live account, according to IBKR's TWS API setup documentation. IB Gateway commonly uses 4002 for paper and 4001 for live. These are defaults and can be changed in the API settings dialog, so confirm the port there if a connection hangs.

Is ib_insync still maintained, or should I use ib_async?

Use ib_async. The original ib_insync author, Ewald de Wit, died in early 2024, and the project was renamed ib_async under the ib-api-reloaded GitHub organization, which now maintains it. The API is nearly identical, so most ib_insync code migrates with an import rename. ib_async requires Python 3.10 or newer and does not need IBKR's ibapi package.

Do I need TWS or IB Gateway running to use the Interactive Brokers API in Python?

Yes for the TWS API and for ib_async. Both connect over a socket to a logged-in TWS or IB Gateway session, and IBKR does not support headless operation without the GUI login. The Web API is the exception: retail users authenticate via the local Client Portal Gateway (OAuth is for institutions and approved vendors) and can call REST endpoints without TWS, though session maintenance is still required and the Web API needs a funded IBKR Pro account.

Why does my IBKR bot disconnect every night?

TWS and IB Gateway are designed to restart daily to reload contract definitions. Enable Auto restart and Never lock under Global Configuration, Lock and Exit, so the restart happens without a login prompt, and write your bot to catch the disconnect and reconnect with backoff. A separate weekly re-authentication, including 2FA, still requires a manual login.

Does the Interactive Brokers API give free market data?

Delayed market data is free on every account, and paper accounts without subscriptions get delayed Level 1 and historical data only. Real-time streaming requires paid monthly subscriptions that vary by exchange, deducted from the brokerage account, and IBKR requires a funded balance of at least $500 in most cases before you can subscribe. Call reqMarketDataType(3) to request delayed data explicitly during development.

Why does ib_insync fail with a ValueError about patching the event loop?

The sync methods in ib_insync and ib_async rely on nest_asyncio to run inside an already-running asyncio loop, and nest_asyncio cannot patch uvloop. In an async application, use connectAsync, qualifyContractsAsync, and the other Async variants instead of the sync calls, and avoid ib.sleep and ib.run. Tradewink hit this exact failure in production and also observed leaked file descriptors on each failed patch attempt.

Is it safe to run a trading bot on Interactive Brokers?

It can be run carefully, but every automated trading system carries a risk of loss from bugs, stale data, and disconnects. Keep the Read-Only API enabled until orders are tested, paper trade through several daily restarts and a weekend, enforce hard notional and daily-loss caps in code, and reconcile positions after every reconnect. This is educational information, not financial advice.

Keep learning with a related guide before putting an idea on your watchlist.

Ready to evaluate a signal?

Start free with a watchlist and inspect the context before you consider a broker connection.

Try AI signals on your watchlist

Send yourself a signal preview, then add tickers to see ranked entries, exits, and risk notes in Tradewink.

Enter the email address where you want to receive a Tradewink AI signal preview.

TW

Tradewink builds explainable market research for self-directed traders. Build a watchlist, inspect signal reasoning and risk context, and paper-track ideas before you decide. Public subscriptions are paper-only; separately approved private beta accounts may submit live broker orders.

How this guide is reviewed

Tradewink reviews educational content against its documented market-data sources, risk controls, and product methodology. See our data sources and evaluation methodology for the evidence and limitations behind the platform.

Important disclosures

Informational purposes only

Tradewink is published by Tradewink LLC, which is not a registered investment adviser, broker-dealer, commodity trading advisor, or financial planner. All data, signals, and analytics on this page are general, impersonal, and for informational purposes only. They do not constitute investment advice, financial advice, or a recommendation to buy or sell any security or other instrument.

Trading risk

Past performance does not guarantee future results. Trading involves substantial risk of loss, including the possibility of losing more than your initial investment. You are solely responsible for your own trading decisions.