Please help with this stock ticker

Good morning!
I found a stock ticker program I like. I want to make it an ESP Now sender. It looks to me like it should work, but I get three errors, one for each stock I’m trying to look up. I got the API Key.
It looks to me like a query comes up empty.
“Fetching price for TSLA
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=TSLAQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for NLY
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=NLYQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for BREW
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=BREWQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching resource from https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_R
ATE&from_currency=BTC&to_currency=USDQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for TSLA
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=TSLAQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for NLY
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=NLYQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for BREW
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=BREWQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput”

Here’s the code:

#include <Arduino.h>

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#include <ArduinoJson.h>

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

#include <ESP8266HTTPClient.h>

#include <WiFiClientSecureBearSSL.h>

// Wifi credential information
const String kSsid = "TP-Link_7C28";
const String kPassword = "64411811";

// AlphaVantage API information, they have free tokens ;)
const String kApiUri = "https://www.alphavantage.co";
const String kApiBaseUri = "https://www.alphavantage.co/query?";
const String kApiStockPrice = "function=GLOBAL_QUOTE&";
const String kApiExchangeRate = "function=CURRENCY_EXCHANGE_RATE&";
const String kApiFromCurrency = "from_currency=BTC&";
// Note: we leave the & off of this one as the last piece is the kApiKey
const String kApiToCurrency = "to_currency=USD";
const String kApiSymbol = "symbol=";
const String kApiKey = "QITRTT5A746YIJBI";

// Define additional ticker symbols here
const String kTickerSymbols[] = {
    "TSLA",   // 
    "NLY", // 
};

// Max document size that we can parse for JSON, resp from AV has about 10 vals
const size_t kMaxJsonDoc = 0x400;
StaticJsonDocument<kMaxJsonDoc> doc;

// Main loop pause
const size_t kDelay = 30000;
// Seconds to wait between POSTs to AlphaVantage
const size_t kStockPause = 15000;
// Blink delay for the lights
const size_t kBlinkDelay = 1000;

// SHA1 fingerprint of the sites certificate
const char *kAlphaVantageFingerprint = "C9 44 AD B9 48 6F 7C D1 64 5F ED 9B 49 53 56 88 DA FD FC 4B";

// Init the SSD1306 display
Adafruit_SSD1306 display = Adafruit_SSD1306();

// Init the ESP8266 Wifi Module
ESP8266WiFiMulti WiFiMulti;

void setup()
{
  Serial.begin(9600);

  Serial.println();
  Serial.println("Stock Ticker Board v0.1");
  Serial.print("Connecting to wifi: ");
  Serial.print(kSsid);

  // initialize with the I2C addr 0x3C (for the 128x32)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.display();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  auto msg = "Connecting to " + kSsid;
  display.print(msg);
  display.setCursor(0, 0);
  display.display();
  delay(500);

  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP(kSsid.c_str(), kPassword.c_str());

  while (WiFiMulti.run() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(" .");
  }

  Serial.println();
  Serial.print("Connected to ");
  Serial.print(kSsid);
  Serial.print(" with IP address: ");
  Serial.println(WiFi.localIP());
  display.setCursor(0, 15);
  display.print("IP: " + WiFi.localIP().toString());
  display.display();
  delay(2000);
}

// Fetches a string response from a remote API endpoint URI
int getApiResponse(const String &uri, String &resp)
{
  Serial.println("Fetching resource from " + uri);
  std::unique_ptr<BearSSL::WiFiClientSecure> secure_client(new BearSSL::WiFiClientSecure);

  secure_client->setFingerprint(kAlphaVantageFingerprint);
  secure_client->setTimeout(3000);

  HTTPClient https;
  auto ret = https.begin(*secure_client, uri);

  if (!ret)
  {
    Serial.println("Failed to connect to URI with " + String(ret));
    return 1;
  }

  auto status = https.GET();
  if (status != HTTP_CODE_OK)
  {
    Serial.println("HTTPS GET failed with code " + String(status));
    return status;
  }

  resp = https.getString();
  return status;
}

void blink()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(kBlinkDelay);
  digitalWrite(LED_BUILTIN, LOW);
  delay(kBlinkDelay);
}

void loop()
{

  // Blink to indicate we're about to fetch new stocks
  blink();

  // We fetch stock values in a loop around the ticker symbols, defined above
  for (const auto sym : kTickerSymbols)
  {
    Serial.println("Fetching price for " + sym);

    // Perform the API request
    String resp;
    String uri = kApiBaseUri + kApiStockPrice + kApiSymbol + sym + kApiKey;
    auto ret = getApiResponse(uri, resp);
    if (ret == 1)
    {
      Serial.println("Fetch remote resource failed. Continuing");
      delay(kStockPause);
      continue;
    }

    // Parse the response into a JSON object
    auto err = deserializeJson(doc, resp);
    if (err)
    {
      Serial.println("Failed to parse response to JSON with " + String(err.c_str()));
      delay(kStockPause);
      continue;
    }

    // We display the price
    auto quote = doc["Global Quote"];
    auto quote_msg = sym + ": " + quote["05. price"].as<String>() + ", " +
                     quote["09. change"].as<String>() + " (" +
                     quote["10. change percent"].as<String>() + ")";
    Serial.println(quote_msg);
    display.clearDisplay();
    display.setCursor(0, 0);
    display.print(quote_msg);
    display.display();
    delay(kStockPause);
  }

  // Lastly, fetch the USD -> BTC exchange rate, for funsies
  String resp;
  String uri = kApiBaseUri + kApiExchangeRate + kApiFromCurrency +
               kApiToCurrency + kApiKey;
  auto ret = getApiResponse(uri, resp);

  // Parse the response into a JSON object
  auto err = deserializeJson(doc, resp);
  if (err)
  {
    Serial.println("Failed to parse response to JSON with " + String(err.c_str()));
  }
  else
  {
    // We display the price
    auto er = doc["Realtime Currency Exchange Rate"];
    auto msg = er["1. From_Currency Code"].as<String>() + " -> " +
               er["3. To_Currency Code"].as<String>() + ": " +
               er["5. Exchange Rate"].as<String>();
    Serial.println(msg);
    display.clearDisplay();
    display.setCursor(0, 0);
    display.print(msg);
    display.display();
    delay(kDelay);
  }
}

and another thing:
“{
“Error Message”: “the parameter apikey is invalid or missing. Please claim your free API key on (Customer Support | Alpha Vantage). It should take less than 20 seconds.”
}”

But when I look at the certificate info on that page in my browser it says

So the SHA1 doesn’t seem to match the source code anymore. Certificate info says it was changed 1 month ago in the “validity” section.

grafik

So what happens if you say

// SHA1 fingerprint of the sites certificate
const char *kAlphaVantageFingerprint = "27 F4 DF 78 11 04 1A DB 4B EC 5A 4C D5 0A FE 1F D6 24 CE BF";

instead?

Wow, that changes things a lot. But this is the return:
Fetching price for TSLA
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=TSLAQITRTT5A746YIJBI
TSLA: null, null (null)
Fetching price for NLY
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=NLYQITRTT5A746YIJBI
NLY: null, null (null)

There are two errors, and neither gets tripped:

auto ret = getApiResponse(uri, resp);

    if (ret == 1)

    {

      Serial.println("Fetch remote resource failed. Continuing");

      delay(kStockPause);

      continue;

    }

    // Parse the response into a JSON object

    auto err = deserializeJson(doc, resp);

    if (err)

    {

      Serial.println("Failed to parse response to JSON with " + String(err.c_str()));

      delay(kStockPause);

      continue;

    }

But that code

with

Is weird because it doesn’t properly give it the API key. Per API Documentation | Alpha Vantage it must be in a GET parameter called apikey. It just pastes it after the symbol of the stock.

When it should be

https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=NLY&apikey=QITRTT5A746YIJBI

Can you change

to

const String kApiKey = "&apikey=QITRTT5A746YIJBI";

?

DUDE!
I have to say that this is a relationship I really value. You’re my imaginary friend.
It works.

So the declaration of const String kApiKey doesn’t initialize with the value but a pointer to that value? Or is it that making it a string requires pointing?

No pointers involved. & In the context of a url is just a parameter separator.