Python project 12
Python Currency Converter Project
Create a currency converter that retrieves current reference rates from the public Frankfurter v2 API. It validates three-letter currency codes, uses Decimal for money-safe arithmetic, handles network failures, and shows the date of the rate used.
Learn Python with mentor-guided projectsView all project ideas
What you will build
The user enters a base currency, target currency, and amount. The program retrieves one current rate, calculates the converted value, rounds it to two decimal places, and clearly labels the result as a reference rate rather than a bank quote.
Skills practised
- HTTPS API requests
- JSON response handling
- Decimal arithmetic
- Network and input errors
Requirements
- Python 3.10 or later
- An internet connection
- No API key or external package
- About 35 minutes to build
Full Python code
Save this code as currency_converter.py, then follow the run instructions below.
"""Convert currencies with the public Frankfurter v2 exchange-rate API."""
from __future__ import annotations
import json
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
API_ROOT = "https://api.frankfurter.dev/v2"
def read_currency(prompt: str) -> str:
while True:
code = input(prompt).strip().upper()
if len(code) == 3 and code.isascii() and code.isalpha():
return code
print("Enter a three-letter currency code such as INR, USD, or EUR.")
def read_amount() -> Decimal:
while True:
try:
amount = Decimal(input("Amount: ").strip())
if not amount.is_finite() or amount < 0:
raise InvalidOperation
return amount
except InvalidOperation:
print("Enter a valid zero or positive amount.")
def fetch_rate(base: str, quote: str) -> tuple[Decimal, str]:
"""Return the latest rate and provider date for one currency pair."""
if base == quote:
return Decimal("1"), "same currency"
url = f"{API_ROOT}/rate/{base}/{quote}"
request = Request(url, headers={"User-Agent": "SoftenantCurrencyProject/1.0"})
try:
with urlopen(request, timeout=10) as response:
data = json.loads(response.read().decode("utf-8"), parse_float=Decimal)
return Decimal(data["rate"]), str(data["date"])
except HTTPError as error:
if error.code == 422:
raise ValueError("The API did not recognise one of those currency codes.") from error
raise ConnectionError(f"The rate service returned HTTP {error.code}.") from error
except (URLError, TimeoutError) as error:
raise ConnectionError("Could not reach the exchange-rate service.") from error
except (json.JSONDecodeError, KeyError, TypeError, InvalidOperation) as error:
raise ConnectionError("The rate service returned an unexpected response.") from error
def convert(amount: Decimal, rate: Decimal) -> Decimal:
return (amount * rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
def main() -> None:
print("Python Currency Converter")
print("Rates are reference rates, not bank or card transaction quotes.\n")
base = read_currency("From currency: ")
quote = read_currency("To currency: ")
amount = read_amount()
try:
rate, rate_date = fetch_rate(base, quote)
except (ValueError, ConnectionError) as error:
print(f"Conversion failed: {error}")
return
result = convert(amount, rate)
print(f"\n{amount:.2f} {base} = {result:.2f} {quote}")
print(f"Reference rate: 1 {base} = {rate} {quote} ({rate_date})")
if __name__ == "__main__":
main()
How the project works
fetch_rate() calls the documented /v2/rate/BASE/QUOTE endpoint and parses JSON floating-point values directly as Decimal. A timeout prevents the program from waiting indefinitely.
convert() multiplies the user amount by the reference rate and applies explicit two-decimal, half-up rounding. Currency markets and retail providers differ, so the displayed result should not be presented as a guaranteed card, bank, or cash-exchange price.
Run the project
- Run
python currency_converter.py. - Enter codes such as
INR,USD, orEUR. - Enter a zero or positive amount.
- Read the converted result, rate, and provider date.
Accuracy and safety notes
- Source: Frankfurter aggregates reference-rate providers and requires no API key.
- Precision: Decimal avoids common binary floating-point surprises.
- Timing: reference rates update when providers publish; they are not tick-by-tick trading prices.
Ways to extend it
Add a currency list, historical-date input, cached rates, a Tkinter interface, or a comparison between several target currencies. Read the official Frankfurter Python guide before extending the API calls.
Continue learning Python
Build the next project, return to the Softenant project library, or explore the Python programming course in Vizag for structured lessons, mentor feedback, and portfolio practice.