Opening checkout

Platforms and brokers9 min read

Alpaca API: How to Build and Run a Python Trading Bot

Set up the Alpaca Trading API in Python: keys, paper trading, orders, market data streams and rate limits, then run your bot 24/7 on a server.

By QuantVPS Team

Published
Updated
Alpaca API: How to Build and Run a Python Trading Bot

The Alpaca API lets you trade US stocks, ETFs, options and crypto from code, and Alpaca's official Python SDK, alpaca-py, wraps it in a few classes: TradingClient for orders and StockHistoricalDataClient or StockDataStream for prices. Every user gets a free paper trading account with a $100k simulated balance, so you can build and test a bot against live quotes before you risk money (checked on 10 Oct 2026).

This guide covers the keys, the switch between paper and live, market data, orders, rate limits and a short example bot that trades on paper unless you pass an explicit --live flag.

What the Alpaca API Offers

Alpaca runs three APIs: the Trading API for your own account, the Market Data API for prices, and the Broker API for businesses that build brokerage apps on top of Alpaca. A personal trading bot uses the first two.

API What it does Host
Trading API Account, assets, orders, positions and order updates api.alpaca.markets (live), paper-api.alpaca.markets (paper)
Market Data API Historical and real-time bars, trades and quotes over HTTP and WebSocket data.alpaca.markets
Broker API Accounts and trading for a business's own customers broker-api.alpaca.markets

Hosts are from Alpaca's authentication docs, checked on 10 Oct 2026. Alpaca says equity trading is commission free, while crypto trades pay a volume-tiered fee that starts at 15 basis points for makers and 25 for takers.

Creating an Account and API Keys

Sign up at Alpaca, open the dashboard, find the API Keys section in the right sidebar and click Generate New Keys. Save both the key ID and the secret; if you lose the secret, regenerate the pair from the same panel.

  • Paper Only Account: Alpaca says anyone globally can open one with just an email address. It runs the full API in the paper environment and gets IEX market data.
  • Live account: a real-money brokerage account for individuals or businesses, plus crypto accounts.
  • Separate keys: each account has its own credentials. Live keys do not work on the paper API, and paper keys do not work on the live API.

Keep the keys out of your code. The examples below read them from the APCA_API_KEY_ID and APCA_API_SECRET_KEY environment variables, the same names as the headers Alpaca's REST API uses.

Uptime

99.999% uptime. Built for 24/7 trading reliability.

Your VPS runs in Tier IV datacenters with redundant power and network, monitored 24/7 on site.

See the uptime SLA
A dot-matrix heatmap resolves into the 99.999% uptime SLA figure. An illustration, not live status.

Alpaca Paper Trading: Switching Between Paper and Live

Alpaca paper trading is a free, real-time simulation: orders go through the same API as live trading, but Alpaca fills them against current quotes instead of sending them to an exchange. To switch, you change two things, the API keys and the host; the code stays the same.

Comparison of an Alpaca paper account and a live account: the paper Trading API host is paper-api.alpaca.markets and the live host is api.alpaca.markets; each account has its own keys; alpaca-py uses paper=True or paper=False; paper money is simulated with a $100k starting balance while live uses real funds; paper fills are simulated from live quotes while live orders are routed to the market; both use data.alpaca.markets for market data.

Switching from paper to live means new keys and a new host, not new code.

In alpaca-py, the host is set by one argument: TradingClient(key, secret, paper=True) calls paper-api.alpaca.markets, and paper=False (the default) calls api.alpaca.markets. For other tools, Alpaca says to set APCA_API_BASE_URL=https://paper-api.alpaca.markets. Market data comes from data.alpaca.markets in both modes.

A new paper account starts with $100k. You can no longer reset one from the dashboard; instead you create a new paper account (click the paper account number, then Open New Paper Account) or delete one under Account Settings, and generate new keys for any new account. Rules from the paper trading docs, checked on 10 Oct 2026:

  • Orders fill only when they become marketable, matched against the NBBO.
  • Order size is not checked against the quoted size, and about 10% of eligible orders get a random partial fill.
  • Paper trading does not simulate dividends, market impact, slippage from latency, queue position, price improvement or regulatory fees.
  • No fill emails are sent.

So a paper result is a test of your code, not a forecast of live results. Treat any live switch as a new test with small size.

Installing alpaca-py and Connecting

Install the SDK with pip. The current alpaca-py package on PyPI requires Python 3.10 or later (some Alpaca docs pages still say 3.7); the examples here were run with alpaca-py 0.44.0 on Python 3.12.

pip install alpaca-py

Then create a TradingClient with your paper keys and read the account. If the keys are wrong for the host, the call fails with an APIError saying "unauthorized."

import os
from alpaca.trading.client import TradingClient

trading = TradingClient(
    os.environ["APCA_API_KEY_ID"],
    os.environ["APCA_API_SECRET_KEY"],
    paper=True,   # paper-api.alpaca.markets; paper=False uses api.alpaca.markets
)
account = trading.get_account()
print(account.status, account.buying_power)

Market Data: Quotes, Bars and WebSocket Streams

Use a historical client for one-off requests and a stream client for live prices. Stock data needs your keys; crypto data does not, although keys raise your rate limit. Results are keyed by symbol even when you ask for one symbol.

import os
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.live import StockDataStream
from alpaca.data.requests import StockLatestQuoteRequest

key, secret = os.environ["APCA_API_KEY_ID"], os.environ["APCA_API_SECRET_KEY"]

# One-off request: latest quote (keyed by symbol, even for one symbol)
client = StockHistoricalDataClient(key, secret)
quotes = client.get_stock_latest_quote(StockLatestQuoteRequest(symbol_or_symbols="SPY"))
print("SPY ask", quotes["SPY"].ask_price)

# Live stream: the handler runs for every new quote
stream = StockDataStream(key, secret)

async def on_quote(quote):
    print(quote.symbol, quote.bid_price, quote.ask_price)

stream.subscribe_quotes(on_quote, "SPY")
stream.run()

StockDataStream uses the IEX feed by default, which is the real-time feed on the free plan. Alpaca's test stream at wss://stream.data.alpaca.markets/v2/test with the symbol FAKEPACA works outside market hours, which helps when you build a handler on a weekend.

Your own order updates arrive on a separate stream: TradingStream(key, secret, paper=True) with subscribe_trade_updates(handler) reports each fill, cancel and rejection. Alpaca recommends the stream as the way to track order state, rather than polling.

DDoS protection

Always-on DDoS protection with advanced filtering. On every plan.

Always-on mitigation filters attack traffic at the network edge. Every plan includes a dedicated IP and unmetered bandwidth for the trading environment.

See every security layer
A flood of traffic meets three filter layers at the network edge. Only clean packets reach your VPS.

Placing Orders

Every order is a request object passed to submit_order(): MarketOrderRequest, LimitOrderRequest, StopOrderRequest or TrailingStopOrderRequest. For stocks, notional (a dollar amount) works only on market orders; for crypto it works on any order type.

from alpaca.trading.enums import OrderSide, TimeInForce
from alpaca.trading.requests import MarketOrderRequest

# Market order: buy $200 of SPY (notional orders are fractional)
order = trading.submit_order(order_data=MarketOrderRequest(
    symbol="SPY",
    notional=200,
    side=OrderSide.BUY,
    time_in_force=TimeInForce.DAY,
))
print(order.id, order.status)

A few rules from Alpaca's orders docs matter for bots (checked on 10 Oct 2026):

  • Extended hours: only limit orders with time_in_force of day or gtc and extended_hours=True can trade overnight (8:00pm to 4:00am ET, Sunday to Friday), pre-market (4:00am to 9:30am ET) or after hours (4:00pm to 8:00pm ET).
  • Crypto: market, limit and stop limit orders, with time_in_force of gtc or ioc. Crypto trades all day, seven days a week, and one order cannot exceed $200k notional.
  • Buying power: open orders count against buying power until they fill or are cancelled.
  • Cleanup: cancel_orders() cancels every open order and close_all_positions(cancel_orders=True) flattens the account.

Rate Limits and Errors

Alpaca's support FAQ says the Trading API is throttled at 200 requests per minute per account, and you cannot raise it yourself. Market data has its own limits, set by your data plan.

Limit (checked on 10 Oct 2026) Basic (free) Algo Trader Plus ($99 a month)
Historical data API calls 200 a minute 10,000 a minute
Real-time stock coverage IEX only All US stock exchanges
Stock WebSocket symbols 30 Unlimited
Recent historical data Not the latest 15 minutes No restriction
Options WebSocket quotes 200 1,000

Source: Market Data API plans. Over the limit, the REST API returns HTTP 429, and the X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers show where you stand. alpaca-py 0.44.0 retries a 429 or 504 three times, three seconds apart, before it raises an APIError.

Streams have their own errors. Most plans allow one data stream connection, so a second script or app on the same account gets error 406, "connection limit exceeded". Error 405 means you subscribed to more symbols than your plan allows, and 407 means your handler is too slow to keep up.

A Simple Strategy Example

The script below checks Bitcoin once a day: if the last closed daily bar is above its 20-day average and you hold no BTC, it buys $100 at market; if the close falls below the average, it closes the position. It runs on your paper account unless you start it with --live, so real orders always need a deliberate choice.

import os
import sys
from datetime import datetime, timedelta, timezone

from alpaca.data.historical import CryptoHistoricalDataClient
from alpaca.data.requests import CryptoBarsRequest
from alpaca.data.timeframe import TimeFrame
from alpaca.trading.client import TradingClient
from alpaca.trading.enums import OrderSide, TimeInForce
from alpaca.trading.requests import MarketOrderRequest

LIVE = "--live" in sys.argv   # paper trading unless you pass --live
SYMBOL = "BTC/USD"
LOOKBACK = 20                 # days in the moving average
NOTIONAL = 100                # dollars per buy

# 1. Signal: last closed daily bar against its 20-day average
data = CryptoHistoricalDataClient()   # no keys needed for crypto data
bars = data.get_crypto_bars(CryptoBarsRequest(
    symbol_or_symbols=SYMBOL,
    timeframe=TimeFrame.Day,
    start=datetime.now(timezone.utc) - timedelta(days=LOOKBACK * 2),
)).df
closes = bars["close"].iloc[:-1]      # drop today's unfinished bar
last = closes.iloc[-1]
average = closes.iloc[-LOOKBACK:].mean()
print(f"{SYMBOL} close {last:.2f}, {LOOKBACK}-day average {average:.2f}")

# 2. Act: paper account by default
trading = TradingClient(
    os.environ["APCA_API_KEY_ID"],
    os.environ["APCA_API_SECRET_KEY"],
    paper=not LIVE,
)
held = [p for p in trading.get_all_positions()
        if p.symbol.replace("/", "") == SYMBOL.replace("/", "")]

if last > average and not held:
    order = trading.submit_order(order_data=MarketOrderRequest(
        symbol=SYMBOL,
        notional=NOTIONAL,
        side=OrderSide.BUY,
        time_in_force=TimeInForce.GTC,
    ))
    print("buy submitted", order.id, order.status)
elif last < average and held:
    order = trading.close_position(held[0].symbol)
    print("exit submitted", order.id, order.status)
else:
    print("no change")

Run it with python bot.py for paper or python bot.py --live with live keys. The rule shows the plumbing; it is not a strategy we recommend, and paper results will not match live fills.

Before you trust a rule, test it on history. Our guide to Python backtesting libraries compares the main options. Our guide to trading bot strategies covers common rule types.

Running Your Alpaca Bot 24/7

A bot can only trade while its computer is on and connected. A daily script needs a scheduler (cron on Linux, Task Scheduler on Windows); a streaming bot needs a process that restarts after a crash and reconnects after a network drop.

  • Restart on failure: run the script as a service so it comes back after an error or a reboot.
  • Watch for stale streams: stream clients have data_timeout off by default; pass data_timeout=60 to reconnect when a socket stays open but stops sending data.
  • Log order IDs: keep each order's id and the X-Request-ID response header, which Alpaca support asks for.
  • One stream per account: a second copy of the bot will hit the 406 connection limit.

QuantVPS hosts its Alpaca VPS in New York, with a published 0 to 10ms route to Alpaca, always-on DDoS protection with advanced filtering and automatic off-site backups on every VPS plan. Its servers run with 99.999% uptime. Performance plans use the AMD Ryzen 9 9950X, which helps when you backtest on the same server. Plans start at $29.99 a month (checked on 10 Oct 2026).

Order flow

Built for scalpers. Heatmaps on dedicated resources.

Order-flow heatmaps and DOM ladders run on dedicated CPU, memory and NVMe, so your charts keep responding during volatility.

See the dedicated hardware
Order flow through a news release: resting bids and offers paint in as a heatmap beside the DOM ladder, then a burst of buys takes the offer wall. Illustrative; prices are decorative.

FAQs

Is the Alpaca API free?

Paper trading and the Basic market data plan are free, and Alpaca says equity trading is commission free. Crypto trades pay a tiered fee from 15 basis points (maker) and 25 basis points (taker), and full market data is $99 a month on Algo Trader Plus (checked on 10 Oct 2026).

How do I switch Alpaca from paper trading to live?

Generate keys on your live account and use the live host. In alpaca-py, that means passing live keys and paper=False to TradingClient; other tools use https://api.alpaca.markets as the base URL. Paper keys never work on the live API.

What is the Alpaca API rate limit?

Alpaca's support FAQ gives 200 requests per minute per account for the Trading API. Historical market data allows 200 calls a minute on the free plan and 10,000 on Algo Trader Plus. Over the limit you get HTTP 429.

Can I use the Alpaca API outside the US?

Yes. Anyone can open a Paper Only Account with an email address, and Alpaca says it supports live brokerage accounts for individuals and businesses from around the world, after onboarding with Alpaca Securities LLC. Retirement accounts are not supported, and business accounts need a $30,000 minimum deposit (checked on 10 Oct 2026).

Which Python library should I use for Alpaca?

Use alpaca-py, Alpaca's official Python SDK (pip install alpaca-py). It replaces the older alpaca-trade-api package and covers the Trading, Market Data and Broker APIs.

Share this article

Disclaimer: QuantVPS does not represent, guarantee, support, or endorse any third-party brands, products, or services mentioned in this article. All brand references are for informational purposes only. This information does not constitute a recommendation to trade futures or any other financial instruments. All trading decisions are made at your own discretion. Please be aware that futures trading involves significant risk of loss, and past performance does not guarantee future results. Read our full Brand Non-Endorsement Disclaimer.

Risk Disclosure: QuantVPS does not provide financial, investment, or trading advice. Trading involves substantial risk of loss and is not suitable for every investor. Past performance is not indicative of future results. You should consult a qualified financial advisor before making any trading decisions. Read our full Trading Disclaimer.