Pagination and Views
Bookmark pagination and the views object in BitBadges API responses, with the SDK helpers that page through them.
Paginated routes return a bookmark and hasMore. Pass the bookmark back to get the next page. Some routes (accounts, collections) group several paginated lists into one views object.
See the API reference for every route's request and response schema.
The TypeScript snippets use the configured BitBadgesApi client from the API setup example.
Example
# First page: empty bookmark. Create an API key at https://bitbadges.io/developer
curl -X POST https://api.bitbadges.io/api/v0/collections \
-H "Content-Type: application/json" -H "x-api-key: $BITBADGES_API_KEY" \
-d '{ "collectionsToFetch": [ { "collectionId": "1",
"viewsToFetch": [ { "viewType": "owners", "viewId": "owners", "bookmark": "" } ] } ] }'const res = await BitBadgesApi.getCollections({
collectionsToFetch: [
{
collectionId: '1',
viewsToFetch: [{ viewType: 'owners', viewId: 'owners', bookmark: '' }]
}
]
});
const collection = res.collections[0];
if (!collection) throw new Error('Collection not found');
const page1 = collection.getOwnersView('owners');
// Next page
await collection.fetchNextForView(BitBadgesApi, 'owners', 'owners');
const page2 = collection.getOwnersView('owners');How Bookmark Pagination Works
- First request: send an empty bookmark (
""). - Each response includes the data, a
bookmarkfor the next page, and ahasMoreboolean. - Next request: send the
bookmarkfrom the previous response. - Stop when
hasMoreisfalse.
The views and owners parts of the collection response above, for a page with two owners (synthesized from the SDK types; the rest of the collection document is omitted here):
{
"views": {
"owners": {
"ids": ["1:bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d", "1:bb1py4mfpg6uf59qkyzg0nmau322c5873eeysp5ue"],
"type": "owners",
"pagination": { "bookmark": "eyJza2lwIjoyNX0", "hasMore": true }
}
},
"owners": [
{
"_docId": "1:bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d",
"collectionId": "1",
"bitbadgesAddress": "bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d",
"balances": [
{
"amount": "1",
"tokenIds": [{ "start": "1", "end": "50" }],
"ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }]
}
],
"incomingApprovals": [],
"outgoingApprovals": [],
"userPermissions": {
"canUpdateOutgoingApprovals": [],
"canUpdateIncomingApprovals": [],
"canUpdateAutoApproveSelfInitiatedOutgoingTransfers": [],
"canUpdateAutoApproveSelfInitiatedIncomingTransfers": [],
"canUpdateAutoApproveAllIncomingTransfers": []
},
"autoApproveSelfInitiatedOutgoingTransfers": true,
"autoApproveSelfInitiatedIncomingTransfers": true,
"autoApproveAllIncomingTransfers": false,
"updateHistory": []
},
{
"_docId": "1:bb1py4mfpg6uf59qkyzg0nmau322c5873eeysp5ue",
"collectionId": "1",
"bitbadgesAddress": "bb1py4mfpg6uf59qkyzg0nmau322c5873eeysp5ue",
"balances": [
{
"amount": "1",
"tokenIds": [{ "start": "51", "end": "51" }],
"ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }]
}
],
"incomingApprovals": [],
"outgoingApprovals": [],
"userPermissions": {
"canUpdateOutgoingApprovals": [],
"canUpdateIncomingApprovals": [],
"canUpdateAutoApproveSelfInitiatedOutgoingTransfers": [],
"canUpdateAutoApproveSelfInitiatedIncomingTransfers": [],
"canUpdateAutoApproveAllIncomingTransfers": []
},
"autoApproveSelfInitiatedOutgoingTransfers": true,
"autoApproveSelfInitiatedIncomingTransfers": true,
"autoApproveAllIncomingTransfers": false,
"updateHistory": []
}
]
}Routes that take a bookmark directly (search, claim attempts, plugin errors) use the same rule without the views wrapper. Check the API reference for each route.
The Views Object
The views object is planned for deprecation in favor of dedicated per-view routes. Prefer those routes when one exists.
Collections and accounts carry a views map keyed by viewId:
type Views = {
[viewId: string]: {
ids: string[]; // document IDs in this page
type: string; // the view type
pagination: {
bookmark: string;
hasMore: boolean;
};
} | undefined;
}| Field | Description |
|---|---|
viewId | Your identifier for the view. Reuse the same viewId when paging the same dataset. |
ids | _docId values. Map them to the full documents in the matching response array. |
type | The view type, for example owners. |
pagination | bookmark and hasMore for the next request. |
Documents live in the response array for their type (activity, owners, and so on). Map ids to documents by _docId:
const activity = collection.views['activity']?.ids.map(
(id) => collection.activity.find((entry) => entry._docId === id)
) ?? [];View Types
| Interface | viewType values (CollectionViewKey / AccountViewKey) |
|---|---|
| Collection | transferActivity, owners, amountTrackers, challengeTrackers, listings, tokenFloorPrices |
| Account | siwbbRequests, transferActivity, tokensCollected, createdTokens, managingTokens, publicClaimActivity, allClaimActivity, pointsActivity |
SDK Helpers
BitBadgesCollection and BitBadgesUserInfo wrap the bookkeeping:
collection.viewHasMore('owners'); // boolean, true when unknown
collection.getViewPagination('owners'); // { bookmark, hasMore }
collection.getViewBookmark('owners'); // string
await collection.fetchNextForView(BitBadgesApi, 'owners', 'owners');
await collection.fetchAllForView(BitBadgesApi, 'owners', 'owners'); // all pages, 1 s between pages
collection.getView('owners', 'owners'); // typed by viewType
collection.getOwnersView('owners');
collection.getActivityView('activity');
collection.getChallengeTrackersView('challengeTrackers');fetchNextForView accepts optional oldestFirst and address arguments for views that support them.
Rules of Thumb
- Keep
viewIdstable while paging one dataset. - Check for an undefined view before reading it.
- Track both
bookmarkandhasMore. - Each response only contains that request's page. Merge pages yourself or use the helpers, which merge into the object.