Skip to content

Error Handling & Retries

Igor Sazonov edited this page Jul 14, 2026 · 1 revision

nansen-go returns structured errors that carry everything you need to understand and react to a failure.

The APIError Type

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.

Inspecting an Error

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)
        }
    }
}

Sentinel Errors

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
}

Automatic Retries

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.

Backoff Behaviour

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.

Clone this wiki locally