DEV Community

Cover image for Stop Hand-Writing DefiLlama URLs: Meet defillama-go, a Go SDK for the Full API Surface
Igor
Igor

Posted on

Stop Hand-Writing DefiLlama URLs: Meet defillama-go, a Go SDK for the Full API Surface

If you are building a Go service around DeFi data, the first prototype is usually straightforward: make an HTTP request, decode JSON, and move on. The friction arrives later. One endpoint lives on a different host. A token identifier needs path escaping. A rate limit needs a sensible response. A Pro key must not leak into application logs. An upstream API adds a field that your struct did not expect.

That is the gap defillama-go is designed to close. It is a small, idiomatic Go client for DefiLlama’s Free and Pro APIs that gives applications one context-aware interface for protocol TVL, token prices, stablecoins, yields, volumes, fees, bridges, real-world assets, equities, and more. Rather than presenting a thin collection of handwritten URLs, the library turns the API’s documented GET surface into discoverable Go services and methods. 1

The headline is substantial but carefully scoped: the project maps all 132 GET operations in its pinned DefiLlama OpenAPI snapshot—31 Free operations and 101 Pro operations—across 21 services. The route registry, generated service methods, and API index are kept in step by contract tests. That does not mean an SDK can eliminate the need to understand data semantics, but it does remove a surprising amount of transport plumbing from a production Go codebase. 1

The problem is not simply “make a request”

DefiLlama’s API is broad by design. Its documentation spans TVL, coins, stablecoins, yields, DEX and derivatives volumes, fees and revenue, bridges, protocol metrics, equities, pre-IPO data, and RWA endpoints. Some Free endpoints are served from api.llama.fi; others use dedicated origins such as coins.llama.fi, stablecoins.llama.fi, or yields.llama.fi. Pro routes use pro-api.llama.fi. 4

When that variety is handled ad hoc, every consumer makes its own decisions about URL construction, timeouts, parameter validation, error interpretation, retries, and authentication. Those choices are easy to get almost right and difficult to keep consistent. A client library earns its place when it centralizes those decisions without forcing a heavy framework on the rest of the application.

defillama-go takes that approach. The package has no runtime dependencies outside Go’s standard library, uses the API’s per-operation origin rather than one assumed base URL, and lets callers inject their own *http.Client when they need a proxy, tracing, custom TLS, or a shared timeout policy. The result is deliberately ordinary Go: construct a client, pass a context.Context, call a service method, and handle a typed result or error. 1

Start with useful Free data

The shortest path into the SDK requires no API key. Install it into a Go 1.22+ module:

go get github.com/tigusigalpa/defillama-go
Enter fullscreen mode Exit fullscreen mode

Then create a client and ask for protocol data and current prices. A request-scoped deadline is included because it is a good default for any network-facing service.

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    defillama "github.com/tigusigalpa/defillama-go"
 )

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
    defer cancel()

    client, err := defillama.New(defillama.WithTimeout(10 * time.Second))
    if err != nil {
        log.Fatal(err)
    }

    protocols, err := client.TVL().GetProtocols(ctx)
    if err != nil {
        log.Fatal(err)
    }
    if len(protocols) > 0 {
        fmt.Printf("%s TVL: $%.0f\n", protocols[0].Name, protocols[0].TVL)
    }

    prices, err := client.Prices().GetCurrentPrices(ctx, []string{
        "coingecko:bitcoin",
        "ethereum:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    })
    if err != nil {
        log.Fatal(err)
    }

    if btc, ok := prices["coingecko:bitcoin"]; ok {
        fmt.Printf("BTC: $%.2f\n", btc.Price)
    }
}
Enter fullscreen mode Exit fullscreen mode

This example illustrates the API shape that carries through the package. Client exposes data domains such as TVL(), Prices(), Stablecoins(), Yields(), Volumes(), and Fees(). Methods accept context.Context first. The client constructor does not make a network request, so invalid options fail early and constructing a shared client does not produce unexpected I/O. 1

For a Free endpoint, callers do not need to remember which DefiLlama host owns the operation. The route metadata does that work. This is a small convenience in a sample program and a meaningful reduction in copy-paste risk once a product needs price lookups, historical charts, pool data, and protocol summaries from the same service.

Broad coverage without a flat, unwieldy API

A full endpoint count is useful only if the interface remains navigable. defillama-go groups its 132 mapped operations into 21 client-owned services. The following are representative starting points rather than a replacement for the full endpoint index. 1

Need Service accessor Example method
Protocols, chains, TVL, and TVL charts TVL() GetProtocols(ctx)
Current and historical coin prices Prices() GetCurrentPrices(ctx, coins)
Supply and chart data for stablecoins Stablecoins() GetStablecoins(ctx, opts)
Free pools and Pro earn/borrow data Yields() GetPools(ctx)
DEX, options, and derivatives activity Volumes() GetDEXOverview(ctx, opts)
Fees and revenue summaries Fees() GetOverview(ctx, opts)
Bridges and transaction data Bridges() GetBridgeTransactions(ctx, id, opts)
ETFs, DAT, equities, pre-IPO, and RWA ETFs(), DAT(), Equities(), PreIPO(), RWA() GetSnapshot(ctx), GetInstitutions(ctx)

The package documentation also covers treasury, oracle, fork, narrative, emissions, ecosystem, dimensions, financial-statement, and account-usage services. Each API operation has a corresponding entry in the project’s generated index, with the Go method and a link to the relevant official DefiLlama documentation. That index is especially helpful when moving from an API reference page to an implementation task. 2

Typed models where the API is stable, raw payloads where it is not

There is an important design choice behind a data client: pretending that every response is permanently stable can be as harmful as returning map[string]any everywhere. defillama-go uses typed models for response shapes that are stable enough to benefit from them, including Protocol, Chain, CoinPrice, Stablecoin, and YieldPool. The package also preserves the complete raw payload on its stable typed models. 1

For endpoints whose responses are intentionally evolving, the library makes that flexibility explicit with clearly named map[string]any or []any returns. For example, the Pro earn-pool query is exposed as a query-oriented result rather than a falsely rigid struct. That trade-off makes an upgrade less urgent when an upstream API adds a field, while still making the common, durable response shapes pleasant to use.

The same restraint applies to numerical values. The client decodes API-supplied numbers as float64 transport values. That is appropriate for moving JSON across the boundary, but it is not a recommendation to use binary floating point for every monetary calculation. If an application needs exact rounding or auditable financial arithmetic, convert prices, TVL, and percentages to a decimal representation at the application boundary. DefiLlama’s data and metrics also follow DefiLlama’s own methodology; the SDK is a transport layer, not financial advice. 1

Options make optional parameters visible

Query-string work is where otherwise clean integrations often become opaque. This package uses option structs so that optional values are declared in Go rather than assembled as strings. Pointer fields are omitted when they are nil, and the SDK validates declared enum values and required parameters before sending a request where applicable. 1

Here is a concise example that asks for stablecoin pools on Ethereum with a minimum TVL threshold:

result, err := client.Yields().QueryEarnPools(ctx, defillama.EarnPoolsQuery{
    Chain:      defillama.Ptr("Ethereum"),
    Stablecoin: defillama.Ptr(true),
    MinTVL:     defillama.Ptr(1_000_000.0),
    Page:       defillama.Ptr(int64(1)),
    Limit:      defillama.Ptr(int64(50)),
})
if err != nil {
    return err
}

fmt.Printf("received %d top-level fields\n", len(result))
Enter fullscreen mode Exit fullscreen mode

This does not make every business rule automatic, nor should it. It does provide a reviewable declaration of what the request means. It also avoids serializing optional fields just because their Go zero value happens to be present.

Production behavior is part of the API

A library’s happy path is only half its value. In a service that calls remote data providers, cancellation, rate limits, transient failures, and actionable diagnostics should be designed rather than improvised.

Every network method in defillama-go accepts a context, and the constructed client is safe to share between goroutines. Retries are off by default, which is an appropriately conservative choice for a library. When enabled with WithRetryPolicy, retries are restricted to GET requests and retryable conditions: transient transport failures, HTTP 429 responses, and HTTP 5xx responses. The policy uses capped backoff, can add jitter, honors Retry-After when it is longer, and stops when the context is cancelled or reaches its deadline. Ordinary 4xx responses and JSON decode errors are not retried. 1

The error model is similarly specific. Applications can use errors.As to distinguish a missing resource, a rate limit, another API response, a transport failure, a malformed successful response, or an attempted Pro call without a key.

_, err := client.TVL().GetProtocol(ctx, "not-a-real-protocol")

var (
    notFound  *defillama.NotFoundError
    rateLimit *defillama.RateLimitError
    apiErr    *defillama.APIError
    transport *defillama.TransportError
)

switch {
case errors.As(err, &notFound):
    // HTTP 404: the requested protocol was not found.
case errors.As(err, &rateLimit):
    // HTTP 429: rateLimit.RetryAfter may be non-zero.
case errors.As(err, &apiErr):
    // Another non-2xx status with a redacted URL and response diagnostics.
case errors.As(err, &transport):
    // DNS, TLS, timeout, or cancellation. errors.Is reaches the cause.
case err != nil:
    return err
}
Enter fullscreen mode Exit fullscreen mode

This error taxonomy matters in operations. A 404 may be a valid absence. A 429 may justify queueing or delaying work. A timeout might point to an overloaded dependency or an overly aggressive deadline. Treating all three as the same error string makes monitoring and recovery much harder than they need to be.

Pro API support that treats the key as sensitive

DefiLlama Pro authenticates requests with the API key in the URL path. That detail deserves care because URLs often surface in logs, traces, proxy diagnostics, and wrapped network errors. The official documentation identifies pro-api.llama.fi as the Pro request origin, and the SDK places the key in one escaped path segment as required by that convention. 4

The library’s design goes beyond merely avoiding a custom header. Calling a Pro method without WithAPIKey returns *ProAPIKeyRequiredError before the SDK builds or sends a request. When a key is configured, the implementation redacts it from URLs, response diagnostics, headers, bodies, and nested transport errors. It also refuses to follow a redirect to a different origin for a Pro request, preventing the path-embedded credential from being forwarded elsewhere. Same-origin redirects can still work. 1

Creating a Pro client remains a normal Go configuration step:

client, err := defillama.New(
    defillama.WithAPIKey(os.Getenv("DEFILLAMA_API_KEY")),
    defillama.WithTimeout(10*time.Second),
    defillama.WithRetryPolicy(defillama.RetryPolicy{
        MaxAttempts: 3,
        BaseDelay:  250 * time.Millisecond,
        MaxDelay:   2 * time.Second,
        Jitter:     true,
    }),
)
if err != nil {
    return err
}

chart, err := client.TVL().GetProtocolTVLChart(ctx, "aave", nil)
Enter fullscreen mode Exit fullscreen mode

There is also an explicit WithPreferProForFree(true) option. If a Pro key is available, it routes the 31 Free operations that have an official Pro mapping through their Pro equivalents. The important word is explicit: configuring a key alone does not silently change the default routing behavior. 1

What to expect from a young library

The project’s changelog identifies v0.1.0 as the initial release, so the right way to evaluate it is not as a decades-old dependency with an enormous ecosystem. Instead, look at whether its scope is honest and whether its maintenance mechanics are visible. The repository includes an MIT license, contribution guidance, a security policy, GitHub Actions for CI, tests, CodeQL, coverage, and releases. Its CI matrix runs formatting checks, go vet, race-enabled tests, coverage, and builds against Go 1.22 and the stable Go release. 1 5

The endpoint coverage is tied to a pinned OpenAPI snapshot. That is good for repeatability, but it also means newly added upstream operations require the SDK to update its snapshot and regenerate the affected route and documentation artifacts. The package currently targets GET operations, so it should not be mistaken for a generalized client for hypothetical future write endpoints. Finally, data fields returned as floats deserve deliberate conversion in code paths where precision matters. These are useful boundaries to understand before adopting any financial-data transport library. 1

A practical starting point

For a dashboard, analytics service, alerting job, portfolio research tool, or backend that needs DefiLlama data in Go, the next step is intentionally small:

git clone https://github.com/tigusigalpa/defillama-go.git
cd defillama-go
go run ./examples/basic
Enter fullscreen mode Exit fullscreen mode

The repository’s basic example calls Free endpoints; the separate Pro example demonstrates environment-based key loading, retry configuration, rate-limit handling, usage retrieval, and a Pro TVL chart request. From there, browse the endpoint index, choose the service that matches the data product you are building, and keep application-specific calculations and validation on your side of the boundary. 1

defillama-go is appealing not because it hides DefiLlama behind magic, but because it makes the integration boring in the best sense of the word. It centralizes API routing, context propagation, parameter encoding, error handling, retries, and key redaction while leaving Go applications in control of their HTTP client, timeouts, precision policy, and domain decisions. For teams that would rather spend time using DeFi data than maintaining endpoint glue, that is a solid foundation.

References

Top comments (0)