To get silver prices in PHP, call goldprice.dev at /v1/spot/XAG-USD-SPOT using native cURL and your API key in the X-API-Key header. The price is in USD per troy ounce. Keep prices as strings and use BCMath for decimal arithmetic. Free prices are 15 minutes late; paid plans are live. The example caches responses within the same process and retries requests that return 429.
Get your goldprice.dev API key
Create a free account at goldprice.dev/onboarding and get your API key. Set it as an environment variable:
export GOLDPRICE_API_KEY=ga_live_replace_with_your_key_hereFree includes silver spot in USD, 5,000 calls a month and 10 requests a minute. Prices are 15 minutes late. No credit card required.
Check the required extensions
This example needs curl, json and bcmath enabled in the PHP installation that runs it. No Composer packages are required. Check the loaded modules below. If an extension is missing, install or enable it for your PHP version.
php -m | grep -E '^(curl|json|bcmath)$'# curl# json# bcmath <- if missing, install php8.3-bcmath (or enable in php.ini)Write the silver price client
Save this as goldprice.php. The spot() method keeps a separate cached response for each symbol for 60 seconds within the process. It makes up to 3 attempts when requests return 429.
Prices stay as strings for BCMath. Read is_stale and computed_at when deciding how to display the result. Free prices remain delayed even when is_stale is false.
<?php// php: goldprice.php, native cURL + BCMath, no Composer// curl-equivalent: https://api.goldprice.dev/v1/spot/XAG-USD-SPOTdeclare(strict_types=1);const API_BASE = 'https://api.goldprice.dev';const TIMEOUT = 5; // secondsconst TTL = 60; // secondsfinal class RateLimitedException extends RuntimeException {}final class GoldpriceClient{ private string $apiKey; /** @var array<string, array{data: array, expires: int}> */ private static array $cache = []; public function __construct() { $key = getenv('GOLDPRICE_API_KEY'); if ($key === false || $key === '') { throw new RuntimeException('GOLDPRICE_API_KEY not set'); } $this->apiKey = $key; } /** * Fetch the latest spot price. Cached 60s per symbol, retries 3x on 429. * Prices stay as strings for BCMath. */ public function spot(string $symbol = 'XAG-USD-SPOT'): array { $now = time(); if (isset(self::$cache[$symbol]) && self::$cache[$symbol]['expires'] > $now) { return self::$cache[$symbol]['data']; } for ($attempt = 0; $attempt < 3; $attempt++) { try { $data = $this->fetch($symbol); self::$cache[$symbol] = ['data' => $data, 'expires' => $now + TTL]; return $data; } catch (RateLimitedException) { if ($attempt < 2) { sleep(1 << $attempt); // 1s, then 2s } } } throw new RateLimitedException('goldprice: 429 after 3 retries'); } private function fetch(string $symbol): array { $ch = curl_init(API_BASE . '/v1/spot/' . rawurlencode($symbol)); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => TIMEOUT, CURLOPT_HTTPHEADER => ['X-API-Key: ' . $this->apiKey], ]); $body = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); curl_close($ch); if ($code === 429) { throw new RateLimitedException('rate limited'); } if ($code !== 200 || $body === false) { throw new RuntimeException("goldprice: unexpected status {$code}"); } return json_decode((string) $body, true, 512, JSON_THROW_ON_ERROR); }}$client = new GoldpriceClient();$r = $client->spot('XAG-USD-SPOT');// Prices are strings; use BCMath for exact math (e.g. price for 5 oz):$fiveOz = bcmul($r['price'], '5', 4);printf("%s-%s: %s (computed %s)\n", $r['symbol'], $r['quote_currency'], $r['price'], $r['computed_at']);printf("5 troy oz = %s %s\n", $fiveOz, $r['quote_currency']);Run the client
Run the file from the shell where you set GOLDPRICE_API_KEY. It fetches silver in USD per troy ounce and calculates a total with BCMath. The output below is an example, not a current quote.
Further calls within the cache window reuse the response in the same process. Under PHP-FPM, this static array does not persist across requests. Use a shared cache if separate requests need to reuse a response.
php goldprice.php# XAG-USD: 54.020 (computed 2026-10-23T18:12:06.354573+00:00)# 5 troy oz = 270.1000 USDSwitch metal or convert currency
Use spot() for silver, gold or copper. Silver and gold here are priced in USD per troy ounce. Copper is priced in USD per pound. Each symbol has a separate cache entry.
Silver spot symbols exist only in USD. For silver in euros per gram, make a separate GET request to /v1/convert?from=XAG&to=EUR&amount=1&unit=gram. This conversion works without a key, with prices 15 minutes late and a limit of 100 requests an hour per IP.
$client = new GoldpriceClient();// Silver in USD per troy ounce$client->spot('XAG-USD-SPOT');// Gold in USD per troy ounce$client->spot('XAU-USD-SPOT');// Copper in USD per pound$client->spot('HG-USD-SPOT');Expected output
{ "symbol": "XAG", "quote_currency": "USD", "unit": "troy_ounce", "contract_type": "spot", "price": "54.020", "is_stale": false, "computed_at": "2026-10-23T18:12:06.354573+00:00"}The CLI prints the silver price and timestamp, then a calculated total. The JSON shows the response a Free key receives, with prices as strings. Check is_stale and computed_at before displaying a response.
Common errors
| Code | What you see | Fix |
|---|---|---|
| 401 | RuntimeException: goldprice: unexpected status 401 | Check that the API key is valid and available to the PHP process. Under PHP-FPM, configure GOLDPRICE_API_KEY in the server environment; exporting it in your terminal does not configure web requests. Get your key through your account and keep it on the server. |
| 429 | All request attempts return 429, then RateLimitedException is thrown | Check your request rate and remaining quota. Free allows 10 requests a minute and 5,000 calls a month. A shared cache reduces duplicate calls across web requests, but retries do not restore an exhausted quota. Reduce requests or review the limits at pricing. |
| N/A | Rounding errors after converting silver prices to floats | Keep the price string returned by the API. Use bcadd, bcmul and bccomp with an explicit scale for decimal arithmetic. Converting to (float) first can lose precision. If the BCMath functions are undefined, enable bcmath in the PHP installation running the code. |
Going further
- Share cached responses across PHP-FPM requests, with separate entries for each symbol
- Check
is_staleand displaycomputed_atbeside the silver price - Use the conversion endpoint for silver in another currency or in
oz,gramorkg - For your own per-gram calculation, use BCMath and the conversion of 31.1035 g per troy ounce
- Read the API reference for response fields and the quickstart for request setup