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.
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.
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.
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.
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.
python manage.py migratepython manage.py runserverExpected output
{ "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
| Code | What you see | Fix |
|---|---|---|
| 401 | Django returns a gateway error with upstream_status set to 401 | The 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. |
| 429 | Django returns a gateway error with upstream_status set to 429 | Read 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. |
| 502 | The 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/A | Django 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-SPOTand a separate cache key - Show a clear warning beside prices marked
is_stale