A production-grade, strongly typed, AOT-safe .NET client suite for the CoinGecko API.
Four packages — install only what you need:
- Complete API coverage — every documented CoinGecko REST endpoint on core + onchain (GeckoTerminal) surfaces, plus the WebSocket beta stream and the hosted MCP server.
- Strong typing end-to-end — typed options records per endpoint,
[Flags]+[EnumMember]enums for closed-set params, discriminated plan tiers, and a 7-exception hierarchy with correlation IDs. - Resilient by default — built-in 429
Retry-Afterhandler + exponential-backoff retry for transient 5xx / socket errors. No Polly dependency; consumers opt in by chainingAddStandardResilienceHandler()if they want more. - Plan-aware —
[RequiresPlan(...)]attribute gates higher-tier endpoints before the HTTP call, throwingCoinGeckoPlanExceptionwith the required tier. No wasted credits on unauthorized calls. - AOT + trim-safe — source-generated
System.Text.Json, zero reflection on the hot path. Validated via a Blazor WebAssembly sample withTrimMode=full. - DI-first —
AddCoinGeckoApi(...)returnsIHttpClientBuilderso consumers can layer their own handlers (custom resilience, logging, distributed tracing). - Observability included —
ActivitySource("CoinGecko.Api")+LoggerMessagesource-gen events; zero forced OpenTelemetry dependency. - Two ways to plug into agents —
CoinGecko.Api.AiAgentHubwraps the REST client asAIFunctiontools;CoinGecko.Api.AiAgentHub.Mcpfetches CoinGecko's hosted MCP tools. SameIReadOnlyList<AIFunction>return type — swap in one line.
Only install what you use. They compose:
# REST core (most common case)
dotnet add package CoinGecko.Api
# Opt-in extensions (preview):
dotnet add package CoinGecko.Api.WebSockets # streaming
dotnet add package CoinGecko.Api.AiAgentHub # MEAI tools backed by REST
dotnet add package CoinGecko.Api.AiAgentHub.Mcp # MCP client to CoinGecko's hosted MCPusing CoinGecko.Api;
builder.Services.AddCoinGeckoApi(opts =>
{
opts.ApiKey = builder.Configuration["CoinGecko:ApiKey"];
opts.Plan = CoinGeckoPlan.Demo; // Demo / Basic / Analyst / Lite / Pro / ProPlus / Enterprise
});
// Inject ICoinGeckoClient anywhere:
public sealed class PriceService(ICoinGeckoClient gecko)
{
public Task<Coin> GetBtcAsync(CancellationToken ct)
=> gecko.Coins.GetAsync("bitcoin", ct: ct);
}using CoinGecko.Api;
using var scope = CoinGeckoClientFactory.Create("my-api-key", CoinGeckoPlan.Pro);
var btc = await scope.Client.Coins.GetAsync("bitcoin");
Console.WriteLine($"BTC: ${btc.MarketData!.CurrentPrice!["usd"]:N2}");List endpoints expose an IAsyncEnumerable<T> sibling that walks pages until exhaustion:
await foreach (var m in gecko.Coins.EnumerateMarketsAsync("usd"))
{
Console.WriteLine($"{m.MarketCapRank,4} {m.Symbol,-6} ${m.CurrentPrice,10:N2}");
}try
{
var data = await gecko.Coins.GetOhlcRangeAsync(/*...*/); // Analyst+ endpoint
}
catch (CoinGeckoPlanException ex) { /* current plan too low; ex.RequiredPlan / ex.ActualPlan */ }
catch (CoinGeckoRateLimitException ex) { /* 429; ex.RetryAfter available */ }
catch (CoinGeckoAuthException ex) { /* 401 / 403 */ }
catch (CoinGeckoNotFoundException) { /* 404 — unknown coin id etc. */ }
catch (CoinGeckoValidationException ex) { /* 400 */ }
catch (CoinGeckoServerException ex) { /* 5xx after retries */ }
catch (CoinGeckoException ex) { /* base type */ }Every exception exposes StatusCode, RawBody, and RequestId (correlates with ActivitySource / ILogger events).
Accessed from ICoinGeckoClient:
| Sub-client | Endpoints covered |
|---|---|
Coins |
list · markets · detail · tickers · history · market_chart · market_chart/range · OHLC · OHLC/range (A+) · supply charts (A+) · contract lookups · top gainers/losers (A+) · new listings (P+) |
Nfts |
list · detail · contract lookup · markets (B+) · market_chart (B+) · tickers (B+) |
Exchanges |
list · detail · tickers · volume_chart · volume_chart/range (B+) |
Derivatives |
tickers · exchanges list · exchanges detail · exchanges id-map |
Categories |
list · with market data |
AssetPlatforms |
list · token lists (B+) |
Companies |
public treasury by coin (B+) |
Simple |
price · token_price by contract · supported vs_currencies |
Global |
global market · DeFi · market_cap_chart (B+) |
Search |
coin/NFT/exchange/category search |
Trending |
trending coins + NFTs + categories |
Onchain |
29 GeckoTerminal endpoints — networks, DEXes, pools, tokens, OHLCV, trades, simple token price, search, categories (B+/A+) |
Key |
plan + rate limit + credits remaining (B+) |
Ping |
health check |
A+ = Analyst or higher, B+ = Basic or higher (any paid plan), P+ = Pro or higher.
Every tier gate is enforced client-side by [RequiresPlan] — attempts to call a Pro-only method with a Demo key throw CoinGeckoPlanException before issuing the HTTP request.
Real-time feeds via CoinGecko's WebSocket beta. Analyst+ plan required. Four typed channels:
| Channel | Tick type | Purpose |
|---|---|---|
C1 CGSimplePrice |
CoinPriceTick |
CoinGecko aggregated coin prices |
G1 OnchainSimpleTokenPrice |
OnchainTokenPriceTick |
GeckoTerminal token prices |
G2 OnchainTrade |
DexTrade |
DEX pool swaps |
G3 OnchainOHLCV |
OnchainOhlcvCandle |
DEX pool OHLCV candles |
using CoinGecko.Api.WebSockets;
using Microsoft.Extensions.DependencyInjection;
services.AddCoinGeckoStream(opts =>
{
opts.ApiKey = Environment.GetEnvironmentVariable("COINGECKO_API_KEY");
opts.AutoReconnect = true; // default
opts.MaxReconnectAttempts = 10; // default
opts.HeartbeatTimeout = TimeSpan.FromSeconds(25); // default
opts.MaxSubscriptionsPerChannel = 100; // default (mirrors upstream cap)
});
var stream = sp.GetRequiredService<ICoinGeckoStream>();
stream.StateChanged += (_, e) => Console.WriteLine($"{e.Previous} → {e.Current}");
await stream.ConnectAsync();
var sub = await stream.SubscribeCoinPricesAsync(
coinIds: ["bitcoin", "ethereum"],
vsCurrencies: ["usd"],
onTick: tick => Console.WriteLine(
$"{tick.CoinId}: ${tick.Price:N2} ({tick.PricePercentChange24h:+0.00;-0.00}%)"));
// Later:
await sub.DisposeAsync(); // unsubscribe this subscription
await stream.DisconnectAsync(); // close the socketFeatures: auto-reconnect with decorrelated-jitter exponential backoff, heartbeat watchdog, automatic resubscription on reconnect, subscription-cap enforcement.
Runnable example: samples/CoinGecko.Api.Samples.StreamConsole.
Wrap the REST client as Microsoft.Extensions.AI function tools, consumable by any IChatClient (OpenAI, Anthropic, Azure OpenAI, Ollama, Gemini, Bedrock — anything MEAI-compatible).
using CoinGecko.Api;
using CoinGecko.Api.AiAgentHub;
using Microsoft.Extensions.AI;
var tools = CoinGeckoAiTools.Create(gecko, new CoinGeckoAiToolsOptions
{
Tools = CoinGeckoToolSet.CoinPrices | CoinGeckoToolSet.Trending,
MaxResults = 25,
ToolFilter = name => !name.Contains("onchain"), // optional custom filter
});
IChatClient chat = /* any IChatClient */;
var response = await chat.GetResponseAsync(
"What's BTC at right now and what's trending?",
new ChatOptions { Tools = tools.Cast<AITool>().ToArray() });Nine built-in tools across eight categories (CoinPrices, CoinSearch, MarketData, Trending, Categories, Nfts, Derivatives, Onchain). Each tool emits a compact projection (e.g. CoinPriceQuote, MarketSnapshot) rather than the full DTO, to conserve LLM context.
Runnable example: samples/CoinGecko.Api.Samples.AgentDemo.
Fetch tool definitions directly from CoinGecko's hosted MCP server — no REST client involved. Same IReadOnlyList<AIFunction> return type, so swapping between REST-backed tools and MCP tools is a one-line change.
using CoinGecko.Api.AiAgentHub.Mcp;
using Microsoft.Extensions.AI;
var tools = await CoinGeckoMcp.ConnectAsync(
apiKey: Environment.GetEnvironmentVariable("COINGECKO_API_KEY")!,
plan: CoinGeckoPlan.Pro);
IChatClient chat = /* any IChatClient */;
var response = await chat.GetResponseAsync(
"What's BTC at right now?",
new ChatOptions { Tools = tools.Cast<AITool>().ToArray() });Need finer control? CoinGeckoMcp.CreateClientAsync(...) returns the underlying MCP client directly. Configurable transport (StreamableHttp default, Sse fallback), per-call timeout, and optional BaseAddress override for proxied / enterprise setups.
Runnable example: samples/CoinGecko.Api.Samples.McpAgent.
| Plan | Base URL | Endpoints available |
|---|---|---|
| Demo (free) | api.coingecko.com/api/v3/ |
Public + Demo-tier endpoints (~70% of the surface) |
| Basic / Analyst / Lite / Pro / Pro+ / Enterprise | pro-api.coingecko.com/api/v3/ |
All documented endpoints; higher-tier methods gated by [RequiresPlan] |
Configure via CoinGeckoOptions.Plan; the library picks the correct base URL automatically. Override with CoinGeckoOptions.BaseAddress if you proxy through your own infrastructure.
CoinGecko.Api |
.WebSockets |
.AiAgentHub |
.AiAgentHub.Mcp |
|
|---|---|---|---|---|
| .NET 8.0 (LTS) | ✅ | ✅ | ✅ | ✅ |
| .NET 9.0 | ✅ | ✅ | ✅ | ✅ |
| NativeAOT | ✅ | ✅ | ||
| Trimming | ✅ | ✅ | ||
| .NET Framework | ❌ | ❌ | ❌ | ❌ |
AIFunctionFactory / MCP SDK uses reflection. A source-generated AOT-safe alternative is planned for the v0.2 line of the agent packages.
Five runnable examples under samples/:
CoinGecko.Api.Samples.Console— minimal REST quickstart: ping,GetMarketsAsync,EnumerateMarketsAsync.CoinGecko.Api.Samples.Blazor— Blazor WebAssembly withPublishTrimmed=true,TrimMode=full— the AOT / trim-safety proof.CoinGecko.Api.Samples.StreamConsole— WebSocket streaming printing live BTC/ETH prices.CoinGecko.Api.Samples.AgentDemo— MEAI tool discovery + one canned invocation (no LLM key required).CoinGecko.Api.Samples.McpAgent— Connects to CoinGecko's hosted MCP, prints the fetched tool catalog.
# Restore + build + test everything
dotnet restore
dotnet build -c Release
dotnet test -c Release
# Pack a single package locally
dotnet pack src/CoinGecko.Api/CoinGecko.Api.csproj -c Release -o artifacts
# Run any sample (env var provides the key)
COINGECKO_API_KEY=your-demo-key dotnet run --project samples/CoinGecko.Api.Samples.Consolesrc/ Four NuGet-packable projects (one per published package)
tests/ xUnit v3 unit + WireMock mock + Kestrel-hosted WS integration tests
samples/ Five runnable samples
docs/ API research + design spec + per-package implementation plans
.github/workflows/ CI + release + CodeQL + Dependabot
eng/ Shared build assets (icon)
ci.yml— matrix build + test on ubuntu / windows / macos × (net8.0, net9.0) on every push and PR. Format check (dotnet format --verify-no-changes) on Ubuntu.codeql.yml— weekly + PR-gated C# security scan.dependabot.yml— weekly NuGet + GitHub Actions dep updates.release.yml— tag-triggered NuGet publish, one tag per package:
| Tag pattern | Publishes |
|---|---|
v{semver}-api |
CoinGecko.Api |
v{semver}-preview-websockets |
CoinGecko.Api.WebSockets |
v{semver}-preview-aiagenthub |
CoinGecko.Api.AiAgentHub |
v{semver}-preview-mcp |
CoinGecko.Api.AiAgentHub.Mcp |
Each release runs under the GitHub Production environment and requires the NUGET_API_KEY secret configured there. Stable -api releases are published as regular releases; any -preview-* tag is automatically marked as a GitHub prerelease.
Bugs, missing endpoints, or protocol drift from upstream CoinGecko — please open an issue or PR. For new endpoints, follow the canonical sub-client pattern established in src/CoinGecko.Api/Resources/PingClient.cs.
Pre-PR checklist:
dotnet format --verify-no-changesdotnet build -c Release— 0 warnings, 0 errorsdotnet test -c Release— all green- New public API surface added to
src/CoinGecko.Api/PublicAPI.Unshipped.txt
MIT © msanlisavas
If this library saves you time, a tip in USDT (TRC20, on Tron) is appreciated:
TGgenJpoPvTFsd61hCUquyM4yQ9mAmD9hc