Persistent 401 Unauthorized on data.alpaca.markets (trading API works fine)

I’m having a persistent authentication issue with the Market Data API that I haven’t been able to resolve after extensive troubleshooting.

Account type: Paper trading

Issue:
Requests to the Trading API (paper-api.alpaca.markets) authenticate successfully — I can retrieve account info and it works as expected.

However, requests to the Market Data API (data.alpaca.markets) consistently return a 401 Unauthorized error, but notably as a raw nginx HTML page rather than Alpaca’s standard JSON error format:

401 Authorization Required

401 Authorization Required


nginx

Example failing request:
GET https://data.alpaca.markets/v2/stocks/bars?start=...&timeframe=1Day&symbols=AAPL&limit=10000

What I’ve already tried:

  • Regenerated my API key/secret pair from the dashboard and tested with fresh credentials — same result
  • Verified the same credentials work correctly against the Trading API
  • Confirmed I’m not using a paid data feed and don’t believe one should be required for basic IEX historical bars
  • Upgraded to the latest alpaca-py Python SDK version
  • Verified network connectivity to data.alpaca.markets (server responds, just rejects auth)
  • Tested using the official alpaca-py client library (StockHistoricalDataClient)

I’d appreciate any insight into why this specific endpoint would reject valid, working credentials.

Try executing the curl command. It returns data for me. I assume something is wrong with your code?

curl -sS 
-H "APCA-API-KEY-ID: xxx" 
-H "APCA-API-SECRET-KEY: xxx" 
"https://data.alpaca.markets/v2/stocks/AAPL/bars?timeframe=1Day&start=2026-06-30T00:00:00Z&end=2026-07-14T23:59:59Z&limit=10&feed=iex&adjustment=raw&sort=desc"

@rblackman50 A 401 error means your API key and/or secret is incorrect. There are a few other causes (such as using the wrong base URL or incorrect header names), but if you are using the alpaca-py SDK those will not be an issue.

The first thing to verify is that you are actually using the API key and secret that you expect. You may want to print out those values immediately before you instantiate your StockHistoricalDataClient. Perhaps like this

print(my_data_key, my_secret)
data_client = StockHistoricalDataClient(my_data_key, my_secret)

I can check the logs, which often contain helpful information if you provide an approximate time (including time zone) and the exact call you are making. For instance

request_params = StockBarsRequest(
                                  symbol_or_symbols=ETFS_TO_TRADE,
                                  timeframe=TimeFrame.Day,
                                  start=pd.to_datetime('2024-01-01').tz_localize('America/New_York'),
                                  end=pd.to_datetime('now', utc=True).tz_convert('America/New_York'),
                                  adjustment='split',
                                  feed='sip'
                                  )

bars = data_client.get_stock_bars(request_params=request_params).df.tz_convert('America/New_York', level='timestamp')
1 Like