flask-unirate

July 8, 2026 · View on GitHub

PyPI Python License

Flask extension for the UniRate currency-exchange API:

  • Drop-in UniRate(app) extension — follows the standard init_app factory pattern, registers itself on app.extensions["unirate"].
  • Jinja filters{{ amount|to_currency('EUR') }}, {{ amount|convert_currency('USD', 'JPY') }}, {{ price|format_money('USD') }}, plus to_usd / to_eur / to_gbp shortcuts.
  • Optional Flask-Caching integration — set UNIRATE_CACHE_TIMEOUT and the extension caches latest-rate / supported-currency lookups through whatever Flask-Caching backend you already have.
  • Wraps the official unirate-api Python client; full method surface is reachable through unirate.client if you need historical rates / VAT / time series.

UniRate covers 593+ fiat, crypto, and commodity codes. Latest rates and conversion are on the free tier; historical endpoints (get_historical_rate, convert_historical) require Pro.

Install

pip install flask-unirate

With Flask-Caching support:

pip install "flask-unirate[caching]"

Quick start

import os

from flask import Flask, render_template_string

from flask_unirate import UniRate

app = Flask(__name__)
app.config["UNIRATE_API_KEY"] = os.environ["UNIRATE_API_KEY"]
unirate = UniRate(app)


@app.route("/rate/<base>/<quote>")
def rate(base: str, quote: str):
    return {"rate": unirate.get_rate(base, quote)}


@app.route("/checkout/<int:amount_usd>")
def checkout(amount_usd: int):
    return render_template_string(
        """
        <p>USD: {{ amount|format_money('USD') }}</p>
        <p>EUR: {{ amount|to_eur|format_money('EUR') }}</p>
        <p>JPY: {{ amount|to_currency('JPY')|format_money('JPY', decimals=0) }}</p>
        """,
        amount=amount_usd,
    )

Configuration

KeyDefaultNotes
UNIRATE_API_KEYRequired. Falls back to the UNIRATE_API_KEY env var.
UNIRATE_TIMEOUT30 (s)HTTP timeout passed to UnirateClient.
UNIRATE_BASE_URLhttps://api.unirateapi.comOverride only if you proxy the API.
UNIRATE_DEFAULT_BASE_CURRENCYUSDDefault base for the to_currency Jinja filter.
UNIRATE_CACHE_TIMEOUTunsetIf set and Flask-Caching is initialised on the app, latest-rate / supported-currency lookups are cached for this many seconds.

Factory pattern

from flask import Flask
from flask_unirate import UniRate

unirate = UniRate()


def create_app() -> Flask:
    app = Flask(__name__)
    app.config.from_pyfile("config.py")
    unirate.init_app(app)
    return app

Inside any view (or template) you can also reach the extension through current_app:

from flask import current_app

current_app.extensions["unirate"].get_rate("USD", "EUR")

…or through the convenience helper:

from flask_unirate import get_unirate

get_unirate().get_rate("USD", "EUR")

Jinja filters

Every filter is registered automatically when you call UniRate(app) / init_app(app):

FilterExampleResult
to_currency(target, base=None)`{{ 100to_currency('EUR') }}`
convert_currency(base, target)`{{ 100convert_currency('USD', 'JPY') }}`
format_money(currency, decimals=2)`{{ 1234.5format_money('USD') }}`
to_usd / to_eur / to_gbp`{{ 100to_eur }}`

Chain them: {{ amount|to_eur|format_money('EUR') }}.

Flask-Caching integration

from flask import Flask
from flask_caching import Cache

from flask_unirate import UniRate

app = Flask(__name__)
app.config.update(
    UNIRATE_API_KEY="...",
    UNIRATE_CACHE_TIMEOUT=300,         # 5 minutes
    CACHE_TYPE="RedisCache",
    CACHE_REDIS_URL="redis://localhost",
)
Cache(app)
UniRate(app)

The extension auto-discovers the Cache instance off app.extensions['cache'] — so all of Flask-Caching's backends (SimpleCache, RedisCache, MemcachedCache, FileSystemCache, …) work with no extra wiring. Failures fall through to a fresh API call rather than raising.

Errors

Errors come from the underlying unirate-api client and propagate unchanged:

HTTPException classMeaning
401unirate.exceptions.AuthenticationErrorMissing or invalid API key
404unirate.exceptions.InvalidCurrencyErrorCurrency not found
429unirate.exceptions.RateLimitErrorRate limit exceeded
503unirate.exceptions.APIErrorService unavailable
403(raised as requests.HTTPError by raise_for_status)Pro plan required (historical, commodities)

Wrap the call site in try / except UnirateError to catch the whole family.

Compatibility

  • Python 3.9 – 3.13
  • Flask ≥ 2.0
  • unirate-api ≥ 1.0
  • (Optional) flask-caching ≥ 2.0

UniRate ecosystem

UniRate ships official integrations for 40+ ecosystems, all maintained under the UniRate-API org.

Core clients (9 languages) Python · Node.js / TypeScript · Go · Rust · Java · Ruby · PHP · .NET · Swift

JavaScript / TypeScript React · Next.js · Remix · SvelteKit · Vue · Angular · Nuxt · NestJS · tRPC

Static-site generators Astro · Eleventy · Hugo · Jekyll

CMS & e-commerce Wagtail · WordPress · WooCommerce · Drupal · Strapi · Medusa · Symfony · Laravel · Directus

Data, AI & backend LangChain (Python) · LangChain.js · FastAPI · Flask · Django REST Framework · Apache Airflow · dbt

Platform & tools MCP server · CLI · Cloudflare Workers · Home Assistant · n8n · Google Sheets · VS Code · Obsidian

Money library bridges money gem (Ruby) · NodaMoney (.NET)

Get a free API key at unirateapi.com.

License

MIT