How to Make a Currency Converter in Python

Learn how to make a real-time currency converter using different ways and from various sources such as xe, yahoo finance, xrates and Fixer API in Python.
  · 14 min read · Updated may 2023 · Web Scraping · Application Programming Interfaces · Finance

Confused by complex code? Let our AI-powered Code Explainer demystify it for you. Try it out!

Disclosure: This post may contain affiliate links, meaning when you click the links and make a purchase, we receive a commission.

A currency converter is an app or tool that allows you to quickly convert from one currency to another. We can easily find such tools for free on the Internet. This tutorial will make a real-time currency converter using several methods utilizing web scraping techniques and APIs.

This tutorial will cover six different ways to get the most recent foreign exchange rates, some of them parse the rates from public web pages such as X-RATES and Xe, and others use official APIs for more commercial and reliable use, such as Fixer API, Currency Conversion API, and ExchangeRate API, feel free to use any one of these.

Feel free to jump into the method you want to use:

  1. Scraping X-RATES
  2. Scraping Xe
  3. Scraping Yahoo Finance
  4. Using ExchangeRate API
  5. Using Fixer API
  6. Using Currency Conversion API

To get started, we have to install the required libraries for all the methods below:

$ pip install python-dateutil requests bs4 yahoo_fin

Scraping X-RATES

In this section, we will extract the data from the x-rates.com website. If you go to the target web page, you'll see most of the currencies along with the most recent date and time. Let's scrape the page:

import requests
from bs4 import BeautifulSoup as bs
from dateutil.parser import parse
from pprint import pprint

The following function is responsible for making a request to that page and extracting the data from the tables:

def get_exchange_list_xrates(currency, amount=1):
    # make the request to x-rates.com to get current exchange rates for common currencies
    content = requests.get(f"https://www.x-rates.com/table/?from={currency}&amount={amount}").content
    # initialize beautifulsoup
    soup = bs(content, "html.parser")
    # get the last updated time
    price_datetime = parse(soup.find_all("span", attrs={"class": "ratesTimestamp"})[1].text)
    # get the exchange rates tables
    exchange_tables = soup.find_all("table")
    exchange_rates = {}
    for exchange_table in exchange_tables:
        for tr in exchange_table.find_all("tr"):
            # for each row in the table
            tds = tr.find_all("td")
            if tds:
                currency = tds[0].text
                # get the exchange rate
                exchange_rate = float(tds[1].text)
                exchange_rates[currency] = exchange_rate        
    return price_datetime, exchange_rates

The above function takes the currency and the amount as parameters and returns the exchange rates of most currencies along with the date and time of the last update.

The time of the last update is in a span tag that has the class of ratesTimestamp. Notice we use the parse() function from dateutil.parser module to automatically parse the string into a Python DateTime object.

The exchange rates are located in two tables. We extract them using the find_all() method from the BeautifulSoup object and get the currency name and the exchange rate in each row in the tables, and add them to our exchange_rates dictionary that we will return. Let's use this function:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    amount = float(sys.argv[3])
    target_currency = "GBP"
    price_datetime, exchange_rates = get_exchange_list_xrates(source_currency, amount)
    print("Last updated:", price_datetime)
    pprint(exchange_rates)

Excellent, we use the built-in sys module to get the target currency and the amount from the command line. Let's run this:

$ python currency_converter_xrates.py EUR 1000

The above run is trying to convert 1000 Euros to all other currencies. Here is the output:

Last updated: 2022-02-01 12:13:00+00:00
{'Argentine Peso': 118362.205708,     
 'Australian Dollar': 1586.232315,    
 'Bahraini Dinar': 423.780164,        
 'Botswana Pula': 13168.450636,       
 'Brazilian Real': 5954.781483,       
 'British Pound': 834.954104,
 'Bruneian Dollar': 1520.451015,      
 'Bulgarian Lev': 1955.83,
 'Canadian Dollar': 1430.54405,       
 'Chilean Peso': 898463.818465,       
 'Chinese Yuan Renminbi': 7171.445692,
 'Colombian Peso': 4447741.922165,    
 'Croatian Kuna': 7527.744707,        
 'Czech Koruna': 24313.797041,
 'Danish Krone': 7440.613895,
 'Emirati Dirham': 4139.182587,
 'Hong Kong Dollar': 8786.255952,
 'Hungarian Forint': 355958.035747,
 'Icelandic Krona': 143603.932438,
 'Indian Rupee': 84241.767127,
 'Indonesian Rupiah': 16187150.010697,
 'Iranian Rial': 47534006.535121,
 'Israeli Shekel': 3569.191411,
 'Japanese Yen': 129149.364679,
 'Kazakhstani Tenge': 489292.515538,
 'Kuwaiti Dinar': 340.959682,
 'Libyan Dinar': 5196.539901,
 'Malaysian Ringgit': 4717.485104,
 'Mauritian Rupee': 49212.933037,
 'Mexican Peso': 23130.471272,
 'Nepalese Rupee': 134850.008728,
 'New Zealand Dollar': 1703.649473,
 'Norwegian Krone': 9953.078431,
 'Omani Rial': 433.360301,
 'Pakistani Rupee': 198900.635421,
 'Philippine Peso': 57574.278782,
 'Polish Zloty': 4579.273862,
 'Qatari Riyal': 4102.552652,
 'Romanian New Leu': 4946.638369,
 'Russian Ruble': 86197.012666,
 'Saudi Arabian Riyal': 4226.530892,
 'Singapore Dollar': 1520.451015,
 'South African Rand': 17159.831129,
 'South Korean Won': 1355490.097163,
 'Sri Lankan Rupee': 228245.645722,
 'Swedish Krona': 10439.125427,
 'Swiss Franc': 1037.792217,
 'Taiwan New Dollar': 31334.286611,
 'Thai Baht': 37436.518169,
 'Trinidadian Dollar': 7636.35428,
 'Turkish Lira': 15078.75981,
 'US Dollar': 1127.074905,
 'Venezuelan Bolivar': 511082584.868731}

That's about 1127.07 in USD at the time of writing this tutorial. Notice the last updated date and time; it usually updates every minute.

Scraping Xe

Xe is an online foreign exchange tools and services company. It is best known for its online currency converter. In this section, we use requests and BeautifulSoup libraries to make a currency converter based on it.

Open up a new Python file and import the necessary libraries:

import requests
from bs4 import BeautifulSoup as bs
import re
from dateutil.parser import parse

Now let's make a function that accepts the source currency, target currency, and the amount we want to convert, and then returns the converted amount along with the exchange rate date and time:

def convert_currency_xe(src, dst, amount):
    def get_digits(text):
        """Returns the digits and dots only from an input `text` as a float
        Args:
            text (str): Target text to parse
        """
        new_text = ""
        for c in text:
            if c.isdigit() or c == ".":
                new_text += c
        return float(new_text)
    
    url = f"https://www.xe.com/currencyconverter/convert/?Amount={amount}&From={src}&To={dst}"
    content = requests.get(url).content
    soup = bs(content, "html.parser")
    exchange_rate_html = soup.find_all("p")[2]
    # get the last updated datetime
    last_updated_datetime = parse(re.search(r"Last updated (.+)", exchange_rate_html.parent.parent.find_all("div")[-2].text).group()[12:])
    return last_updated_datetime, get_digits(exchange_rate_html.text)

At the time of writing this tutorial, the exchange rate is located in the third paragraph on the HTML page. This explains the soup.find_all("p")[2]. Make sure to change the extraction whenever a change is made to the HTML page. Hopefully, I'll keep an eye out whenever a change is made.

The latest date and time of the exchange rate is located at the second parent of the exchange rate paragraph in the HTML DOM.

Since the exchange rate contains string characters, I made the get_digits() function to extract only the digits and dots from a given string, which is helpful in our case.

Let's use the function now:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    destination_currency = sys.argv[2]
    amount = float(sys.argv[3])
    last_updated_datetime, exchange_rate = convert_currency_xe(source_currency, destination_currency, amount)
    print("Last updated datetime:", last_updated_datetime)
    print(f"{amount} {source_currency} = {exchange_rate} {destination_currency}")

This time, we get the source and target currencies as well as the amount from the command-lines, trying to convert 1000 EUR to USD:

$ python currency_converter_xe.py EUR USD 1000

Output:

Last updated datetime: 2022-02-01 13:04:00+00:00
1000.0 EUR = 1125.8987 USD

That's great! Xe usually updates every minute too, so it's real-time!

Scraping Yahoo Finance

Yahoo Finance provides financial news, currency data, stock quotes, press releases, and financial reports. This section uses the yahoo_fin library in Python to make a currency exchanger based on Yahoo Finance data.

Importing the libraries:

import yahoo_fin.stock_info as si
from datetime import datetime, timedelta

yahoo_fin does an excellent job of extracting the data from the Yahoo Finance web page, and it is still maintained now; we use the get_data() method from the stock_info module and pass the currency symbol to it.

Below is the function that uses this function and returns the converted amount from one currency to another:

def convert_currency_yahoofin(src, dst, amount):
    # construct the currency pair symbol
    symbol = f"{src}{dst}=X"
    # extract minute data of the recent 2 days
    latest_data = si.get_data(symbol, interval="1m", start_date=datetime.now() - timedelta(days=2))
    # get the latest datetime
    last_updated_datetime = latest_data.index[-1].to_pydatetime()
    # get the latest price
    latest_price = latest_data.iloc[-1].close
    # return the latest datetime with the converted amount
    return last_updated_datetime, latest_price * amount

We pass "1m" to the interval parameter in the get_data() method to extract minute data instead of daily data (default). We also get minute data of the previous two days, as it may cause issues on the weekends, just to be cautious.

The significant advantage of this method is you can get historical data by simply changing start_date and end_date parameters on this method. You can also change the interval to be "1d" for daily, "1wk" for weekly, and "1mo" for monthly.

Let's use the function now:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    destination_currency = sys.argv[2]
    amount = float(sys.argv[3])
    last_updated_datetime, exchange_rate = convert_currency_yahoofin(source_currency, destination_currency, amount)
    print("Last updated datetime:", last_updated_datetime)
    print(f"{amount} {source_currency} = {exchange_rate} {destination_currency}")

Running the code:

$ python currency_converter_yahoofin.py EUR USD 1000

Output:

Last updated datetime: 2022-02-01 13:26:34
1000.0 EUR = 1126.1261701583862 USD

Using ExchangeRate API

As mentioned at the beginning of this tutorial, if you want a more reliable way to make a currency converter, you have to choose an API for that. There are several APIs for this purpose. However, we have picked two APIs that seem convenient and easy to get started.

ExchangeRate API supports 161 currencies and offers a free monthly 1,500 requests if you want to try it out, and there is an open API as well that offers daily updated data, and that's what we are going to use:

import requests
from dateutil.parser import parse 

def get_all_exchange_rates_erapi(src):
    url = f"https://open.er-api.com/v6/latest/{src}"
    # request the open ExchangeRate API and convert to Python dict using .json()
    data = requests.get(url).json()
    if data["result"] == "success":
        # request successful
        # get the last updated datetime
        last_updated_datetime = parse(data["time_last_update_utc"])
        # get the exchange rates
        exchange_rates = data["rates"]
    return last_updated_datetime, exchange_rates

The above function requests the open API and returns the exchange rates for all the currencies with the latest date and time. Let's use this function to make a currency converter function:

def convert_currency_erapi(src, dst, amount):
    # get all the exchange rates
    last_updated_datetime, exchange_rates = get_all_exchange_rates_erapi(src)
    # convert by simply getting the target currency exchange rate and multiply by the amount
    return last_updated_datetime, exchange_rates[dst] * amount

As usual, let's make the main code:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    destination_currency = sys.argv[2]
    amount = float(sys.argv[3])
    last_updated_datetime, exchange_rate = convert_currency_erapi(source_currency, destination_currency, amount)
    print("Last updated datetime:", last_updated_datetime)
    print(f"{amount} {source_currency} = {exchange_rate} {destination_currency}")

Running it:

$ python currency_converter_erapi.py EUR USD 1000

Output:

Last updated datetime: 2022-02-01 00:02:31+00:00
1000.0 EUR = 1120.0 USD

The rates update daily, and it does not offer the exact exchange number as it's an open API; you can freely sign up for an API key to get precise exchange rates.

Using Fixer API

One of the promising alternatives is Fixer API. It is a simple and lightweight API for real-time and historical foreign exchange rates. You can easily create an account and get the API key.

After you've done that, you can use the /convert endpoint to convert from one currency to another. However, that's not included in the free plan and requires upgrading your account.

There is the /latest endpoint that does not require an upgrade and works in a free account just fine. It returns the exchange rates for the currency of your region. We can pass the source and target currencies we want to convert and calculate the exchange rate between both. Here's the function:

import requests
from datetime import datetime

API_KEY = "<YOUR_API_KEY_HERE>"

def convert_currency_fixerapi_free(src, dst, amount):
    """converts `amount` from the `src` currency to `dst` using the free account"""
    url = f"http://data.fixer.io/api/latest?access_key={API_KEY}&symbols={src},{dst}&format=1"
    data = requests.get(url).json()
    if data["success"]:
        # request successful
        rates = data["rates"]
        # since we have the rate for our currency to src and dst, we can get exchange rate between both
        # using below calculation
        exchange_rate = 1 / rates[src] * rates[dst]
        last_updated_datetime = datetime.fromtimestamp(data["timestamp"])
        return last_updated_datetime, exchange_rate * amount

Below is the function that uses the /convert endpoint in case you have an upgraded account:

def convert_currency_fixerapi(src, dst, amount):
    """converts `amount` from the `src` currency to `dst`, requires upgraded account"""
    url = f"https://data.fixer.io/api/convert?access_key={API_KEY}&from={src}&to={dst}&amount={amount}"
    data = requests.get(url).json()
    if data["success"]:
        # request successful
        # get the latest datetime
        last_updated_datetime = datetime.fromtimestamp(data["info"]["timestamp"])
        # get the result based on the latest price
        result = data["result"]
        return last_updated_datetime, result

Let's use either function:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    destination_currency = sys.argv[2]
    amount = float(sys.argv[3])
    # free account
    last_updated_datetime, exchange_rate = convert_currency_fixerapi_free(source_currency, destination_currency, amount)
    # upgraded account, uncomment if you have one
    # last_updated_datetime, exchange_rate = convert_currency_fixerapi(source_currency, destination_currency, amount)
    print("Last updated datetime:", last_updated_datetime)
    print(f"{amount} {source_currency} = {exchange_rate} {destination_currency}")

Before running the script, make sure to replace API_KEY with the API key you get when registering for an account.

Running the script:

Last updated datetime: 2022-02-01 15:54:04
1000.0 EUR = 1126.494 USD

You can check the documentation of Fixer API here.

Using Currency Conversion API

Currency Conversion API helps you with current and historical foreign exchange rates. In this section, we're going to use the currencyapi.com API with Python.

To get started, you have to create an account. You can easily log in with your Gmail with a click of a button, or manually sign up with your email:

Sign up at Currency Conversion APIAfter signing up, you're immediately redirected to the dashboard:

Currency API dashboardYou can see the API key hidden in my image, make sure to copy it, and let's get started with the code. Open up a new file named currency_converter_currencyapi.py and add the following:

import requests
import urllib.parse as p

API_KEY = "<YOUR_API_KEY>"
base_url = "https://api.currencyapi.com/v3/"

We're going to see two of the core endpoints of this API, which are the /latest to get the latest currency rates, and /historical to see the historical exchange rates. The response data format is the same, therefore, let's make a common function to handle both:

# utility function that both functions will use
def get_currencyapi_data(endpoint, date=None, base_currency="USD", print_all=True):
    """Get the list of currency codes from the API"""
    # construct the url
    url = p.urljoin(base_url, 
                    f"{endpoint}?apikey={API_KEY}{'' if endpoint == 'latest' else f'&date={date}'}&base_currency={base_currency}")
    # make the request
    res = requests.get(url)
    # get the json data
    data = res.json()
    # print all the currency codes and their values
    if print_all:
        for currency_code, currency_name in data.get("data").items():
            print(f"{currency_code}: {currency_name.get('value')}")
    
    if endpoint == "latest":
        # get the last updated date
        last_updated = data.get("meta").get("last_updated_at")
        print(f"Last updated: {last_updated}")
    return data

This function takes the endpoint (either latest or historical), the date (in case of historical), the base currency (default is "USD"), and whether to print all the rates available, the default is True.

We're using the requests library to make the API call, retrieving the JSON data using the .json() method, printing all the rates if print_all is True, and printing the last updated date in case it's the latest endpoint.

Let's make the two functions for the /latest and /historical endpoints:

def get_latest_rates(base_currency="USD", print_all=True):
    """Get the latest rates from the API"""
    return get_currencyapi_data(endpoint="latest", base_currency=base_currency, print_all=print_all)
    
def get_historical_rates(base_currency="USD", print_all=True, date="2023-01-01"):
    """Get the historical rates from the Currency API
    `date` must be in the format of YYYY-MM-DD"""
    return get_currencyapi_data(endpoint="historical", base_currency=base_currency, date=date, print_all=print_all)

Amazing. Let's use these functions:

if __name__ == "__main__":
    latest_rates = get_latest_rates()
    print(f"\n{'-'*50}\n")
    # get the historical rates for the date 2021-01-01
    historical_rates = get_historical_rates(date="2021-01-01", print_all=False)
    # get EUR rate, for example
    eur_rate = historical_rates.get("data").get("EUR").get("value")
    print(f"EUR rate on 2021-01-01: {eur_rate}")

First, we're getting the latest rates using our get_latest_rates() function, and printing them.

Second, we get the historical rates but pass False to print_all, and then as an example, we get the EUR rate ("USD" is the base currency) of the 2021-01-01 date. Here's the output of the entire code:

ADA: 2.57927
AED: 3.672219   
AFN: 86.155058  
ALL: 101.250181 
AMD: 388.059705 
ANG: 1.79865    
AOA: 510.500992
...
<SNIPPED>
...
XOF: 594.500673
XPF: 109.225174
XRP: 2.136364
YER: 250.300428
ZAR: 18.401632
ZMK: 9001.2
ZMW: 17.739516
ZWL: 321.999592
Last updated: 2023-05-01T23:59:59Z

--------------------------------------------------

EUR rate on 2021-01-01: 0.82132

The output is snipped as it's too long, a total of 177 global currencies. Please check the CurrencyAPI documentation for more info.

There is an alternative to using raw API calls using their Python wrapper here.

Conclusion

There are many ways to make a currency converter, and we have covered six of them. If one method does not work for you, you can choose another one!

Finally, if you're a beginner and want to learn Python, I suggest you take the Python For Everybody Coursera course, in which you'll learn a lot about Python. You can also check our resources and courses page to see the Python resources I recommend on various topics!

You can get the complete code for all the files here.

Learn also: Webhooks in Python with Flask.

Happy coding ♥

Loved the article? You'll love our Code Converter even more! It's your secret weapon for effortless coding. Give it a whirl!

View Full Code Build My Python Code
Sharing is caring!



Read Also



Comment panel

    Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!