TutorialsCode

How to get live gold prices in Gold price in Django

Fetch a gold quote with requests, keep your API key on the server, and cache the result with Django.

23 Oct 2026 · 4 min read

To get a gold price in Django, call https://api.goldprice.dev/v1/spot/XAU-USD-SPOT from a function view using requests. Read the API key from the environment and keep it on the server. Return a JsonResponse and cache successful quotes for a minute with django.core.cache. Return a JSON error if the API fails. Keep the price as a decimal string.

Get your API key and create the project

Get a free API key at /onboarding. No credit card is required. Start with Python 3.12 installed, an empty folder, and a Bash or zsh terminal.

Run these commands, then paste your key at the hidden prompt. The key stays in the terminal environment. Keep this terminal open for the remaining steps.

shell
python3.12 -m venv .venv. .venv/bin/activatepython -m pip install 'Django>=5.2,<6' requestspython -m django startproject goldsitecd goldsiteexport GOLDPRICE_API_KEY="$(python -c 'from getpass import getpass; print(getpass("goldprice.dev API key: "))')"

Configure the key and cache

Append these lines to the generated goldsite/settings.py. Keep its existing settings. Django reads the key from the environment when it starts.

LocMemCache stores quotes in memory within each process. Restarting the server clears it. The view below sets the cache lifetime to a minute.

goldsite/settings.py
import osGOLDPRICE_API_KEY = os.environ["GOLDPRICE_API_KEY"]CACHES = {    "default": {        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",        "LOCATION": "goldprice-quotes",    }}

Write the gold price view

Create goldsite/views.py. The view checks the cache before calling the API. It validates the price and freshness fields, then returns the price as its original decimal string.

Failed requests and invalid responses return a JSON error with HTTP status 502. Errors are never cached. When the API limits requests, the view forwards its Retry-After header without retrying.

goldsite/views.py
from decimal import Decimal, InvalidOperationimport requestsfrom django.conf import settingsfrom django.core.cache import cachefrom django.http import JsonResponsefrom django.utils.dateparse import parse_datetimefrom django.utils.timezone import is_naivefrom django.views.decorators.http import require_GET@require_GETdef gold_price(request):    cache_key = "goldprice:XAU-USD-SPOT"    quote = cache.get(cache_key)    if quote is not None:        return JsonResponse(quote)    try:        response = requests.get(            "https://api.goldprice.dev/v1/spot/XAU-USD-SPOT",            headers={"Authorization": f"Bearer {settings.GOLDPRICE_API_KEY}"},            timeout=10,            allow_redirects=False,        )    except requests.RequestException:        return JsonResponse({"error": "Gold price request failed."}, status=502)    if response.status_code != 200:        error = JsonResponse(            {                "error": "Gold price request failed.",                "upstream_status": response.status_code,            },            status=502,        )        if response.status_code == 429 and "Retry-After" in response.headers:            error["Retry-After"] = response.headers["Retry-After"]        return error    try:        raw = response.json()        quote = {field: raw[field] for field in (            "symbol", "quote_currency", "unit", "contract_type",            "price", "is_stale", "computed_at",        )}        if not isinstance(quote["price"], str):            raise ValueError("Price must be a decimal string")        price = Decimal(quote["price"])        if not price.is_finite() or price <= 0:            raise ValueError("Invalid price")        if not isinstance(quote["is_stale"], bool):            raise ValueError("Invalid freshness flag")        computed_at = parse_datetime(quote["computed_at"])        if computed_at is None or is_naive(computed_at):            raise ValueError("Invalid timestamp")    except (ValueError, TypeError, KeyError, InvalidOperation):        return JsonResponse({"error": "Invalid gold price response."}, status=502)    cache.set(cache_key, quote, timeout=60)    return JsonResponse(quote)

Add the URL route

Replace goldsite/urls.py with this minimal configuration. It maps /gold-price/ to the gold_price function. The browser calls this local route, while Django sends the authenticated request to goldprice.dev.

goldsite/urls.py
from django.urls import pathfrom .views import gold_priceurlpatterns = [    path("gold-price/", gold_price, name="gold_price"),]

Run Django and request the quote

From the folder containing manage.py, run the commands below in the terminal where you set the key. Leave the development server running.

In another terminal, run curl -i http://127.0.0.1:8000/gold-price/. Repeat the request within a minute to use the cached quote. The curl request needs no API key.

If you display prices on the Free plan, place a visible link to goldprice.dev near them.

shell
python manage.py migratepython manage.py runserver

Expected output

JSON
{  "symbol": "XAU",  "quote_currency": "USD",  "unit": "troy_ounce",  "contract_type": "spot",  "price": "4023.54",  "is_stale": false,  "computed_at": "2026-10-23T10:51:32.965761+00:00"}

The JSON from your Django route keeps the API field names and the price string. The price is in USD per troy ounce. This sample shows the response shape; your price, freshness flag and timestamp can differ.

Common errors

CodeWhat you seeFix
401Django returns a gateway error with upstream_status set to 401The API rejected the supplied key. Enter a valid key from /onboarding into the environment prompt, then restart Django in that terminal. Keep the key out of browser code and committed settings.
429Django returns a gateway error with upstream_status set to 429Read the forwarded Retry-After header and wait before requesting again. Do not retry in a tight loop. Check your usage and plan limits. Separate workers and simultaneous requests on an empty cache can still make separate API calls.
502The response contains Gold price request failed. or Invalid gold price response.Check whether the server can reach the API. If the JSON includes upstream_status, inspect that status too. The view returns a gateway error for connection failures, timeouts and invalid quote data. It caches none of these errors.
N/ADjango stops at startup with KeyError: 'GOLDPRICE_API_KEY'Set the environment variable in the same terminal that starts Django. The setting deliberately requires a key. If you opened a new terminal, activate the virtual environment and repeat the key prompt before starting the server.

Going further

  • Use a shared Django cache when running multiple workers
  • Add a separate view for daily history using /v1/bars
  • Request gold in EUR with XAU-EUR-SPOT and a separate cache key
  • Show a clear warning beside prices marked is_stale

Questions

What does the returned gold price measure?

The Django route returns the gold spot price in US dollars per troy ounce. A troy ounce contains 31.1035 g. The quote_currency and unit fields identify those units, while price remains a decimal string.

Will this setup fit the free tier?

Free includes 5,000 calls a month and 10 requests a minute, with no credit card required. Cache hits avoid an API call, but continuous use can still exhaust the allowance. Free prices are fifteen minutes late and require a visible link to goldprice.dev near displayed prices.

Can the browser see my API key?

The browser calls your Django route without the goldprice.dev key. Django reads the key from the server environment and sends it only in the API request header. The view returns selected quote fields and generic errors, never the key.

Does the cache work across Django workers?

LocMemCache belongs to one process. Each Django worker keeps its own quotes, and restarting a worker clears them. Use a shared cache for multiple workers. Simultaneous requests that find an empty cache can still make separate API calls.

How should I calculate with the price?

Construct a Python Decimal directly from the returned price string before doing arithmetic. Avoid converting through float, which can introduce rounding differences. The example validates the decimal value but keeps the original string in its JSON response.

How do I check whether a quote is fresh?

Check both is_stale and computed_at before displaying a quote. The view preserves these fields, including when serving cached data. A false stale flag does not remove the Free plan's fifteen minute delay. Paid plans provide live prices.

Can Claude write this Django view?

Yes. Ask Claude for a Django function view using requests, an environment variable for the key, a cache lasting a minute, and JSON errors for failed API calls. Give it the API reference, then review and run the generated code.

Keep reading

Start with a free API key

5,000 requests a month on the free plan. No credit card.

Get a free key