Skip to content

Currency Exchange Rate Calculator GUI

Abstract

A currency converter is the perfect “small but real” beginner project: it has a user interface, talks to a live API on the internet, parses structured data (JSON), and produces output the user actually cares about. In this tutorial you will build a desktop GUI in Tkinter that lets the user type an amount, pick a source currency and a target currency from dropdowns, and click Convert to see today’s rate applied.

You will learn:

  • How to build a Tkinter window with labels, entries, dropdowns, and buttons.
  • How to call an HTTP API with requestsrequests and parse JSON responses.
  • How to handle network errors gracefully so the app never crashes mid-conversation.
  • How to organize widget-driven code with callbacks.
  • How to extend the converter into a tool you might actually use.

Prerequisites

  • Python 3.6 or above.
  • A text editor or IDE.
  • An internet connection.
  • Comfort installing packages with pippip.
  • Familiarity with functions and conditionals.

Install Dependencies

install
pip install requests
install
pip install requests
  • tkintertkinter ships with Python — no install needed.
  • requestsrequests is the de-facto library for HTTP in Python.

Pick an Exchange-Rate API

There are several free public APIs you can use without an API key:

APIURL patternNotes
exchangerate-apihttps://open.er-api.com/v6/latest/USDhttps://open.er-api.com/v6/latest/USDFree tier, no key, daily updates.
frankfurter.apphttps://api.frankfurter.app/latest?from=USD&to=EURhttps://api.frankfurter.app/latest?from=USD&to=EUROpen-source, ECB rates.
Fixer.iohttps://api.exchangerate.host/latest?base=USDhttps://api.exchangerate.host/latest?base=USDLarger currency list.

For this tutorial we will use exchangerate.host (no key required). The response is JSON like:

json
{
  "base": "USD",
  "rates": { "EUR": 0.92, "GBP": 0.79, "INR": 83.10, ... }
}
json
{
  "base": "USD",
  "rates": { "EUR": 0.92, "GBP": 0.79, "INR": 83.10, ... }
}

💡 Any API can change its terms. If yours stops working, swap the URL — the parsing code is similar.

Getting Started

Create the project

  1. Create a folder named currency-exchange-calculatorcurrency-exchange-calculator.
  2. Inside it, create currencyexchnageratecalculator.pycurrencyexchnageratecalculator.py.
  3. Open the folder in your editor.

Write the code

Currency Exchange Rate Calculator GUISource
Currency Exchange Rate Calculator GUI
# Currency Exchange Rate Calculator GUI
 
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
import requests
import json
 
# Create an instance of tkinter frame
win = Tk()
 
# Set the geometry of tkinter frame
win.geometry("700x400")
 
# Set the title of tkinter frame
win.title("Currency Exchange Rate Calculator")
 
# Disable resizing the GUI by passing in False/False
win.resizable(False, False)
 
# Create a function to convert currency
def exchange():
    # Get the amount from the amount entry box
    amount = float(amount_entry.get())
    
    # Get the currency from the currency combobox
    currency = currency_combo.get()
    
    # Get the converted currency from the converted currency combobox
    converted_currency = converted_currency_combo.get()
    
    # Create a base URL
    base_url = "https://api.exchangerate-api.com/v4/latest/"
    
    # Create a full URL
    full_url = base_url + currency
    
    # Send a request to the URL
    response = requests.get(full_url)
    
    # Convert the response to JSON format
    response_json = response.json()
    
    # Get the currency rates from the response
    rates = response_json['rates']
    
    # Get the currency rate for the converted currency
    converted_currency_rate = rates[converted_currency]
    
    # Calculate the converted currency amount
    converted_currency_amount = converted_currency_rate * amount
    
    # Round the converted currency amount to 2 decimal places
    converted_currency_amount = round(converted_currency_amount, 2)
    
    # Create a message box to display the converted currency amount
    messagebox.showinfo("Converted Currency Amount", f"{converted_currency_amount} {converted_currency}")
    
# Create a function to clear the GUI
def clear():
    # Clear the amount entry box
    amount_entry.delete(0, END)
    
    # Clear the currency combobox
    currency_combo.set("")
    
    # Clear the converted currency combobox
    converted_currency_combo.set("")
    
# Create a function to exit the GUI
def exit():
    # Exit the GUI
    win.destroy()
    
# Create a label for the amount entry box
amount_label = Label(win, text="Amount:", font=("Arial", 10, "bold"))
 
# Create a label for the currency combobox
currency_label = Label(win, text="Currency:", font=("Arial", 10, "bold"))
 
# Create a label for the converted currency combobox
converted_currency_label = Label(win, text="Converted Currency:", font=("Arial", 10, "bold"))
 
# Create a label for the amount entry box
amount_label.place(x=30, y=50)
 
# Create a label for the currency combobox
currency_label.place(x=30, y=100)
 
# Create a label for the converted currency combobox
converted_currency_label.place(x=30, y=150)
 
# Create a variable for the amount entry box
amount = StringVar()
 
# Create an entry box for the amount
amount_entry = Entry(win, textvariable=amount, font=("Arial", 10, "normal"), width=20)
 
# Create a variable for the currency combobox
currency = StringVar()
 
# Create a combobox for the currency
currency_combo = ttk.Combobox(win, textvariable=currency, font=("Arial", 10, "normal"), width=18)
 
# Create a variable for the converted currency combobox
converted_currency = StringVar()
 
# Create a combobox for the converted currency
converted_currency_combo = ttk.Combobox(win, textvariable=converted_currency, font=("Arial", 10, "normal"), width=18)
 
# Create a list of currencies
currencies = ["USD", "EUR", "GBP", "INR", "AUD", "CAD", "SGD", "CHF", "MYR", "JPY", "CNY"]
 
# Set the default value for the currency combobox
currency_combo.set("Select Currency")
 
# Set the default value for the converted currency combobox
converted_currency_combo.set("Select Converted Currency")
 
# Add the currencies to the currency combobox
currency_combo['values'] = currencies
 
# Add the currencies to the converted currency combobox
converted_currency_combo['values'] = currencies
 
# Create a button to convert currency
convert_button = Button(win, text="Convert", font=("Arial", 10, "bold"), command=exchange)
 
# Create a button to clear the GUI
clear_button = Button(win, text="Clear", font=("Arial", 10, "bold"), command=clear)
 
# Create a button to exit the GUI
exit_button = Button(win, text="Exit", font=("Arial", 10, "bold"), command=exit)
 
# Create an entry box for the amount
amount_entry.place(x=200, y=50)
 
# Create a combobox for the currency
currency_combo.place(x=200, y=100)
 
# Create a combobox for the converted currency
converted_currency_combo.place(x=200, y=150)
 
# Create a button to convert currency
convert_button.place(x=30, y=200)
 
# Create a button to clear the GUI
clear_button.place(x=130, y=200)
 
# Create a button to exit the GUI
exit_button.place(x=230, y=200)
 
# Run the tkinter event loop
win.mainloop()
Currency Exchange Rate Calculator GUI
# Currency Exchange Rate Calculator GUI
 
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
import requests
import json
 
# Create an instance of tkinter frame
win = Tk()
 
# Set the geometry of tkinter frame
win.geometry("700x400")
 
# Set the title of tkinter frame
win.title("Currency Exchange Rate Calculator")
 
# Disable resizing the GUI by passing in False/False
win.resizable(False, False)
 
# Create a function to convert currency
def exchange():
    # Get the amount from the amount entry box
    amount = float(amount_entry.get())
    
    # Get the currency from the currency combobox
    currency = currency_combo.get()
    
    # Get the converted currency from the converted currency combobox
    converted_currency = converted_currency_combo.get()
    
    # Create a base URL
    base_url = "https://api.exchangerate-api.com/v4/latest/"
    
    # Create a full URL
    full_url = base_url + currency
    
    # Send a request to the URL
    response = requests.get(full_url)
    
    # Convert the response to JSON format
    response_json = response.json()
    
    # Get the currency rates from the response
    rates = response_json['rates']
    
    # Get the currency rate for the converted currency
    converted_currency_rate = rates[converted_currency]
    
    # Calculate the converted currency amount
    converted_currency_amount = converted_currency_rate * amount
    
    # Round the converted currency amount to 2 decimal places
    converted_currency_amount = round(converted_currency_amount, 2)
    
    # Create a message box to display the converted currency amount
    messagebox.showinfo("Converted Currency Amount", f"{converted_currency_amount} {converted_currency}")
    
# Create a function to clear the GUI
def clear():
    # Clear the amount entry box
    amount_entry.delete(0, END)
    
    # Clear the currency combobox
    currency_combo.set("")
    
    # Clear the converted currency combobox
    converted_currency_combo.set("")
    
# Create a function to exit the GUI
def exit():
    # Exit the GUI
    win.destroy()
    
# Create a label for the amount entry box
amount_label = Label(win, text="Amount:", font=("Arial", 10, "bold"))
 
# Create a label for the currency combobox
currency_label = Label(win, text="Currency:", font=("Arial", 10, "bold"))
 
# Create a label for the converted currency combobox
converted_currency_label = Label(win, text="Converted Currency:", font=("Arial", 10, "bold"))
 
# Create a label for the amount entry box
amount_label.place(x=30, y=50)
 
# Create a label for the currency combobox
currency_label.place(x=30, y=100)
 
# Create a label for the converted currency combobox
converted_currency_label.place(x=30, y=150)
 
# Create a variable for the amount entry box
amount = StringVar()
 
# Create an entry box for the amount
amount_entry = Entry(win, textvariable=amount, font=("Arial", 10, "normal"), width=20)
 
# Create a variable for the currency combobox
currency = StringVar()
 
# Create a combobox for the currency
currency_combo = ttk.Combobox(win, textvariable=currency, font=("Arial", 10, "normal"), width=18)
 
# Create a variable for the converted currency combobox
converted_currency = StringVar()
 
# Create a combobox for the converted currency
converted_currency_combo = ttk.Combobox(win, textvariable=converted_currency, font=("Arial", 10, "normal"), width=18)
 
# Create a list of currencies
currencies = ["USD", "EUR", "GBP", "INR", "AUD", "CAD", "SGD", "CHF", "MYR", "JPY", "CNY"]
 
# Set the default value for the currency combobox
currency_combo.set("Select Currency")
 
# Set the default value for the converted currency combobox
converted_currency_combo.set("Select Converted Currency")
 
# Add the currencies to the currency combobox
currency_combo['values'] = currencies
 
# Add the currencies to the converted currency combobox
converted_currency_combo['values'] = currencies
 
# Create a button to convert currency
convert_button = Button(win, text="Convert", font=("Arial", 10, "bold"), command=exchange)
 
# Create a button to clear the GUI
clear_button = Button(win, text="Clear", font=("Arial", 10, "bold"), command=clear)
 
# Create a button to exit the GUI
exit_button = Button(win, text="Exit", font=("Arial", 10, "bold"), command=exit)
 
# Create an entry box for the amount
amount_entry.place(x=200, y=50)
 
# Create a combobox for the currency
currency_combo.place(x=200, y=100)
 
# Create a combobox for the converted currency
converted_currency_combo.place(x=200, y=150)
 
# Create a button to convert currency
convert_button.place(x=30, y=200)
 
# Create a button to clear the GUI
clear_button.place(x=130, y=200)
 
# Create a button to exit the GUI
exit_button.place(x=230, y=200)
 
# Run the tkinter event loop
win.mainloop()

Run it

run
python currencyexchnageratecalculator.py
run
python currencyexchnageratecalculator.py

A window appears. Enter 100100, pick USD → EUR, click Convert, and a popup shows the result.

Step-by-Step Explanation

1. Import what you need

currencyexchnageratecalculator.py
import tkinter as tk
from tkinter import ttk, messagebox
import requests
currencyexchnageratecalculator.py
import tkinter as tk
from tkinter import ttk, messagebox
import requests
  • tkintertkinter for the window, ttkttk for themed widgets (dropdowns look prettier than the legacy OptionMenuOptionMenu), and messageboxmessagebox for the result popup.
  • requestsrequests for the HTTP call.

2. Create the main window

currencyexchnageratecalculator.py
win = tk.Tk()
win.title("Currency Exchange Calculator")
win.geometry("400x300")
currencyexchnageratecalculator.py
win = tk.Tk()
win.title("Currency Exchange Calculator")
win.geometry("400x300")

Tk()Tk() is the root window. geometry("WxH")geometry("WxH") sets a fixed size.

3. Build the widgets

currencyexchnageratecalculator.py
tk.Label(win, text="Amount:").pack()
amount_entry = tk.Entry(win)
amount_entry.pack()
 
CURRENCIES = ["USD", "EUR", "GBP", "INR", "JPY", "AUD", "CAD", "CNY"]
tk.Label(win, text="From:").pack()
from_currency = ttk.Combobox(win, values=CURRENCIES)
from_currency.set("USD")
from_currency.pack()
 
tk.Label(win, text="To:").pack()
to_currency = ttk.Combobox(win, values=CURRENCIES)
to_currency.set("EUR")
to_currency.pack()
currencyexchnageratecalculator.py
tk.Label(win, text="Amount:").pack()
amount_entry = tk.Entry(win)
amount_entry.pack()
 
CURRENCIES = ["USD", "EUR", "GBP", "INR", "JPY", "AUD", "CAD", "CNY"]
tk.Label(win, text="From:").pack()
from_currency = ttk.Combobox(win, values=CURRENCIES)
from_currency.set("USD")
from_currency.pack()
 
tk.Label(win, text="To:").pack()
to_currency = ttk.Combobox(win, values=CURRENCIES)
to_currency.set("EUR")
to_currency.pack()
  • EntryEntry is a one-line text input.
  • ComboboxCombobox is a dropdown the user can also type into; .set(value).set(value) chooses an initial selection.
  • .pack().pack() is the simplest of Tkinter’s layout managers — it stacks widgets top-to-bottom.

4. The conversion callback

currencyexchnageratecalculator.py
def exchange():
    try:
        amount = float(amount_entry.get())
    except ValueError:
        messagebox.showerror("Invalid input", "Amount must be a number.")
        return
 
    src = from_currency.get()
    dst = to_currency.get()
 
    try:
        url = f"https://api.exchangerate.host/latest?base={src}&symbols={dst}"
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        rates = response.json()["rates"]
        rate = rates[dst]
    except requests.exceptions.RequestException as e:
        messagebox.showerror("Network error", str(e))
        return
    except KeyError:
        messagebox.showerror("Bad response", f"No rate for {dst} found.")
        return
 
    converted = amount * rate
    messagebox.showinfo(
        "Result",
        f"{amount:.2f} {src} = {converted:.2f} {dst}  (rate {rate:.4f})",
    )
currencyexchnageratecalculator.py
def exchange():
    try:
        amount = float(amount_entry.get())
    except ValueError:
        messagebox.showerror("Invalid input", "Amount must be a number.")
        return
 
    src = from_currency.get()
    dst = to_currency.get()
 
    try:
        url = f"https://api.exchangerate.host/latest?base={src}&symbols={dst}"
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        rates = response.json()["rates"]
        rate = rates[dst]
    except requests.exceptions.RequestException as e:
        messagebox.showerror("Network error", str(e))
        return
    except KeyError:
        messagebox.showerror("Bad response", f"No rate for {dst} found.")
        return
 
    converted = amount * rate
    messagebox.showinfo(
        "Result",
        f"{amount:.2f} {src} = {converted:.2f} {dst}  (rate {rate:.4f})",
    )

Three layers of error handling:

  1. ValueErrorValueError if the amount field is not a number.
  2. RequestExceptionRequestException if the network is down, the server is unreachable, the request times out, or the API returns 4xx/5xx (because of raise_for_status()raise_for_status()).
  3. KeyErrorKeyError if the API responds successfully but does not include the requested currency.

The timeout=10timeout=10 keeps the GUI responsive even if the API hangs.

5. Wire the button and start the loop

currencyexchnageratecalculator.py
tk.Button(win, text="Convert", command=exchange).pack(pady=10)
win.mainloop()
currencyexchnageratecalculator.py
tk.Button(win, text="Convert", command=exchange).pack(pady=10)
win.mainloop()
  • command=exchangecommand=exchange registers the callback.
  • win.mainloop()win.mainloop() enters Tkinter’s event loop, the program lives here until the window closes.

Common Mistakes

ProblemCauseFix
GUI freezes after clickLong HTTP call running on the main threadUse threading.Threadthreading.Thread for the request, update the UI via win.afterwin.after
requests.exceptions.ConnectionErrorrequests.exceptions.ConnectionErrorNo internet, or API downShow a friendly message; consider caching the last rate
KeyError: 'EUR'KeyError: 'EUR'Misspelled currency code in the dropdownValidate codes against a known list
Result shows 9.9999999999999999.999999999999999Floating-point displayFormat with :.2f:.2f
Multiple Convert clicks stack popupsUser clicks while one is showingUse messagebox.showinfomessagebox.showinfo, which is modal — but consider disabling the button until the call completes

Variations to Try

1. Real-time updates

Use win.after(60_000, refresh_rates)win.after(60_000, refresh_rates) to re-fetch every minute and show a live “last updated” label.

2. Live result label instead of popup

Replace the messageboxmessagebox with a LabelLabel in the window that updates after each conversion. Better UX.

3. Conversion history

Append each result to a ListboxListbox:

history.py
history_box.insert("end", f"{amount} {src}{converted:.2f} {dst}")
history.py
history_box.insert("end", f"{amount} {src}{converted:.2f} {dst}")

Bonus: write the history to history.csvhistory.csv on exit.

4. Two-way swap button

A ”⇄” button that swaps the From and To selections.

5. Charts

Use matplotlib to plot a 30-day history. The API typically supports /timeseries/timeseries:

chart.py
url = "https://api.exchangerate.host/timeseries?start_date=2025-01-01&end_date=2025-01-31&base=USD&symbols=EUR"
chart.py
url = "https://api.exchangerate.host/timeseries?start_date=2025-01-01&end_date=2025-01-31&base=USD&symbols=EUR"

6. Larger currency list

Pull the full list from the API itself:

full_list.py
all_currencies = requests.get("https://api.exchangerate.host/symbols").json()["symbols"]
full_list.py
all_currencies = requests.get("https://api.exchangerate.host/symbols").json()["symbols"]

7. Crypto support

Use CoinGecko’s free API (no key required) for BTC, ETH, etc.

8. Calculator-style entry

Replace the single Entry with a numeric keypad of buttons. Helpful for touch-screen kiosks.

9. Convert to/from multiple currencies at once

Instead of a single “To”, show a table of values across ten major currencies.

10. Offline mode

Cache the last successful response to last_rates.jsonlast_rates.json. If the network fails, fall back to the cache and label the result as “stale”.

Concept Spotlight: HTTP Errors

requestsrequests returns a ResponseResponse object that does not raise on 4xx/5xx by default. Explicit checking is essential:

errors.py
r = requests.get(url, timeout=5)
print(r.status_code)        # 200 = OK, 404 = not found, 500 = server error
r.raise_for_status()        # raises HTTPError for 4xx/5xx
data = r.json()             # raises JSONDecodeError if body isn't JSON
errors.py
r = requests.get(url, timeout=5)
print(r.status_code)        # 200 = OK, 404 = not found, 500 = server error
r.raise_for_status()        # raises HTTPError for 4xx/5xx
data = r.json()             # raises JSONDecodeError if body isn't JSON

The error categories you should plan for:

  • Connection errors — DNS failure, no internet, server unreachable.
  • Timeout errors — server took too long.
  • HTTP errors — server replied but with an error status.
  • Decode errors — server replied with non-JSON content.

requests.exceptions.RequestExceptionrequests.exceptions.RequestException is the base of all of these, so a single except RequestExceptionexcept RequestException catches everything network-related.

Best Practices Demonstrated

  • Validate input before making a network call. Cheap checks first, expensive operations second.
  • Always set a timeout. Default is no limit — a hanging server can freeze your GUI forever.
  • Use raise_for_status()raise_for_status() to turn HTTP errors into exceptions you can exceptexcept.
  • Show errors in the UI, not the terminal. End users do not read terminals.
  • Format numbers explicitly. :.2f:.2f for currency always.

Real-World Applications

  • Travel apps that show prices in the user’s home currency.
  • E-commerce checkout flows handling multiple currencies.
  • Internal finance dashboards showing daily P&L in a base currency.
  • Crypto portfolio trackers.
  • Currency-aware invoice generators.

Educational Value

This project teaches:

  • GUI fundamentals — windows, widgets, layouts, callbacks, event loops.
  • HTTP and JSON — talking to the wider web from Python.
  • Robust error handling — three layers, friendly messages.
  • Separation of concerns — UI building vs. business logic vs. networking.
  • Working with currency — formatting, decimal precision.

Next Steps

  • Replace messageboxmessagebox popups with an inline result label that updates immediately.
  • Add a “favorites” panel for currency pairs the user converts often.
  • Plug into a historical-rates endpoint and draw a chart with matplotlib.
  • Wrap the conversion logic in a class for easier testing.
  • Port the GUI to CustomTkinter for a modern look.
  • Add a command-line mode: python currencyexchnageratecalculator.py --amount 100 --from USD --to EURpython currencyexchnageratecalculator.py --amount 100 --from USD --to EUR.

Conclusion

You built a real desktop application that talks to a live web service — the same architecture every modern app uses, just smaller. You handled bad input, network failures, and missing data without crashing the GUI. The full source is on GitHub. Explore more GUI and API projects on Python Central Hub.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did