Stage 3 of 4, lesson 17 of 31. Improver
Your first API call: read your positions on a demo account
Log in to a demo account from Python and read your open positions. The script only reads: it cannot place a trade. It introduces HTTP requests, headers, sessions and pacing.
Risk warning: CFDs and spread bets are complex instruments and come with a high risk of losing money rapidly due to leverage. You can lose money on these products, and each provider publishes its own loss figure under FCA rules. You should consider whether you understand how they work and whether you can afford to take the high risk of losing your money. Automated and API trading can amplify losses through bugs, connectivity failures and runaway orders.
You need: The lessons on reading an API response and on keys, an IG demo account with an API key, and the requests package.
This is the same job as the lesson on reading an API response, except the answer now comes from a broker's demo system instead of a file. Install the one package it needs with: pip install requests
The script does three things. It logs in, which returns two tokens. It waits. Then it asks for your positions, sending the tokens back so the broker knows who is asking.
The wait matters. Brokers limit how many requests you may send, and a loop with no pause can spend a week's allowance in seconds. The limits below are read from this site's register. Where the broker's own pages disagree, pace against the lower figure.
When a call fails, the answer carries an error code. Do not guess: look the code up. The error pages on this site say what each one means and how to fix it.
This is the one lesson in the track that needs a broker account, and it is optional. Every lesson after it runs offline, so you can read this one and carry on.
The code
# Your first real API call, against a DEMO account only.
# Set these three environment variables on your own machine first.
# Never type them into this file, and never type them into any website, including this one.
# IG_API_KEY, IG_USERNAME, IG_PASSWORD
# They are the same three names the IG quickstart on this site uses, so a value set for one works for the other.
import os
import time
import requests
BASE_URL = "https://demo-api.ig.com/gateway/deal" # the demo host, not the live one
session = requests.Session()
session.headers.update({
"X-IG-API-KEY": os.environ["IG_API_KEY"],
"Accept": "application/json; charset=UTF-8",
"Content-Type": "application/json; charset=UTF-8",
})
# Step 1: log in. The answer carries two tokens in its headers.
login = session.post(
BASE_URL + "/session",
json={"identifier": os.environ["IG_USERNAME"], "password": os.environ["IG_PASSWORD"]},
headers={"VERSION": "2"},
timeout=10,
)
login.raise_for_status()
session.headers.update({
"CST": login.headers["CST"],
"X-SECURITY-TOKEN": login.headers["X-SECURITY-TOKEN"],
})
# Step 2: wait a moment between calls. A script that never pauses is how limits get broken.
time.sleep(2)
# Step 3: read your open positions. This call only reads. It cannot place a trade.
positions = session.get(BASE_URL + "/positions", headers={"VERSION": "2"}, timeout=10)
positions.raise_for_status()
for item in positions.json()["positions"]:
print(item["market"]["instrumentName"], item["position"]["direction"], item["position"]["size"])Test pending. This script has been checked for syntax and written from the endpoints recorded on this site. It has not been run end to end against a demo account, so it carries no tested mark. Credentials are read from environment variables on your own machine and are never entered on this site.
IG limits, from the register
| Limit | Value | Scope | Source |
|---|---|---|---|
| Historical price data points a week | 10,000 | Per account, price history endpoints only | labs.ig.comchecked |
| Non trading requests a minute | 30 | Per account, non trading calls only | labs.ig.comchecked |
| Non trading requests a minute | 60 | Per application, that is per API key, non trading calls only | labs.ig.comchecked |
| Concurrent streaming subscriptions | 40 | Per streaming connection | labs.ig.comchecked corroborated by labs.ig.com/streaming-api-guide.html |
| Trading requests a minute | 100Sources conflict ig.com states around 40, checked . IG's own pages disagree. The conflict is labelled, not resolved. Pace against 40. | Per account, trading calls only | labs.ig.comchecked |
Endpoints this lesson uses
Errors you may meet
- error.security.account-access-deniedThe account has been denied login privileges.
- error.security.account-migratedThe account has been migrated to IG's client and account model.
- ACCOUNT_NOT_ENABLED_TO_TRADINGThe account is not enabled to trade. IG returns this in the reason field of a deal confirmation.
- error.security.account-not-yet-activatedThe account has not been activated yet.
- error.security.account-suspendedThe account has been suspended.
- error.security.account-token-invalidThe endpoint needs an account token and the one sent was not valid.
Check yourself
Answer each question from the code above before you open it. The answers were written from the script on this page and what it printed.
Where do the key, the username and the password come from?
From three environment variables, read with os.environ. None of them is written in the file, so the script can be shared without sharing them.
What is the time.sleep(2) between the two calls for?
It pauses for two seconds so the script does not send requests back to back. The comment says why: a script that never pauses is how limits get broken.
What does raise_for_status do after each call?
It stops the script with an error if the broker answered with an error status, instead of carrying on and trying to read a reply that is not there.
Exercise
Before listing the positions, print how many are open. Keep the reply in a variable so the script still makes only one positions call.
Test pending. The solution below has been checked for syntax only. It has not been run against a demo account, so no expected output is printed for it.
One solution, not yet run
# Exercise solution: the lesson script with the one change the exercise asks for.
# Your first real API call, against a DEMO account only.
# Set these three environment variables on your own machine first.
# Never type them into this file, and never type them into any website, including this one.
# IG_API_KEY, IG_USERNAME, IG_PASSWORD
# They are the same three names the IG quickstart on this site uses, so a value set for one works for the other.
import os
import time
import requests
BASE_URL = "https://demo-api.ig.com/gateway/deal" # the demo host, not the live one
session = requests.Session()
session.headers.update({
"X-IG-API-KEY": os.environ["IG_API_KEY"],
"Accept": "application/json; charset=UTF-8",
"Content-Type": "application/json; charset=UTF-8",
})
# Step 1: log in. The answer carries two tokens in its headers.
login = session.post(
BASE_URL + "/session",
json={"identifier": os.environ["IG_USERNAME"], "password": os.environ["IG_PASSWORD"]},
headers={"VERSION": "2"},
timeout=10,
)
login.raise_for_status()
session.headers.update({
"CST": login.headers["CST"],
"X-SECURITY-TOKEN": login.headers["X-SECURITY-TOKEN"],
})
# Step 2: wait a moment between calls. A script that never pauses is how limits get broken.
time.sleep(2)
# Step 3: read your open positions. This call only reads. It cannot place a trade.
positions = session.get(BASE_URL + "/positions", headers={"VERSION": "2"}, timeout=10)
positions.raise_for_status()
open_positions = positions.json()["positions"]
print("Open positions:", len(open_positions))
for item in open_positions:
print(item["market"]["instrumentName"], item["position"]["direction"], item["position"]["size"])Your progress
Your marks are kept in this browser only, in its local storage. They are not sent anywhere, there is no account, and they will not follow you to another browser or device. To remove them, use Clear my progress on the learning track page, or clear this site's data in your browser's settings. Marking needs JavaScript. With scripts off the track reads the same, without the ticks.
This browser refused to store anything for this page, so marks cannot be kept here.
Where this sits in the path
Stage 3 of 4: Talking to a broker. What an API sends back, real requests to a practice API that needs no account, how to keep a key safe, a first read only call on a demo account, and histories that arrive in pages.
- Read an API responseDone
- First HTTP requestDone
- Positions and exposureDone
- Keys and demo accountsDone
- First API callDoneYou are here
- Paged resultsDone
- Follow the next pageDone
- Price history to CSVDone
Repositories to read next
- ib-api-reloaded/ib_async1,750 stars, read . Python sync/async framework for Interactive Brokers API (replaces ib_insync)
- alpacahq/alpaca-py1,542 stars, read . The Official Python SDK for Alpaca API
- EA31337/EA313371,307 stars, read . EA31337 Lite, Advanced and Rider - Forex multi-strategy trading robot for MT4/MT5 platforms
- gnzsnz/ib-gateway-docker1,263 stars, read . Docker image with IB Gateway/TWS and IBC
- Voyz/ibeam857 stars, read . IBeam is an authentication and maintenance tool used for the Interactive Brokers Client Portal Web API Gateway.
- alpacahq/alpaca-backtrader-api704 stars, read . Alpaca Trading API integrated with backtrader