Angela Yu 100 Days of Code: The Tequila Endpoint Mystery

Hello,
I’m being dramatic, but I have been looking everywhere. I also have to accept the possibility that the error could be in my code. I am attempting to use Sheety, Google Sheets, and Tequila to return IATA Codes for airports and put them into the sheet. The seemingly easiest part of simply copying/pasting the Tequila endpoint has been tough. When I run the script, I get 404, but my test for Tokyo does return the IATA Code TYO for Narita Airport. My code:

import requests
import os


# Make sure to import os for getenv
from dotenv import load_dotenv
load_dotenv()

TEQUILA_ENDPOINT = "https://tequila-api.kiwi.com"
TEQUILA_API_KEY = os.getenv("TEQUILA_API_KEY")

class FlightSearch:
    def __init__(self):
        self.tequila_endpoint = TEQUILA_ENDPOINT
        self.api_key = TEQUILA_API_KEY

    def get_iata_code(self, city_name):
        location_endpoint = f"{TEQUILA_ENDPOINT}/locations/query"
        headers = {"apikey": self.api_key}
        query = {"term": city_name, "location_types": "city"}
        response = requests.get(url=location_endpoint, params=query, headers=headers)
        response.raise_for_status()
        data = response.json()["locations"]
        if data:
            return data[0]["code"]
        else:
            return None

# Correct way to use the class and method
flight_search = FlightSearch()
test_city = "Tokyo"
iata_code = flight_search.get_iata_code(test_city)
print(f"The IATA code for {test_city} is: {iata_code}")

Any assistance would be appreciated.

Hmm. That’s a lot of moving parts. Good thing you can tackle one part at a time :slight_smile:

Let’s be real here, we programmers are TERRIBLE at predicting what will be easy and what will be tough!

Since I can’t directly run your program (as it requires the necessary API key), I can’t actually test this out, so this is general advice based on reading over the code. The first potential failure point is the loading of dotenv and fetching of the API key. So that’s where I’d start: print out the API key and see if it looks right. Even better, print out the repr of it, to see if there’s any kind of messed-up formatting or anything.

print(repr(TEQUILA_API_KEY))

If that looks wrong, I’d say your best bet would be to ditch the whole dotenv system and choose either environment variables or some other config file (I often use a JSON file for this sort of config). Trying to have a config file (.env) that gets folded into your environment (os.getenv) is a layer of complexity that doesn’t really buy you anything.

But let’s suppose the API key itself looks fine. Then the next likely issue I’m seeing is the formatting of the request. The requests module makes extensive use of Python’s logging subsystem, so you could delve into exactly what it’s sending and what it’s getting back. Alternatively, have a read through the documentation, and audit your code to see whether you got everything right. Here’s some places to look:

  • The URL. Is it https://tequila-api.kiwi.com/locations/query ? Make sure that that’s the right domain, the right path, there’s nothing else getting in the way (eg a lot of API URLs have a version number in them like v3, v5, or helix - yes, “helix” counts as a version number if you talk to Twitch). This is the first and foremost place to look.
  • Should the API key be placed in a header called apikey? Make sure it isn’t actually meant to be api-key or something. This probably isn’t it, though, as this should give back a 401 or 403. But it might give a 404.
  • You said your test does return the correct response. Maybe the API uses return values in a way that means raise_for_status() isn’t the best way to handle things - maybe a 404 means “couldn’t find that city”. Is there any other information in the response?

By the way, side note: Your __init__ method grabs two constants and makes them into instance variables, which is probably unnecessary since they’re, well, constants; but then in get_iata_code(), you make use of one of those instance variables, and one of the original constants. It’s inconsistent. :slight_smile: Personally, I’d drop the entire class and just have get_iata_code() as a stand-alone function, and let it make direct use of the constants. But either way works.

Hopefully that’ll point you in a useful direction. Good luck with it!

Hello! Thank you so much for taking the time to write such a detailed response.

Hmm. That’s a lot of moving parts. Good thing you can tackle one part at a time :slight_smile:

Yeah, this is one of my biggest projects to date.

I did this and the API key looks fine. No spaces or anything else that would trip up the script.

From what I found on a Gist forum, the endpoint should be "[https://tequila-api.kiwi.com”], so I will try that out and see if I can get a positive response.

Yes, I was so focused on the other stuff I didn’t realize the redundancy. I have flight_search.py, main.py, data_manager.py and one more I can’t recall right now and I’m going from tab to tab doing different things. I agree that making get_iata_code its own function would make things cleaner and easier to understand if I come back to this project at some point.

Again, thank you. Lol, you will definitely see me here again.

It’s still saying the endpoint doesn’t exist and I have cycled through many endpoints in the API documentation. I have no idea. If I run this, it returns the iata code, no problem:

flight_search = FlightSearch()
test_city = "Denver"
iata_code = flight_search.get_iata_code(test_city)
print(f"The IATA code for {test_city} is: {iata_code}")

Even without specific instruction, the script can grab my API key and appears to be using the endpoint to retrieve the data. Is there a way to share an API key with you to see if you get the same result?

I can’t understand this explanation, because it seems self-contradictory. “If you run this”, it relies on the endpoint existing in order to work. That’s how get_iata_code was defined, according to the code you showed us. So when you say “it’s still saying the endpoint doesn’t exist”, I can’t understand why you believe “it” is saying this - nor can I understand what “it” is.

What exactly do you run, in order to have it not work? Exactly what happens in that case (copy and paste the complete program and error output, and format it the same way as the code)?

Happy to help.

Try to make a simple, self-contained example, so that the only thing it requires is the API key and it will work. Show us the exact program and its output. We should be able to figure things out from there, and if not, it’ll be easier to talk about it from that basis.

Thank you for your response and apologies for being vague. The entire script is this:

import requests
import os


# Make sure to import os for getenv
from dotenv import load_dotenv
load_dotenv()

TEQUILA_ENDPOINT = "https://tequila-api.kiwi.com"
TEQUILA_API_KEY = os.getenv("TEQUILA_API_KEY")

class FlightSearch:
    def __init__(self):
        self.tequila_endpoint = TEQUILA_ENDPOINT
        self.api_key = TEQUILA_API_KEY

    def get_iata_code(self, city_name):
        location_endpoint = f"{TEQUILA_ENDPOINT}/locations/query"
        headers = {"apikey": self.api_key}
        query = {"term": city_name, "location_types": "city"}
        response = requests.get(url=location_endpoint, params=query, headers=headers)
        response.raise_for_status()
        data = response.json()["locations"]
        if data:
            return data[0]["code"]
        else:
            return None

# Correct way to use the class and method
flight_search = FlightSearch()
test_city = "Tokyo"
iata_code = flight_search.get_iata_code(test_city)
print(f"The IATA code for {test_city} is: {iata_code}")

I added this to see if the script would return an IATA Code if given a city:

# Correct way to use the class and method
flight_search = FlightSearch()
test_city = "Tokyo"
iata_code = flight_search.get_iata_code(test_city)
print(f"The IATA code for {test_city} is: {iata_code}")

What I thought would happen is that the tequila endpoint would return everything as 404, but it gave me this:

<bound method Response.json of <Response [200]>>
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}
{
  "code": 404,
  "message": "Not Found. Endpoint doesn't exist."
}

Lol, yes my explanation is contradictory because the results appear that way to me. I’m getting 200 and the IATA code in one case, but 404 in the other.

This seems to indicate that I am not properly understanding Tequila’s documentation (highly probable). As Mr. Angelico said above, there are a lot of moving parts in this program for me at my current skill level, so I’m bound to miss something. Here is everything:

#data_manager.py
import os
import requests

#CONSTANT
SHEETY_API = os.getenv("SHEETY_API")
SHEETY_ENDPOINT = "https://api.sheety.co/d741b494d378a4eff912af14a92fd22e/metroSkies/prices"
class DataManager:
  # Initialization
  def __init__(self):
      self.destination_data = {}

  def get_destination_data(self):
      headers = {"Authorization": f"Bearer {SHEETY_API}"}
      response = requests.get(url=SHEETY_ENDPOINT, headers=headers)
      response.raise_for_status()
      data = response.json()
      print(response.json)
      self.destination_data = data["prices"]
      return self.destination_data

  def update_destination_codes(self, updated_data):
      for destination in updated_data:
          new_data = {
              "price": {
                  "iataCode": destination["city"]
              }
          }
          response = requests.put(
              url=f"{SHEETY_ENDPOINT}/prices/{destination['id']}",
              json=new_data,
              headers={"Authorization": f"Bearer {SHEETY_API}"}
          )
          print(response.text)  # For debugging

#fligh_search.py
import requests
import os


# Make sure to import os for getenv
from dotenv import load_dotenv
load_dotenv()

TEQUILA_ENDPOINT = "https://tequila-api.kiwi.com/locations/query"
TEQUILA_API_KEY = os.getenv("TEQUILA_API_KEY")

class FlightSearch:
    def __init__(self):
        self.tequila_endpoint = TEQUILA_ENDPOINT
        self.api_key = TEQUILA_API_KEY

    def get_iata_code(self, city_name):
        location_endpoint = TEQUILA_ENDPOINT        
        headers = {"apikey": TEQUILA_API_KEY}
        query = {"term": city_name, "location_types": "city"}
        response = requests.get(url=location_endpoint, params=query, headers=headers)
        data = response.json()["locations"]
        if data:
            return data[0]["code"]
        else:
            return None
# main.py
# Imports
from dotenv import load_dotenv
import os
from data_manager import DataManager
from flight_search import FlightSearch

# Load the dotenv file
load_dotenv()

# Constants
TEQUILA_API_KEY = os.getenv("TEQUILA_API_KEY")

# Initialize DataManager and FlightSearch
data_manager = DataManager()
flight_search = FlightSearch()

# Fetch destination data from Google Sheet
sheet_data = data_manager.get_destination_data()

# Update IATA codes if missing
for destination in sheet_data:
    if destination["iataCode"] == "":
        city_name = destination["city"]
        iata_code = flight_search.get_iata_code(city_name)
        if iata_code:
            destination["iataCode"] = iata_code
        else:
            print(f"IATA Code not found or error fetching code for {city_name}")

# Reflect updated IATA codes back to Google Sheet
data_manager.update_destination_codes(sheet_data)

I’ll do my best to be more specific. Thank you!

So if this works…

… but this doesn’t, then the first place I’d look is: what exactly is city_name? Try printing it out, particularly its repr:

print(repr(city_name))

to see whether you’re getting “Tokyo” or something else.

Hello,
Hope you’re having a great day (or evening).

I wrote a contained script that writes to a JSON:

import requests
import json
import os

TQLA_API_KEY = os.getenv("TQLA_API_KEY")

def get_iata_code(city_name, api_key):
    tequila_endpoint = "https://tequila-api.kiwi.com/locations/query"
    headers = {"apikey": TQLA_API_KEY}
    query = {"term": city_name, "location_types": "city"}

    response = requests.get(tequila_endpoint, headers=headers, params=query)
    response.raise_for_status()
    data = response.json()
    # Save to a JSON file
    with open(f"{city_name}_iata_code.json", "w") as json_file:
        json.dump(data, json_file, indent=4)

    if data['locations']:
        return data['locations'][0]['code']
    else:
        return "No IATA code found"

# Usage example:
api_key = "YOUR_TEQUILA_API_KEY"
city_name = "Tokyo"
iata_code = get_iata_code(city_name, api_key)
print(f"The IATA code for {city_name} is: {iata_code}")

The script above returns this in the console:
The IATA code for Tokyo is: TYO

The JSON contains everything:

{
    "locations": [
        {
            "id": "tokyo_jp",
            "active": true,
            "name": "Tokyo",
            "slug": "tokyo-japan",
            "slug_en": "tokyo-japan",
            "code": "TYO",
            "alternative_names": [
                "\u6771\u4eac"
            ],
            "rank": 14,
            "global_rank_dst": 2,
            "dst_popularity_score": 10088214.0,
            "timezone": "Asia/Tokyo",
            "population": 8336599,
            "airports": 2,
            "stations": 1,
            "hotels": 0,
            "bus_stations": 0,
            "subdivision": {
                "id": "TK_JP",
                "name": "Tokyo",
                "slug": "tokyo-japan-1",
                "code": "TK"
            },
            "autonomous_territory": null,
            "country": {
                "id": "JP",
                "name": "Japan",
                "slug": "japan",
                "code": "JP"
            },
            "region": {
                "id": "eastern-asia",
                "name": "Eastern Asia",
                "slug": "eastern-asia"
            },
            "continent": {
                "id": "asia",
                "name": "Asia",
                "slug": "asia",
                "code": "AS"
            },
            "nearby_country": null,
            "location": {
                "lat": 35.689487,
                "lon": 139.691706
            },
            "tags": [
                {
                    "tag": "activities",
                    "month_to": -1,
                    "month_from": -1
                },
                {
                    "tag": "family fun",
                    "month_to": -1,
                    "month_from": -1
                },
                {
                    "tag": "famous cities",
                    "month_to": -1,
                    "month_from": -1
                }
            ],
            "alternative_departure_points": [
                {
                    "id": "IBR",
                    "distance": 104.86,
                    "duration": 5159.7
                },
                {
                    "id": "MMJ",
                    "distance": 209.07,
                    "duration": 10787.2
                },
                {
                    "id": "FSZ",
                    "distance": 211.55,
                    "duration": 9700.7
                },
                {
                    "id": "FKS",
                    "distance": 223.94,
                    "duration": 11201
                },
                {
                    "id": "HND",
                    "distance": 29.08,
                    "duration": 2782.1
                },
                {
                    "id": "NRT",
                    "distance": 76.16,
                    "duration": 3669
                }
            ],
            "providers": [
                1175,
                1280,
                1282
            ],
            "car_rentals": [
                {
                    "provider_id": 1175,
                    "providers_locations": [
                        "2663626",
                        "1655303",
                        "2663616",
                        "2649613",
                        "2647193",
                        "425818",
                        "2663621",
                        "2318946",
                        "425968",
                        "1905216",
                        "2650626",
                        "2651391",
                        "4188539",
                        "4188538"
                    ]
                }
            ],
            "type": "city"
        }
    ],
    "meta": {
        "locale": {
            "code": "en-US",
            "status": "Locale not specified, using default."
        }
    },
    "last_refresh": 1709715855,
    "results_retrieved": 1
}

I will also see what I can find on YT and take another look at the documentation to hopefully gain some perspective. Maybe I need to write to the json first then read the iata codes from there. I think that would mean hard coding my cities which kinda defeats the purpose.

Solved it! Thanks for your help! You were right, all of the moving pieces was the biggest problem because of my level. I also was depending too much on AI instead of methodically going through the code and testing step by step. :smile:

Well, there’s the problem. AI generated code sucks. Work through things yourself, and definitely do not use an AI to try to help you with coursework.

1 Like

Yes, sir! The rules are the same whether it’s a programming language or Japanese.

1 Like