Skip to content

Currency Exchange Rate Calculator GUI

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 requests 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.
  • Python 3.6 or above.
  • A text editor or IDE.
  • An internet connection.
  • Comfort installing packages with pip.
  • Familiarity with functions and conditionals.
install
pip install requests
  • tkinter ships with Python — no install needed.
  • requests is the de-facto library for HTTP in Python.

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

APIURL patternNotes
exchangerate-apihttps://open.er-api.com/v6/latest/USDFree tier, no key, daily updates.
frankfurter.apphttps://api.frankfurter.app/latest?from=USD&to=EUROpen-source, ECB rates.
Fixer.iohttps://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, ... }
}

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

  1. Create a folder named currency-exchange-calculator.
  2. Inside it, create currencyexchnageratecalculator.py.
  3. Open the folder in your editor.
Currency Exchange Rate Calculator GUI pch.viewSource
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
python currencyexchnageratecalculator.py

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

Read from the top: this is what runs when you execute the file, and which function calls which. It is generated from the code, so it cannot drift from it.

diagram Diagram mermaid
currencyexchnageratecalculator.py
import tkinter as tk
from tkinter import ttk, messagebox
import requests
  • tkinter for the window, ttk for themed widgets (dropdowns look prettier than the legacy OptionMenu), and messagebox for the result popup.
  • requests for the HTTP call.
currencyexchnageratecalculator.py
win = tk.Tk()
win.title("Currency Exchange Calculator")
win.geometry("400x300")

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

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()
  • Entry is a one-line text input.
  • Combobox is a dropdown the user can also type into; .set(value) chooses an initial selection.
  • .pack() is the simplest of Tkinter’s layout managers — it stacks widgets top-to-bottom.
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. ValueError if the amount field is not a number.
  2. RequestException if the network is down, the server is unreachable, the request times out, or the API returns 4xx/5xx (because of raise_for_status()).
  3. KeyError if the API responds successfully but does not include the requested currency.

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

currencyexchnageratecalculator.py
tk.Button(win, text="Convert", command=exchange).pack(pady=10)
win.mainloop()
  • command=exchange registers the callback.
  • win.mainloop() enters Tkinter’s event loop, the program lives here until the window closes.
ProblemCauseFix
GUI freezes after clickLong HTTP call running on the main threadUse threading.Thread for the request, update the UI via win.after
requests.exceptions.ConnectionErrorNo internet, or API downShow a friendly message; consider caching the last rate
KeyError: 'EUR'Misspelled currency code in the dropdownValidate codes against a known list
Result shows 9.999999999999999Floating-point displayFormat with :.2f
Multiple Convert clicks stack popupsUser clicks while one is showingUse messagebox.showinfo, which is modal — but consider disabling the button until the call completes

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

Replace the messagebox with a Label in the window that updates after each conversion. Better UX.

Append each result to a Listbox:

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

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

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

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

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

Pull the full list from the API itself:

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

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

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

9. Convert to/from multiple currencies at once

Section titled “9. Convert to/from multiple currencies at once”

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

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

requests returns a Response 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

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.RequestException is the base of all of these, so a single except RequestException catches everything network-related.

  • 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() to turn HTTP errors into exceptions you can except.
  • Show errors in the UI, not the terminal. End users do not read terminals.
  • Format numbers explicitly. :.2f for currency always.
  • 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.

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.
  • Replace messagebox 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 EUR.

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.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading