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.
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).
| Factor | TWS | IB Gateway |
|---|---|---|
| Purpose | Full trading platform with charts, Risk Navigator, OptionTrader | Lightweight API-only server |
| Resource use | Heavier | About 40% fewer resources, per IBKR docs |
| API enabled by default | No, must enable in settings | Yes, accepts socket clients by default |
| Auto-update | Yes | Offline version only, upgrade manually |
| Default live port | 7496 | 4001 |
| Default paper port | 7497 | 4002 |
| Best for | Learning the API, watching orders fill | Unattended 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.
- 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.
- 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.
- Trusted IPs. Add
127.0.0.1if 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) | |
|---|---|---|---|
| Transport | Socket to TWS/Gateway | Socket to TWS/Gateway | HTTPS REST + WebSocket |
| Needs desktop app running | Yes | Yes | No for OAuth; yes for the local Client Portal Gateway |
| Programming model | EClient + EWrapper callbacks, two threads | Sync or asyncio, objects stay in sync automatically | Request/response, session cookie via /tickle |
| Install | Download from IBKR, setup.py install | pip install ib_async | HTTP client of your choice |
| Maintained by | Interactive Brokers | ib-api-reloaded community org | Interactive Brokers |
| License | IBKR terms | BSD-2-Clause | IBKR terms |
| Good for | Full protocol access, latest fields first | Most Python bots | Web 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:
connect()opens the socket. If it hangs, the API is disabled, the port is wrong, or the IP is not trusted.qualifyContracts()resolves your partial description into one unambiguous contract with aconId. Skipping it is the most common cause of "no security definition found" rejections.placeOrder()returns aTradeobject that stays updated as fills arrive.MarketOrderis the simplest type; for anything beyond a test, read market vs limit orders and use a limit.ib.sleep()is nottime.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.
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.
- 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.
- Assert the port at startup. Refuse to run on a live port unless an environment variable such as
ALLOW_LIVE=1is set. - Keep Read-Only API on until orders are tested. It is IBKR's default for a reason.
- Hard caps in code. Max notional per order, max open positions, max daily loss.
- Reconcile on reconnect. Re-request positions and open orders after every reconnect. Cancel anything the bot does not recognize.
- 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.
- 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.
- Separate usernames. One for the Gateway session, one for Client Portal, so a browser login does not knock the bot offline.
- 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.
Read next
Keep learning with a related guide before putting an idea on your watchlist.
How to Build a Trading Bot: Step-by-Step Guide (2026)
Learn how to build a trading bot from scratch — architecture, data feeds, strategy logic, risk management, and broker connectivity. Includes a practical framework for beginner and intermediate algorithmic traders.
How to Choose a Broker for Algorithmic Trading (2026 Guide)
Compare broker APIs, Rule 605 execution quality and Rule 606 routing reports, paper environments and costs using the same order profile.
Build an AI Trading Agent with Alpaca Python: Paper Only
Build an Alpaca Python paper agent with completed IEX bars, validated model proposals, bounded bracket requests, dry run, and order reconciliation.
How to Automate Stock Trading with Alpaca in 2026
Learn Alpaca stock automation with an offline Python request preview, paper-account setup, order states, and failure reconciliation before execution.
Tradier API Trading Bot in Python: Setup, Orders, and Automation
Build a Tradier API trading bot in Python: sandbox vs production URLs, bearer auth, quotes, option chains, order fields, status polling, and rate limits.
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.
Related Signal Types
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.