auradefi 0.1.2
PyPI GitHub

Limits and cost

This package issues one request per thing it needs, and never retries. Two numbers therefore decide what it costs you: how many requests an operation makes, and what the service at the other end allows.

Both are knowable in advance, so they are on this page instead of in your first bill.

What each call costs

Call Requests it makes Under budget?
Anything in Sandbox zero. The recording is in the wheel and no socket opens n/a
sync(budget=n) at most n history pages, shared across every connection in that tick yes, n is the cap
holdings(), scalar_metrics() per connection: one native balance, then one tokentx page per page of that address' token-transfer history, then one per distinct token contract found. Plus one DefiLlama request covering the whole set no
Bitcoin gap-20 scan about 40 for an empty wallet: one per derived address, over the receive and change chains, stopping after 20 consecutive unused no
Solana balances() two: getBalance and getTokenAccountsByOwner no

Two things in that table surprise people.

budget covers history pages and nothing else. It is the cap on fetch_txlist calls inside one sync(), so it bounds the backfill. It does not bound holdings().

holdings() is neither budgeted nor cached. Every call refetches every balance for every connection. A dashboard that calls it per page view pays the full cost per page view, so hold the report on your side and refresh it on your own schedule.

Nothing in the holdings path batches its reads, so one token balance is one request. An address that has touched two hundred tokens costs about two hundred requests to value, and a wide address is expensive in proportion to its width. auradefi.sources.evm.multicall.Multicall3 ships and will collapse a batch of contract reads into one eth_call, but the holdings path does not use it yet: today it is a component you wire yourself.

What the services allow

Service Published limit Does a key change it?
Etherscan V2 3 requests per second, 100,000 per day on the free tier yes. Without a key the keyless tier applies
DefiLlama none published there is no key
Blockstream Esplora none published there is no key
Solana public endpoint none published, and rate-limited aggressively in practice pass a keyed provider's full URL

What happens when you exceed one

Nothing absorbs it. No source and no oracle in this package retries, backs off or throttles, so the failure arrives at your code on the first request that crosses the line:

Service What you get
Etherscan V2 SourceError carrying the envelope, since Etherscan answers HTTP 200 with status: "0"
Solana SourceError: solana rpc HTTP 429
Esplora, DefiLlama SourceError carrying the status

Inside sync(), a SourceError is contained to the one connection that raised it: its report row is marked failed and its siblings keep their share of the budget. Outside sync(), it reaches you.

If you want backoff, own the transport. Every source takes an httpx.Client in its constructor, so a client configured with your retry policy, your timeouts and your connection limits replaces the one the library would build:

from httpx import Client, HTTPTransport

from auradefi.sources.evm.source import EtherscanSource

client = Client(transport=HTTPTransport(retries=3), timeout=20.0)
source = EtherscanSource(client, api_key="…")

How to pace yourself

Three controls, all yours.

budget=n caps the pages one tick may spend. Start small and raise it: a decade-old wallet drains over many ticks, and the cursors mean no page is fetched twice.

AURADEFI_SYNC_MIN_INTERVAL_S (default 60) is the floor between two ticks for one connection. A sync() inside that window touches no transport and comes back with no_op=True, so a host that ticks every integration on one fixed timer costs nothing extra.

The schedule itself is yours. Nothing here starts a thread, and no work happens unless you call sync().

The HTTP API's own quota

If you run the HTTP API, it meters your callers on three windows per tenant: per second, per day and per month. Every response carries nine headers, X-RateLimit-{Limit,Remaining,Reset}-{Second,Day,Month}, and a refusal is a 429 with Retry-After in whole seconds.

That quota is yours to set and has no relationship to Etherscan's. It limits your customers; Etherscan limits you.

Webhook delivery does retry

The one place this package retries at all. A delivery gets six attempts on a pinned schedule, measured from when it was created: immediately, then after 1 minute, 5 minutes, 30 minutes, 2 hours and 24 hours. After the sixth it is dead-lettered, and a dead letter can be replayed. See guide 09.