Freshness and new tokens
Every answer comes at once, from what Blocktronics has stored for the token. When that is more than 15 minutes old, the token's refresh starts behind your answer, and a pull a few minutes later has the newest hours. A token Blocktronics hasn't indexed is ingested first
How fresh an answer is
A pull never waits for a refresh. It is charged 2 credits for the stored data it gets, and the refresh it starts is free. Every tool's data carries data_freshness (the last settled UTC day it covers) and, for a pull, reads: reads.refreshing is true when the token's refresh started behind this answer. The text then ends with a line you can show:
This pull used 2 credits. Blocktronics is bringing this token up to the hour now; a pull in a few minutes has the newest hours.Asking again sooner doesn't make the refresh finish faster, and a second pull is charged like the first. Wait a few minutes when you need the newest hours
How long it takes
- Every indexed token: answered at once, from what is stored
- A token a few hours behind: its refresh takes seconds behind the answer. Only the missing hours are read; history already held is never read again
- A busy token after a quiet spell: its refresh can take minutes. Ask again later for the newest hours;
data_freshnesssays what the answer covers - Grading: the last stage of every build scores the structure of the token (an A to E badge, served by
token_overviewasgrade). If one of the inputs is still building when the badge is scored, the token is regraded automatically once it lands, so a later answer can carry more measured pillars than the first
Ingesting a new token
A tool asked about a token Blocktronics hasn't indexed answers 400 tool_error, with nothing charged. Ingesting builds the token from its first transfer. It is quoted by the token's age; the quote is taken from your account when the build starts, and given back in full if it fails. A young token builds in a few minutes; a large one can take an hour or more. Wrapped natives, stablecoins, wrapped stocks and similar reference assets aren't supported: their ingest answers 422 unsupported_token, with nothing charged. See what an ingest costs.
{
"tool": "token_series",
"ok": false,
"code": "tool_error",
"error": "\"base:0x…\" is not in the Blocktronics cache. Use search_tokens to find a cached token.",
"charged": 0,
"data": null
}/v1/tokens/{chain}/{token}/ingest· The quote, free{
"chain": "base",
"token": "0x…",
"covered": false,
"credits": 20,
"basis": {
"transfers": 1840,
"swaps": 960,
"wallets": 412,
"days": 9,
"ageDays": 9.4,
"from": "history"
},
"valid_until": 1792756800000
}covered is true when Blocktronics already has the token. valid_until is when the quote ends, in milliseconds since 1970. basis is what it was measured on
/v1/tokens/{chain}/{token}/ingest· Build itThe body is optional JSON: {"expect": 20}. The credits you were quoted. If the quote has risen above it, nothing starts: 409 price with the new figure, nothing charged. Leave it out to take the current quote.
202 · buildingThe build is running, its quote already taken from your balance (charged shows it). A young token takes a few minutes, a large one an hour or more. POST again after retry_after seconds to follow it: free, and it joins the same build.200 · readyBuilt. The quote taken when it started is the whole charge, once. The tools and the query serve the token from now on.200 · indexedBlocktronics already has it. Nothing charged; ask the tools for it.200 · failedIt couldn't be built. The credits it took are given back (refunded shows them). too_large is true when the token is past what a build takes.A balance that can't cover the quote answers 402 insufficient; a quote that rose above expect answers 409 price with the new figure. Neither charges anything
Ingesting, end to end
const base = "https://api.blocktronics.xyz/v1/tokens/base/0x…";
const headers = { Authorization: `Bearer ${process.env.BLOCKTRONICS_KEY}` };
const sleep = (s: number) => new Promise((r) => setTimeout(r, s * 1000));
// 1. The quote. Free; nothing runs.
const quote = await (await fetch(`${base}/ingest`, { headers })).json();
if (!quote.covered) {
// 2. Build it at that price. POST again while it says "building": free, it joins the same build.
for (;;) {
const res = await fetch(`${base}/ingest`, {
method: "POST",
headers: { ...headers, "content-type": "application/json" },
body: JSON.stringify({ expect: quote.credits }),
});
const body = await res.json();
if (res.status === 202) {
await sleep(body.retry_after ?? 10);
continue;
}
if (!res.ok) throw new Error(`${body.code}: ${body.error}`); // 402, 409 price: nothing charged
if (body.state === "failed") throw new Error(body.error); // the credits it took were given back
break; // "ready" (charged the quote, once) or "indexed" (already there)
}
}
// 3. Ask for it like any other token.
const data = await (
await fetch("https://api.blocktronics.xyz/v1/tools/token_series", {
method: "POST",
headers: { ...headers, "content-type": "application/json" },
body: JSON.stringify({ token: "base:0x…", metrics: ["holders", "netflow_usd"] }),
})
).json();What waiting costs
Nothing. The refresh behind an answer, a quote, a following POST while a build runs and a failed build are all free; every answer says what it charged. Full rules in credits and billing.