Pagination
All list endpoints return paginated results using cursor-based pagination. This approach is faster and more reliable than offset-based pagination, especially when data changes between requests.
How It Works
┌──────────────────────────────────────────────────────────────────┐
│ GET /tokens?limit=3 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ T1 │ │ T2 │ │ T3 │ │ T4 │ │ T5 │ │ T6 │ │ T7 │ │
│ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ │
│ ╰──── Page 1 ────╯ ╰──── Page 2 ────╯ ╰─ Page 3 ─╯ │
│ starting_after=T3 → starting_after=T6 → │
└──────────────────────────────────────────────────────────────────┘
Each response includes a has_more flag. When true, use the last item's id as the starting_after cursor to fetch the next page.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Items per page (1–100) |
starting_after | string | — | Cursor: ID of the last item from the previous page |
Response Format
{
"data": [
{ "id": "tok_abc123", "name": "Gold", "symbol": "GLD", ... },
{ "id": "tok_def456", "name": "Silver", "symbol": "SLV", ... },
{ "id": "tok_ghi789", "name": "Bronze", "symbol": "BRZ", ... }
],
"has_more": true,
"total": 42
}
| Field | Type | Description |
|---|---|---|
data | array | Page of results |
has_more | boolean | true if more pages exist |
total | integer | Total count of matching items |
SDK Example — Full Pagination
async function getAllTokens() {
const allTokens = [];
let cursor: string | undefined;
do {
const page = await urblock.tokens.list({
limit: 100,
starting_after: cursor,
});
allTokens.push(...page.data);
cursor = page.has_more ? page.data[page.data.length - 1].id : undefined;
} while (cursor);
return allTokens; // All tokens across all pages
}
const tokens = await getAllTokens();
console.log(`Fetched ${tokens.length} tokens`);
curl Example
# Page 1
curl -s https://api.urblock.io/tokens?limit=3 \
-H "Authorization: Bearer $TOKEN" | jq
# Page 2 — use last ID as cursor
curl -s "https://api.urblock.io/tokens?limit=3&starting_after=tok_ghi789" \
-H "Authorization: Bearer $TOKEN" | jq
Why Cursor-Based?
| Feature | Cursor-Based | Offset-Based |
|---|---|---|
| Consistency when data changes | ✅ Stable | ❌ Can skip/duplicate |
| Performance on large datasets | ✅ O(1) seek | ❌ O(n) skip |
| Deep pagination (page 1000+) | ✅ Fast | ❌ Slow |
| Random page access | ❌ Sequential only | ✅ Any page |
Cursor-based pagination guarantees you will never miss or duplicate items, even if new items are created between requests.
Cursor Stability
Cursors are item IDs, not opaque tokens. They remain valid as long as the referenced item exists. If the item is deleted, the API will return results from the next available item.
When filtering (e.g., ?status=confirmed), the cursor still works correctly — the API skips to the next matching item after the cursor.
Best Practices
- Use
limit: 100for bulk exports — minimizes round trips - Use
limit: 20for UI pages — faster response, better UX - Always check
has_more— don't assume page count fromtotal - Store cursors, not page numbers — enables resume after failures
- Handle empty pages —
data: []withhas_more: falsemeans you're done
Next Steps
- Rate Limiting — understand request quotas
- Async Operations — how operations are processed
- Tokens API — list tokens with pagination
- Transactions API — list transactions with pagination