C# / .NET examples
Provider configuration, cancellation, and structured error handling.
Add providers
using AffordableGeoApis;
using var client = new GeocodingClient(new ClientOptions
{
BaseUrl = Environment.GetEnvironmentVariable("GEOCODING_BASE_URL")!,
ApiKey = Environment.GetEnvironmentVariable("GEOCODING_API_KEY")!,
ConfidenceThreshold = 0.8,
MaxFallbackRequests = 1000,
FallbackCircuitBreaker = new(3, 30_000),
Fallbacks = [
new("google", Environment.GetEnvironmentVariable("GOOGLE_MAPS_API_KEY")!),
new("here", Environment.GetEnvironmentVariable("HERE_API_KEY")!),
],
});
var response = await client.GeocodeAsync(new AddressRequest(
Street: "10 Downing Street", City: "London", Country: "GB"
));
Console.WriteLine($"{response.Provider}: {response.FallbackReason}");
foreach (var attempt in response.Attempts)
{
Console.WriteLine($"{attempt.Provider}: {attempt.Outcome}");
}
if (response.ProviderResponse is { } body)
{
Console.WriteLine(body.GetRawText());
}
GB skips primary and routes to backups. The raw selected body is available as a nullable JsonElement; check provider and field types before consuming it. The first nonempty backup wins. Review fallback policy before choosing an order or threshold.
Bound the complete call
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(15));
var response = await client.ReverseGeocodeAsync(
new ReverseRequest(Near: new Point(40.7074, -74.0113)),
deadline.Token
);
This deadline covers the entire provider chain. Constructor TimeoutMs limits each provider request separately, including response body reading.
For caller-controlled cancellation:
using var cancellation = new CancellationTokenSource();
var pending = client.GeocodeAsync(
new AddressRequest("11 Wall Street", City: "New York"),
cancellation.Token
);
cancellation.Cancel();
try
{
await pending;
}
catch (OperationCanceledException) when (cancellation.IsCancellationRequested)
{
Console.WriteLine("Request cancelled");
}
Cancellation stops the chain. A provider timeout instead raises GeocodingError with code timeout and can trigger fallback.
Handle provider errors
using System.Text.Json;
try
{
var response = await client.GeocodeAsync(
new AddressRequest("11 Wall Street", City: "New York")
);
if (response.Results.Count == 0) Console.WriteLine("No match");
}
catch (GeocodingError error)
{
Console.Error.WriteLine(JsonSerializer.Serialize(error, GeocodingClient.JsonOptions));
}
The error JSON includes message, provider, code, optional status, and attempts. HTTP 4xx and malformed primary responses do not call backups. A successful recovery exposes the original primary error in response.Attempts. See error handling.
Filter primary sources
var response = await client.GeocodeAsync(new AddressRequest(
Street: "11 Wall Street", City: "New York", State: "NY",
ExcludeSources: ["osm"]
));
This setting affects primary results only. Configured backups can still run if filtering leaves no acceptable primary candidates.
Reuse and transport
Reuse one client across calls to share backup budget and breaker state. Concurrent calls on the same client share these controls. Dispose the client when your application shuts down.
Pass an optional HttpMessageHandler as the constructor's second argument to use a custom transport or mock. It must honor cancellation and disable redirects. The client owns and disposes the supplied handler. The default transport disables redirects.
Address autocomplete
var response = await client.AutocompleteAsync(new AutocompleteRequest("11 Wall", "US"));
if (response.Suggestions.Count > 0)
{
var address = await client.ResolveAutocompleteAsync(response.Suggestions[0]);
Console.WriteLine($"{address.Label}: {address.Lat}, {address.Lon}");
}
Keep the returned suggestion object, including its provider and Google session token, until selection. See the autocomplete guide.
For a Google-backed typing session, reuse one SessionToken across searches:
var sessionToken = Guid.NewGuid().ToString();
var response = await client.AutocompleteAsync(new AutocompleteRequest(
Q: "11 Wall", Country: "US", SessionToken: sessionToken
));
Pass the selected suggestion unchanged to resolution. Generate a new token for the next session. The token is SDK-only; it is not a primary HTTP field. Resolution is a separate provider call, outside fallback budgets/circuit breakers, and returns one address without an envelope or attempt history.