Home › Guides › Trading bots & code
Interactive Brokers API basics: TWS, IB Gateway and Python
Interactive Brokers' API doesn't talk to the internet directly. It talks to a program on your own computer — TWS or IB Gateway — which talks to IBKR. Once that clicks, the rest is ordinary code.
How the pieces connect
Most broker APIs are a web address. Interactive Brokers (IBKR) is different for its classic API, called the TWS API:
- Your script connects to a program running on your machine.
- That program is either TWS (Trader Workstation, the full trading app) or IB Gateway (a smaller app with no charts, made for API use).
- TWS or IB Gateway connects to IBKR's servers.
So TWS or IB Gateway must be open and logged in for your script to work. If it closes, your script loses its connection — something your code must handle.
IBKR also offers web-based APIs. This page covers the TWS API, which is the one most Python examples use.
Paper first: the paper account
IBKR accounts come with a paper trading account (simulated money). You log into TWS or IB Gateway and choose paper mode.
Each mode listens on its own default port (a numbered "door" on your computer):
| Program | Paper port | Live port |
|---|---|---|
| TWS | 7497 | 7496 |
| IB Gateway | 4002 | 4001 |
These are defaults and can be changed in the settings. The port in your script must match the one in TWS or IB Gateway. Using the paper port is a simple guard: a script pointed at 7497 can't reach a live TWS on 7496.
The 2 settings that trip up beginners
In TWS or IB Gateway, look for the API settings (in TWS, under the global configuration, then API, then Settings). Then:
- Turn on "Enable ActiveX and Socket Clients". Without it, scripts can't connect at all.
- Look at "Read-Only API". It's on by default and blocks all orders from code. Leave it on while you're only reading data. Turn it off only on the paper account, when you're ready to test orders.
While you're there, set Trusted IPs to only your own computer (127.0.0.1), so nothing else on the network can connect.
Which Python library?
- ibapi — IBKR's official Python library, part of the TWS API download. It works through "callbacks": you send a request, and the answer arrives later in a separate function. Powerful, but wordy for beginners.
- ib_async — a free community library that wraps the official API in a simpler style. It's the successor to the older ib_insync, whose author died in 2024; IBKR's own docs point ib_insync users to it. It needs Python 3.10 or newer.
The example below uses ib_async (pip install ib_async pandas). IBKR's support team can't help with third-party libraries, so keep its docs handy.
Step 1: Connect and read prices
Example code for learning — test it on paper first; it is not a ready-made trading system.
from ib_async import IB, Stock, util
ib = IB()
ib.connect("127.0.0.1", 7497, clientId=1) # 7497 = TWS paper (default)
contract = Stock("SPY", "SMART", "USD")
ib.qualifyContracts(contract) # ask IBKR to fill in the details
bars = ib.reqHistoricalData(
contract,
endDateTime="", # "" = up to now
durationStr="60 D",
barSizeSetting="1 day",
whatToShow="TRADES",
useRTH=True, # regular hours only
)
df = util.df(bars)
print(df[["date", "open", "high", "low", "close"]].tail(3))
In plain words:
- ib.connect connects to TWS on this computer (127.0.0.1), on the paper port. clientId is a number that names this script; each script connected at the same time needs its own.
- Stock("SPY", "SMART", "USD") describes what you want: SPY shares, routed by IBKR's smart routing, in US dollars.
- qualifyContracts asks IBKR to fill in the exact contract details. Do this before using a contract.
- reqHistoricalData asks for 60 days of daily candles, regular trading hours only.
- util.df turns the answer into a pandas table.
Market data may need a subscription on your account. If you see an error about market data permissions, check the market data settings in your IBKR account.
Step 2: Errors, disconnects and a bracket order
def on_disconnect():
print("Disconnected. No new orders until we reconnect and re-check positions.")
ib.disconnectedEvent += on_disconnect
def on_error(reqId, errorCode, errorString, contract):
print("IB error", errorCode, errorString) # log it, don't ignore it
ib.errorEvent += on_error
bracket = ib.bracketOrder(
"BUY", 10,
limitPrice=500.00, # entry (a limit order)
takeProfitPrice=510.00, # target
stopLossPrice=495.00, # stop
)
for order in bracket: # parent, target, stop
trade = ib.placeOrder(contract, order)
print(trade.order.orderType, trade.orderStatus.status)
ib.sleep(2) # let messages arrive
ib.disconnect()
In plain words:
- disconnectedEvent and errorEvent let you run your own function when the connection drops or IBKR reports an error. Log both. After a disconnect, a bot should stop, reconnect, and check open orders and positions before doing anything new.
- bracketOrder builds 3 linked orders: a limit buy at 500, a take-profit sell at 510 and a stop sell at 495. ib_async sets them up so they're sent together; once the entry fills, the stop and target are live at IBKR.
- placeOrder sends each one. The loop goes through all 3.
- ib.sleep(2) pauses while still letting messages from IBKR arrive. Use it instead of Python's normal sleep inside ib_async scripts.
The numbers here are placeholders. In a real rule, the stop comes from the chart (for example, just past the last place price turned around), and the share count comes from the stop:
- 10,000 account × 1% = 100 at risk.
- Entry 500 − stop 495 = 5 per share.
- 100 ÷ 5 = 20 shares.
A math example, not a promise. Gaps can fill a stop beyond its price. Our position size calculator does the same math.
The risk controls to add next
The example sends orders. A bot needs more around it:
- A daily loss limit. Track account value from the start of the day; stop new orders after, say, a 2% drop. See daily loss limit.
- A kill switch you can flip without editing code.
- A maximum order size, so a typo can't send 1,000 shares instead of 10.
- Order checks. Confirm each order's status. Never resend blindly after an error.
- Logs of every request, reply and error.
- A daily restart plan. TWS and IB Gateway restart or log out on a schedule, so your bot must survive reconnecting.
More: trading bot risk controls.
Test it properly
- Test the rule on past data first, avoiding look-ahead bias (using data before it existed) and overfitting (tuning until the past looks perfect). See how to backtest in Python.
- Run it on the paper account for weeks. See paper trading a bot.
- Compare its trades with what your written rule says should have happened.
Paper fills are simulated and can look better than real fills, so expect real results to differ. Losses are possible with any bot.
The live port is one number away. 7497 vs 7496. Keep paper and live setups separate — different settings files, different login — and treat going live as a written decision, not a quick edit.
Common mistakes
- Forgetting TWS must be running. The script has nothing to connect to without it.
- Leaving Read-Only on, then fighting "order rejected" errors. Or turning it off on the live account too early.
- Two scripts with the same clientId. One will be kicked off.
- No reconnect logic. The first nightly restart breaks the bot.
Questions
Does Interactive Brokers have an API?
Yes. The TWS API works through TWS or IB Gateway running on your computer and supports Python, Java, C++ and C#, among others. IBKR also offers web-based APIs.
What is the difference between TWS and IB Gateway?
TWS is the full trading app with charts. IB Gateway is a lighter app with no trading screens, made to stay connected for API use. Both let scripts connect.
What port does the Interactive Brokers API use?
By default, TWS uses 7497 for paper and 7496 for live; IB Gateway uses 4002 for paper and 4001 for live. You can change these in the API settings.
Can I paper trade with the IBKR API?
Yes. Log into TWS or IB Gateway in paper mode and connect your script to the paper port. Test orders there for weeks before considering live trading.
Is ib_insync still maintained?
No. Its successor is ib_async, a community-maintained library that works the same way. Switching mostly means changing the import name.
Want the next step? We'll walk you through it.
Four free lessons by email, one every few days — each with a 10-minute exercise on a real chart.
Send Me the Free Lessons 4 short emails · No spam · Unsubscribe anytimeOr see the whole method, step by step:
See What’s Inside the Course Try everything free first. Decide when you're ready. · Open the free chart tool (works in the TradingView phone app)One chart idea a day, in plain English. Follow along: