Repository navigation
Error Handling & Retries
nansen-go returns structured errors that carry everything you need to understand and react to a failure.
Every non-2xx response from the Nansen API is returned as an *nansen.APIError. The type is defined as follows:
type APIError struct {
StatusCode int
Message string
RawBody []byte
Headers http.Header
RetryAfter *time.Duration
RateLimitRemaining *int
}| Field | Description |
|---|---|
StatusCode |
The HTTP status code returned by the API. |
Message |
The decoded error message from the response body. |
RawBody |
The raw response body bytes, for cases where you need to inspect the original payload. |
Headers |
A clone of the full response headers, including rate-limit information. |
RetryAfter |
Populated when the response included a Retry-After header, expressing how long to wait before retrying. |
RateLimitRemaining |
Populated from RateLimit-Remaining or X-RateLimit-Remaining, indicating how many requests remain in the current window. |
Use errors.As to unwrap the *APIError and inspect its fields:
resp, err := client.SmartMoney.Netflow(ctx, req)
if err != nil {
var apiErr *nansen.APIError
if errors.As(err, &apiErr) {
fmt.Println(apiErr.StatusCode)
fmt.Println(apiErr.Message)
fmt.Println(apiErr.RawBody)
fmt.Println(apiErr.Headers.Get("RateLimit-Remaining"))
if apiErr.RetryAfter != nil {
fmt.Println("retry after:", *apiErr.RetryAfter)
}
if apiErr.RateLimitRemaining != nil {
fmt.Println("requests remaining:", *apiErr.RateLimitRemaining)
}
}
}For simple branching on the kind of failure, the package exposes a set of sentinel errors that work with errors.Is:
| Sentinel | HTTP Status |
|---|---|
nansen.ErrNotFound |
404 |
nansen.ErrUnauthorized |
401 |
nansen.ErrRateLimited |
429 |
nansen.ErrBadRequest |
400 |
nansen.ErrPaymentRequired |
402 |
nansen.ErrForbidden |
403 |
nansen.ErrValidation |
422 |
nansen.ErrInternal |
500–599 |
if errors.Is(err, nansen.ErrRateLimited) {
// back off and retry later
}
if errors.Is(err, nansen.ErrUnauthorized) {
// check your API key
}
if errors.Is(err, nansen.ErrNotFound) {
// the requested resource does not exist
}Retries are disabled by default. Enable them by passing WithRetry when constructing the client:
client, err := nansen.New(apiKey,
nansen.WithRetry(3, 500*time.Millisecond, 5*time.Second),
)Once enabled, the client automatically retries the following situations:
429 Too Many Requests. The client reads the Retry-After header (supporting both delay-seconds and HTTP-date formats per RFC 7231). If that header is absent, it falls back to RateLimit-Reset or X-RateLimit-Reset. If none of those are present, it uses the calculated exponential backoff. The RateLimit-Remaining and X-RateLimit-Remaining values are stashed on the returned APIError.
Transient 5xx responses. Server errors that typically clear up on a second attempt are retried using exponential backoff.
Flaky network errors. Transient network failures are retried, unless the context has already been cancelled.
The backoff delay starts at initialDelay and doubles after each failed attempt, but never exceeds maxDelay. The formula is:
delay = min(initialDelay * 2^attempt, maxDelay)
The total time budget — including all retry attempts and backoff sleep — is bounded by the deadline on your context or the WithTimeout value, whichever is smaller.