Executing Orders

  1. Can we execute multiple orders at once? We are currently trying to send 1000 bracket orders. What is the quickest and most efficient way to execute all the orders.
  2. While executing orders I run into this - ’ [Sleep 3 seconds and retrying https://paper-api.alpaca.markets/v2/orders 3 more time(s)]'. I just upgraded to Alpaca Unlimited Subscription so I am not sure why I am receiving this error if API Calls are unlimited. Please advise.

@suraj Standard accounts are limited to 200 calls/minute. Orders must be placed individually so it would take a minimum of 1000 calls to place 1000 orders.

The simple way to ensure one doesn’t exceed the rate limit is to wait 1/200 minutes (or .3 seconds) between calls. A more sophisticated method to maximize throughput is to look at the header information of the last API call. All Trading API responses include 3 rate related pieces of data in their header

X-Ratelimit-Limit: 200
X-Ratelimit-Remaining: 195
X-Ratelimit-Reset: 1679411617

The X-Ratelimit-Limit is static and simply tells you the account’s rate limit. The X-Ratelimit-Remaining tells you how many calls you can make until the account’s rate counter is reset. The X-Ratelimit-Reset is the next time (in seconds epoch time) when the rate counter will reset. One way to use this information is to send orders as fast as you wish. If one gets a "429 rate limit exceeded" error, simply look at the header X-Ratelimit-Reset time. Pause until that time and then continue.

There is also a new program called Alpaca Plus which, for a fee, one can get increased rates (up to 1000/minute) along with some other perks. You may want to check that out.

How am I to access the header information of the last API call?

I upgraded and have an Unlimited Plan and the pricing options say that there are unlimited API calls with that plan. Why am I being limited to 200/calls?

@suraj The data plans which Alpaca offers (ie the “Unlimited” plan) are completely separate offerings than general account access. The rate limit for the market data APIs (ie https://data.alpaca.markets) is separate than the rate limit to access one’s account via the trading APIs (ie https://api.alpaca.markets and https://paper-api.alpaca.markets). Those trading API rate limits are independant of any data plan you may choose and do not change.

The header responses can be fetched in different ways depending upon the SDK or method you use to make the API calls. For example, using Postman they appear in the ‘headers’ tab

Which language and/or SDK are you using?

We are using Python as the programming language and Python’s SDK provided by alpaca. How can I extract the header responses using Python? Thank you for all the help.

In addition, how much does Alpaca Plus plan cost?

Hi Dan, just want to follow up on the above request.

I read the headers in python and put it in a try/catch like Dan recommended. I had to do it for myself so i figured I’d put it here for anyone in the future.

import sys, time, json, requests
import pandas as pd
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

# global variables and constants
ratelimit_remaining = 200 # 1000
ratelimit_reset = 0
num_api_calls = 0
rate_limit_reached = False
QUERIES_TO_DO_AFTER_RATE_LIMIT_REACHED = 3
queries_done_after_rate_limit_reached = 0
TIMEZONE = 'US/Eastern' # 'US/Pacific' # 'UTC'
exchange = 'iex'
ticker_symbols = ['AAPL', 'JNJ', 'CVX']
API_KEY    = ""
API_SECRET = ""
HEADERS = {
    "accept": "application/json",
    "APCA-API-KEY-ID": API_KEY,
    "APCA-API-SECRET-KEY": API_SECRET
}

def get_latest_quotes():

    global ratelimit_remaining, ratelimit_reset, num_api_calls
    print("\nquerying alpaca ... ")
    print(f'X-Ratelimit-Remaining = {ratelimit_remaining}')
    print(f'X-Ratelimit-Reset     = {ratelimit_reset}')
    print(f'current_time          = {int(time.time())}')
    print(f'number of API calls made so far: {num_api_calls}')
    # if ratelimit_remaining <= 0: # avoid rate limit error all together
    #     seconds_till_reset = ratelimit_reset - time.time()
    #     print(f'waiting {"%.2f" % seconds_till_reset} second(s) to query API again to not surpass API rate limit')

    # get quotes with requests library
    # https://docs.alpaca.markets/reference/stocklatestquotes
    symbols = '%2C'.join(ticker_symbols) # ex: AAPL%2CTSLA%2CMSFT
    url = f"https://data.alpaca.markets/v2/stocks/quotes/latest?symbols={symbols}&feed={exchange}"
    query_time = datetime.now().strftime('%Y-%m-%d %I:%M:%S %p %Z')
    try:
        response = requests.get(url, headers=HEADERS)
        num_api_calls += 1
        response.raise_for_status()
        ratelimit_reset = int(response.headers['X-Ratelimit-Reset'])
        ratelimit_remaining = int(response.headers['X-Ratelimit-Remaining'])
        print_quotes(response, query_time)

    # if rate limit is reached, wait until X-Ratelimit-Reset
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 429:
            print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RATE LIMIT REACHED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
            print(json.dumps(dict(e.response.headers), indent=4))
            print(json.dumps(json.loads(e.response.text), indent=4))
            global rate_limit_reached
            rate_limit_reached = True
            seconds_till_reset = ratelimit_reset - time.time()
            print(f'must wait {"%.2f" % seconds_till_reset} second(s) to query API again')
            time.sleep(seconds_till_reset)
        else:
            raise e

def print_quotes(response, query_time):
    print(f'quote {query_time}')
    quotes = json.loads(response.text)['quotes']
    # print(json.dumps(quotes, indent=4))
    df = pd.DataFrame(columns=[
        'ticker',
        'highestBid',
        'lowestAsk',
        'bidSize',
        'askSize',
        'exchange',
        'quote_query_time',
    ])
    for t, ticker in enumerate(ticker_symbols):
        df = pd.concat([
            df if not df.empty else None,
            pd.DataFrame({
                'ticker'           : ticker,
                'highestBid'       : quotes[ticker]['bp'],
                'lowestAsk'        : quotes[ticker]['ap'],
                'bidSize'          : quotes[ticker]['bs'],
                'askSize'          : quotes[ticker]['as'],
                'exchange'         : exchange,
                'quote_query_time' : query_time,
            }, index=[t + 1])
        ])
        # NOTE: in the quote response 'V' represents the IEX exchange
        # https://docs.alpaca.markets/reference/stocklatestquotes
    df_str = df.to_string(max_rows=df.shape[0])
    print(df_str)

print('bid/ask spread datafeed:')
while True:
    get_latest_quotes()
    if rate_limit_reached:
        if queries_done_after_rate_limit_reached >= QUERIES_TO_DO_AFTER_RATE_LIMIT_REACHED:
            print('\nrate limit test complete\n')
            sys.exit() # this line is for testing purposes only
        else:
            queries_done_after_rate_limit_reached += 1

Side Note: Unfortunately the alpaca-py library “does not directly expose these headers in its high-level API.” - kapa.ai. I was looking in the docs and couldn’t find anything either, so I used the requests library to query the Alpaca URLs directly. It’d be nice if alpaca-py could do this, otherwise I’ll have to not use it, or wait 1/200th a second between API calls.