Documentation Index Fetch the complete documentation index at: https://buttercms.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
ButterCMS offers two pagination approaches, each suited to different use cases. These methods are mutually exclusive - use one or the other, not both.
Page-based pagination is the most common approach and is ideal for traditional “page 1, page 2, page 3” navigation patterns.
Parameters:
page - Page number (default: 1, minimum: 1)
page_size - Number of items per page (default: 10, minimum: 1, maximum: 100)
Example Request:
# Fetch page 2 with 15 items per page
curl "https://api.buttercms.com/v2/posts/?page=2&page_size=15&auth_token=YOUR_TOKEN"
Response Structure:
{
"meta" : {
"count" : 45 ,
"next_page" : 3 ,
"previous_page" : 1
},
"data" : [
// Array of 15 blog posts
]
}
React Implementation:
import { useState , useEffect } from 'react' ;
function BlogList () {
const [ posts , setPosts ] = useState ([]);
const [ page , setPage ] = useState ( 1 );
const [ meta , setMeta ] = useState ({});
const pageSize = 10 ;
useEffect (() => {
async function fetchPosts () {
const response = await fetch (
`https://api.buttercms.com/v2/posts/?page= ${ page } &page_size= ${ pageSize } &exclude_body=true&auth_token= ${ API_TOKEN } `
);
const data = await response . json ();
setPosts ( data . data );
setMeta ( data . meta );
}
fetchPosts ();
}, [ page ]);
return (
< div >
{ posts . map ( post => (
< article key = { post . slug } >
< h2 > { post . title } </ h2 >
< p > { post . summary } </ p >
</ article >
)) }
< nav >
< button
disabled = { ! meta . previous_page }
onClick = { () => setPage ( meta . previous_page ) }
>
Previous
</ button >
< span > Page { page } of { Math . ceil ( meta . count / pageSize ) } </ span >
< button
disabled = { ! meta . next_page }
onClick = { () => setPage ( meta . next_page ) }
>
Next
</ button >
</ nav >
</ div >
);
}
Offset-based pagination is ideal for infinite scroll implementations and more flexible navigation patterns.
Parameters:
limit - Maximum number of items to return (default: 10, minimum: 1, maximum: 100)
offset - Number of items to skip before returning results (default: 0, minimum: 0)
Example Request:
# Skip first 20 items, fetch next 10
curl "https://api.buttercms.com/v2/posts/?limit=10&offset=20&auth_token=YOUR_TOKEN"
Response Structure:
{
"meta" : {
"count" : 45 ,
"next_offset" : 30 ,
"previous_offset" : 10
},
"data" : [
// Array of 10 blog posts
]
}
Infinite Scroll Implementation:
import { useState , useEffect , useRef , useCallback } from 'react' ;
function InfinitePostsList () {
const [ posts , setPosts ] = useState ([]);
const [ offset , setOffset ] = useState ( 0 );
const [ hasMore , setHasMore ] = useState ( true );
const [ loading , setLoading ] = useState ( false );
const limit = 10 ;
const observer = useRef ();
const lastPostRef = useCallback ( node => {
if ( loading ) return ;
if ( observer . current ) observer . current . disconnect ();
observer . current = new IntersectionObserver ( entries => {
if ( entries [ 0 ]. isIntersecting && hasMore ) {
setOffset ( prev => prev + limit );
}
});
if ( node ) observer . current . observe ( node );
}, [ loading , hasMore ]);
useEffect (() => {
async function fetchMore () {
setLoading ( true );
const response = await fetch (
`https://api.buttercms.com/v2/posts/?limit= ${ limit } &offset= ${ offset } &exclude_body=true&auth_token= ${ API_TOKEN } `
);
const data = await response . json ();
setPosts ( prev => [ ... prev , ... data . data ]);
setHasMore ( data . meta . next_offset !== null );
setLoading ( false );
}
fetchMore ();
}, [ offset ]);
return (
< div >
{ posts . map (( post , index ) => (
< article
key = { post . slug }
ref = { index === posts . length - 1 ? lastPostRef : null }
>
< h2 > { post . title } </ h2 >
< p > { post . summary } </ p >
</ article >
)) }
{ loading && < p > Loading more posts... </ p > }
</ div >
);
}
Use Case Recommended Method Reason Traditional page navigation Page-based Intuitive “Page X of Y” display Infinite scroll Offset-based Easy to append new items Load more button Offset-based Simple offset increment SEO-friendly archives Page-based Predictable, crawlable URLs Random access to pages Page-based Direct page number access
Filtering paginated results
To filter by author, category, tags, or custom field values, see Filtering Requests .
Combining pagination and filtering
// JavaScript SDK example
const butter = Butter ( 'YOUR_API_TOKEN' );
// Fetch page 1 of featured tutorials by a specific author
const response = await butter . post . list ({
page: 1 ,
page_size: 10 ,
author_slug: 'john-doe' ,
category_slug: 'tutorials' ,
exclude_body: true
});
console . log ( `Found ${ response . data . meta . count } posts` );
console . log ( `Showing page 1 of ${ Math . ceil ( response . data . meta . count / 10 ) } ` );
Choose appropriate page sizes
10-25 items is optimal for most use cases
Larger page sizes increase response time and payload
Smaller page sizes (under 10) may require excessive requests for large content sets
Consider your content type: text-heavy items warrant smaller pages
Always use exclude_body for listings
For blog post listings, always include exclude_body=true: # Good - 50-70% smaller response
/v2/posts/?page =1 & page_size = 10 & exclude_body = true
# Avoid for listings - unnecessarily large response
/v2/posts/?page =1 & page_size = 10
See Query Optimization for more on exclude_body and levels.
Implement client-side or server-side caching for paginated results: // Cache key includes all filter and pagination parameters
const cacheKey = `posts-page ${ page } -size ${ pageSize } -cat ${ category } ` ;
Building archive pages
A common use case is building blog archive pages with category and date filtering:
// Next.js archive page example
export async function getServerSideProps ({ query }) {
const butter = Butter ( process . env . BUTTER_API_TOKEN );
const page = parseInt ( query . page ) || 1 ;
const pageSize = 12 ;
const category = query . category || null ;
const params = {
page ,
page_size: pageSize ,
exclude_body: true ,
... ( category && { category_slug: category })
};
const [ posts , categories ] = await Promise . all ([
butter . post . list ( params ),
butter . category . list ()
]);
return {
props: {
posts: posts . data . data ,
meta: posts . data . meta ,
categories: categories . data . data ,
currentPage: page ,
currentCategory: category
}
};
}