To get silver prices in C#/.NET, call https://api.goldprice.dev/v1/spot/XAG-USD-SPOT with HttpClient and your goldprice.dev key in the X-API-Key header. Parse price strings into decimal fields with System.Text.Json. The example caches each symbol for 60 seconds and retries rate limited requests. Silver spot is quoted in US dollars per troy ounce. Free includes 5,000 calls a month with prices delayed by 15 minutes. Paid plans are live.
Get your goldprice.dev API key
Create a Free account at goldprice.dev/onboarding and copy your API key. Set it in the shell where you will run the app:
export GOLDPRICE_API_KEY=ga_live_replace_with_your_key_hereFree includes silver spot, 5,000 calls a month, and 10 requests a minute. Prices are delayed by 15 minutes. No credit card is required.
Create the project
The .NET base class library provides HttpClient for requests and System.Text.Json for parsing responses. Create a console app with no extra packages:
dotnet new console -o GoldpriceDemocd GoldpriceDemoWrite the silver price client
Save this as GoldpriceClient.cs. The API returns prices as strings. The custom JsonConverter reads them into decimal fields to preserve decimal places.
SpotAsync() stores each symbol in a ConcurrentDictionary for 60 seconds. It makes at most three requests when responses keep returning 429, waiting longer between attempts. Other unsuccessful responses throw immediately.
The record also reads is_stale and computed_at.
// GoldpriceClient.cs// curl-equivalent: https://api.goldprice.dev/v1/spot/XAG-USD-SPOTusing System.Collections.Concurrent;using System.Globalization;using System.Net.Http.Json;using System.Text.Json;using System.Text.Json.Serialization;namespace GoldpriceDemo;/// Converts the API's string-encoded price fields to decimal without/// the precision loss a double round-trip would introduce.public sealed class DecimalStringConverter : JsonConverter<decimal>{ public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => decimal.Parse(reader.GetString()!, CultureInfo.InvariantCulture); public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString());}public sealed record SpotResponse( [property: JsonPropertyName("symbol")] string Symbol, [property: JsonPropertyName("quote_currency")] string QuoteCurrency, [property: JsonPropertyName("price"), JsonConverter(typeof(DecimalStringConverter))] decimal Price, [property: JsonPropertyName("is_stale")] bool IsStale, [property: JsonPropertyName("computed_at")] string ComputedAt);public sealed class GoldpriceRateLimitedException : Exception{ public GoldpriceRateLimitedException() : base("goldprice: 429 rate limited") { }}public sealed class GoldpriceClient{ private const string ApiBase = "https://api.goldprice.dev"; private static readonly TimeSpan Ttl = TimeSpan.FromSeconds(60); private readonly HttpClient _http; private readonly ConcurrentDictionary<string, (SpotResponse Data, DateTimeOffset Expires)> _cache = new(); public GoldpriceClient(HttpClient http) { var apiKey = Environment.GetEnvironmentVariable("GOLDPRICE_API_KEY") ?? throw new InvalidOperationException("GOLDPRICE_API_KEY not set"); _http = http; _http.BaseAddress = new Uri(ApiBase); _http.Timeout = TimeSpan.FromSeconds(5); _http.DefaultRequestHeaders.Add("X-API-Key", apiKey); } /// Fetches the latest spot price. Cached 60s per symbol, retries 3x on 429. public async Task<SpotResponse> SpotAsync(string symbol = "XAG-USD-SPOT", CancellationToken ct = default) { if (_cache.TryGetValue(symbol, out var cached) && cached.Expires > DateTimeOffset.UtcNow) { return cached.Data; } for (var attempt = 0; attempt < 3; attempt++) { using var response = await _http.GetAsync($"/v1/spot/{symbol}", ct); if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests) { if (attempt < 2) { await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)), ct); // 1s, then 2s } continue; } if (!response.IsSuccessStatusCode) { throw new HttpRequestException($"goldprice: unexpected status {(int)response.StatusCode}"); } var data = await response.Content.ReadFromJsonAsync<SpotResponse>(cancellationToken: ct) ?? throw new JsonException("goldprice: empty response body"); _cache[symbol] = (data, DateTimeOffset.UtcNow.Add(Ttl)); return data; } throw new GoldpriceRateLimitedException(); }}Run the silver price request
Save this as Program.cs and run dotnet run from the shell where you set GOLDPRICE_API_KEY. The program prints the silver price in US dollars per troy ounce and its computation timestamp.
Repeated calls on the same client return the cached record until its 60 second lifetime ends. Restarting the program clears this cache. The sample output is an example, and your response will carry its own price and timestamp.
using GoldpriceDemo;using var http = new HttpClient();var client = new GoldpriceClient(http);var spot = await client.SpotAsync("XAG-USD-SPOT");Console.WriteLine($"{spot.Symbol}-{spot.QuoteCurrency}: {spot.Price} (computed {spot.ComputedAt})");// XAG-USD: 54.020 (computed 2026-10-23T18:12:06.354573+00:00)Switch metal or convert silver
Use SpotAsync() for silver, gold, or copper. Silver and copper spot symbols exist only in US dollars. Silver and gold are priced per troy ounce. Copper is priced per pound, so keep the unit visible when comparing results.
For silver in another currency, make a separate request to /v1/convert?from=XAG&to=EUR&amount=1&unit=gram. Conversion supports oz, gram, and kg. Without a key, prices are delayed by 15 minutes and the limit is 100 requests an hour per IP.
// Silver in USD per troy ounceawait client.SpotAsync("XAG-USD-SPOT");// Gold in USD per troy ounceawait client.SpotAsync("XAU-USD-SPOT");// Copper in USD per poundawait client.SpotAsync("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 console prints the symbol, currency, decimal price, and computation timestamp. The JSON shows the response a Free key receives, with the price as a string. Read is_stale and computed_at from the actual response before using a price.
Common errors
| Code | What you see | Fix |
|---|---|---|
| 401 | goldprice: unexpected status 401 on the silver request | Set GOLDPRICE_API_KEY to a valid key from your account in the shell running the app. Avoid printing the key in logs. An unset variable makes the constructor throw before the request; an invalid key can produce this response. |
| 404 | goldprice: unexpected status 404 with a not_found error | The API has no quote for that symbol. Silver spot is quoted in US dollars, so XAG-EUR-SPOT answers 404. Use XAG-USD-SPOT, or convert with the conversion endpoint described in the API reference. A symbol the API does not know can answer 403 plan_gated, and a malformed one answers 400 invalid_symbol. |
| 429 | All three attempts return 429, then SpotAsync() throws GoldpriceRateLimitedException | Reduce request frequency and stagger bursts. Free allows 10 requests a minute and 5,000 calls a month. The cache only helps repeated calls on the same client after a result is stored. Separate processes and simultaneous cache misses can still make extra requests. See pricing for plan limits. |
| N/A | Price deserialization fails or decimal values are parsed incorrectly | Prices arrive as JSON strings. Keep [JsonConverter(typeof(DecimalStringConverter))] on every price field. The sample parses with CultureInfo.InvariantCulture, so the server's locale cannot change how the decimal strings are read. |
Going further
- Check
IsStaleand displayComputedAtalongside the silver price - Handle missing
BidandAskvalues when your plan does not include them - Add a conversion request for silver in another currency or unit using the API reference
- Track cache hits, request failures, and rate limits to understand your app's quota use