Skip to content

Pagination and data handling

Use the interactive reference to inspect the response and try a single page. This guide shows how to continue through results safely.

Set partner_id to the on-chain partner object ID, not your API-key account name or wallet address. Filtering happens before sorting and pagination. A partner with no matching presales returns 200 with an empty array and a null cursor.

This server-side JavaScript example follows every page of live presales. Set BLAST_PARTNER_KEY and BLAST_PARTNER_ID in your backend environment.

const key = process.env.BLAST_PARTNER_KEY;
const partnerId = process.env.BLAST_PARTNER_ID;
if (!key || !/^0x[0-9a-f]{64}$/.test(partnerId ?? '')) {
throw new Error('Configure a partner key and canonical partner object ID.');
}
const query = new URLSearchParams({
partner_id: partnerId,
status: 'live',
sort: 'ending',
limit: '50',
});
const sales = new Map();
const visited = new Set();
for (;;) {
const response = await fetch(
`https://www.blast.fun/api/v1/presales?${query}`,
{ headers: { 'X-Blast-Partner-Key': key }, signal: AbortSignal.timeout(15000) },
);
if (!response.ok) {
// Honor Retry-After for 429/503 in your job scheduler. Do not loop here.
throw new Error(`Blast HTTP ${response.status}; retry-after=${response.headers.get('retry-after')}`);
}
const page = await response.json();
for (const sale of page.presales) sales.set(sale.presale_id, sale);
if (page.next_cursor === null) break;
if (visited.has(page.next_cursor)) throw new Error('Repeated pagination cursor.');
visited.add(page.next_cursor);
query.set('cursor', page.next_cursor);
}
console.log([...sales.values()]);

Keep status, sort, partner_id, verified_team, and audited unchanged between pages. Treat cursor tokens as opaque. Results can change as new activity is indexed; deduplicate by ID when collecting multiple pages. Paginating is not a frozen snapshot of the entire dataset.

Endpoint Read from response Send in next request Stop when
Presales next_cursor cursor Cursor is null
Discovery section.data.nextCursor cursor, same view Cursor is null or section is empty
Search Group’s section.data.nextCursor cursor, same group and q Cursor is null or section is empty
Holders next_cursor cursor, same coin and view Cursor is null; incomplete data is unavailable
Launch activity next_cursor cursor, same launch and view Cursor is null
Pool candles Oldest bucket_start to Candle array is empty
Pool trades Last row’s cursor before Trade array is empty or archive boundary is reached

Amounts ending in _atoms, market reserves, and market trade volumes are decimal strings in the coin’s smallest units. Use BigInt for integer arithmetic and a decimal library for prices. Converting large balances to JavaScript Number loses precision.

  • Social price history: prices are USD per whole token. Points are [timestamp_ms, decimal_price_string] pairs.
  • Market pool and candle prices: prices are quote atoms per meme atom. Convert to quote coins per whole token by multiplying by 10^(meme_decimals − quote_decimals) using decimal arithmetic.
  • Basis points: 10,000 means 100%. Check the field schema: some endpoints use JSON integers and others decimal strings.
  • Time: timestamps labeled _ms or Ms are Unix milliseconds. A duration is not a timestamp. Specific fields such as publish_time use Unix seconds; their schemas say so.

[] means a successful read with no matching items. null means the particular fact is unavailable or not applicable; do not silently turn it into zero. An omitted optional field is different from a required field whose value is null.

Discovery and search expose available, stale, empty, and unavailable sections. Only available and stale contain data. A search can return 200 while one group is unavailable. An incomplete holder snapshot has null facts, not zero holders.

Inspect as_of or provenance before presenting data as current. Preserve the indexed timestamp if you cache a response, and follow the returned cache policy. See rate limits and errors for retry behavior.