Python API error when requesting stock data

Hi everyone,

I am trying to explore Alpaca-py and the stock market. I tried requesting Historical stock bars using this code:

from alpaca.data.timeframe import TimeFrame
from alpaca.data.requests import StockBarsRequest
from alpaca.data.historical import StockHistoricalDataClient# Create stock historical data client

client = StockHistoricalDataClient(KEY_ID, SECRET_KEY)
request_params = StockBarsRequest(
                        symbol_or_symbols=["TSLA"],
                        timeframe=TimeFrame.Day,
                        start="2023-01-01 00:00:00"
                 )
bars = client.get_stock_bars(request_params)
bars_df = bars.df
print(bars_df)

But when I tried to run this code I got the following error:

I don’t understand why because it’s available with crypto data. Can someone please help me?

---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)
File /opt/homebrew/Caskroom/miniconda/base/envs/trading/lib/python3.11/site-packages/alpaca/common/rest.py:196, in RESTClient._one_request(self, method, url, opts, retry)
    195 try:
--> 196     response.raise_for_status()
    197 except HTTPError as http_error:
    198     # retry if we hit Rate Limit

File /opt/homebrew/Caskroom/miniconda/base/envs/trading/lib/python3.11/site-packages/requests/models.py:1021, in Response.raise_for_status(self)
   1020 if http_error_msg:
-> 1021     raise HTTPError(http_error_msg, response=self)

HTTPError: 403 Client Error: Forbidden for url: https://data.alpaca.markets/v2/stocks/bars?start=2023-01-01T00%3A00%3A00Z&timeframe=1Day&symbols=TSLA

During handling of the above exception, another exception occurred:

APIError                                  Traceback (most recent call last)
Cell In[20], line 11
      5 client = StockHistoricalDataClient(KEY_ID, SECRET_KEY)
      6 request_params = StockBarsRequest(
      7                         symbol_or_symbols=["TSLA"],
      8                         timeframe=TimeFrame.Day,
      9                         start="2023-01-01 00:00:00"
     10                  )
---> 11 bars = client.get_stock_bars(request_params)
     12 bars_df = bars.df
     13 print(bars_df)

File /opt/homebrew/Caskroom/miniconda/base/envs/trading/lib/python3.11/site-packages/alpaca/data/historical/stock.py:92, in StockHistoricalDataClient.get_stock_bars(self, request_params)
     89 params = request_params.to_request_fields()
     91 # paginated get request for market data api
---> 92 raw_bars = self._data_get(
     93     endpoint_data_type="bars",
     94     endpoint_asset_class="stocks",
     95     api_version="v2",
     96     **params,
     97 )
     99 if self._use_raw_data:
    100     return raw_bars

File /opt/homebrew/Caskroom/miniconda/base/envs/trading/lib/python3.11/site-packages/alpaca/data/historical/stock.py:338, in StockHistoricalDataClient._data_get(self, endpoint_asset_class, endpoint_data_type, api_version, symbol_or_symbols, limit, page_limit, extension, **kwargs)
    335 params["limit"] = actual_limit
    336 params["page_token"] = page_token
--> 338 response = self.get(path=path, data=params, api_version=api_version)
    340 # TODO: Merge parsing if possible
    341 if extension == DataExtensionType.SNAPSHOT:

File /opt/homebrew/Caskroom/miniconda/base/envs/trading/lib/python3.11/site-packages/alpaca/common/rest.py:221, in RESTClient.get(self, path, data, **kwargs)
    210 def get(self, path: str, data: Union[dict, str] = None, **kwargs) -> HTTPResult:
    211     """Performs a single GET request
    212 
    213     Args:
   (...)
    219         dict: The response
    220     """
--> 221     return self._request("GET", path, data, **kwargs)

File /opt/homebrew/Caskroom/miniconda/base/envs/trading/lib/python3.11/site-packages/alpaca/common/rest.py:129, in RESTClient._request(self, method, path, data, base_url, api_version)
    127 while retry >= 0:
    128     try:
--> 129         return self._one_request(method, url, opts, retry)
    130     except RetryException:
    131         time.sleep(self._retry_wait)

File /opt/homebrew/Caskroom/miniconda/base/envs/trading/lib/python3.11/site-packages/alpaca/common/rest.py:205, in RESTClient._one_request(self, method, url, opts, retry)
    202     # raise API error for all other errors
    203     error = response.text
--> 205     raise APIError(error, http_error)
    207 if response.text != "":
    208     return response.json()

APIError: <html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>

In case you reset your paper account, you need to generate new API keys.

@Luuk1816 The code is trying to fetch data starting at “2023-01-01 00:00:00” and since no end time was provided, through the current time (ie the default). I am guessing that you are subscribed to the free market data and not a paid plan? The free market data is restricted from fetching full market data more recent than the previous 15 minutes. The error Forbidden for url is stating that your data subscription does allow fetching bars up through the current time.

You can do two things. 1) explicitly set an end datetime which is at least 15 minutes earlier than the current time or 2) explicitly set the feed parameter='iex'. Something like this

request_params = StockBarsRequest(
                        symbol_or_symbols=["TSLA"],
                        timeframe=TimeFrame.Day,
                        start="2023-01-01 00:00:00",
                        end="2023-10-01 00:00:00",
                        feed=DataFeed.IEX,
                 )

Setting the feed to IEX will restrict the data to only those trades and quotes which occurred on the IEX exchange. That exchange provides their real time data at no charge so is available in the free data plan. Note however this is not full market data and really provided as a way to test and debug ones algo (eg get around the Forbidden for url error).

Does that help?

I feel very stupid, I copied the api keys from an old file.

Stating no end date doesn’t matter. I think it handles that automatically, but thanks for your reply! It was indeed as @Reto4 said… I copied the api keys from an old file