Pagination
Introduction
Feedframer uses cursor-based pagination for the REST API and offset-based pagination for GraphQL. This guide explains how to paginate through large datasets efficiently.
REST API Pagination (Cursor-Based)
How It Works
Cursor-based pagination uses an encoded cursor to mark your position in the result set. This provides consistent results even when data changes between requests.
Page Size
Control how many results per page (1-100, default: 12):
GET /api/v1/me?api_key=YOUR_API_KEY&page[size]=50
Navigation
The response includes pagination metadata:
{
"username": "your_username",
"name": "Your Name",
"posts": [...],
"pagination": {
"nextCursor": "eyJpZCI6MTJ9",
"prevCursor": null,
"hasMore": true,
"perPage": 12
}
}
Fetching Next Page
Use the nextCursor from the pagination object:
GET /api/v1/me?api_key=YOUR_API_KEY&page[cursor]=eyJpZCI6MTJ9
Pagination Loop Example
JavaScript:
const apiKey = 'YOUR_API_KEY';
let cursor = null;
let allPosts = [];
do {
const url = cursor
? `https://feedframer.com/api/v1/me?api_key=${apiKey}&page[cursor]=${cursor}`
: `https://feedframer.com/api/v1/me?api_key=${apiKey}&page[size]=50`;
const response = await fetch(url);
const data = await response.json();
allPosts = allPosts.concat(data.posts);
cursor = data.pagination.nextCursor;
} while (cursor !== null);
console.log(`Fetched ${allPosts.length} total posts`);
PHP:
<?php
$apiKey = 'YOUR_API_KEY';
$cursor = null;
$allPosts = [];
do {
$url = $cursor
? "https://feedframer.com/api/v1/me?api_key={$apiKey}&page[cursor]={$cursor}"
: "https://feedframer.com/api/v1/me?api_key={$apiKey}&page[size]=50";
$response = file_get_contents($url);
$data = json_decode($response, true);
$allPosts = array_merge($allPosts, $data['posts']);
$cursor = $data['pagination']['nextCursor'];
} while ($cursor !== null);
echo "Fetched " . count($allPosts) . " total posts";
Python:
import requests
api_key = 'YOUR_API_KEY'
cursor = None
all_posts = []
while True:
url = f'https://feedframer.com/api/v1/me?api_key={api_key}'
if cursor:
url += f'&page[cursor]={cursor}'
else:
url += '&page[size]=50'
response = requests.get(url)
data = response.json()
all_posts.extend(data['data'])
cursor = data['meta']['next_cursor']
if cursor is None:
break
print(f'Fetched {len(all_posts)} total posts')
GraphQL API Pagination (Offset-Based)
How It Works
GraphQL uses traditional page numbers for navigation.
Basic Pagination
query {
posts(first: 12, page: 1) {
data {
id
caption
mediaUrl
}
paginatorInfo {
count # Items on current page
currentPage # Current page number
total # Total items
lastPage # Total pages
hasMorePages # More pages available?
perPage # Items per page
}
}
}
Fetching Next Page
Increment the page argument:
query {
posts(first: 12, page: 2) {
data { id caption }
paginatorInfo { currentPage lastPage hasMorePages }
}
}
Pagination Loop Example
JavaScript:
const query = (page) => `
query {
posts(first: 50, page: ${page}) {
data {
id
caption
mediaUrl
}
paginatorInfo {
hasMorePages
}
}
}
`;
const apiKey = 'YOUR_API_KEY';
let page = 1;
let allPosts = [];
let hasMore = true;
while (hasMore) {
const response = await fetch(`https://feedframer.com/graphql?api_key=${apiKey}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: query(page) })
});
const result = await response.json();
const posts = result.data.posts;
allPosts = allPosts.concat(posts.data);
hasMore = posts.paginatorInfo.hasMorePages;
page++;
}
console.log(`Fetched ${allPosts.length} total posts`);
Best Practices
Choose Appropriate Page Size
Balance between number of requests and data per request:
# ✅ Good for most use cases
page[size]=50
# ❌ Too small - many requests
page[size]=5
# ❌ Too large - slow requests, high memory
page[size]=100
Handle Pagination Limits
Free tier accounts are limited by post count (12 posts per account). Premium accounts have no post limits but still benefit from pagination.
Cache Results
Cache paginated results to reduce API calls:
// Example with simple in-memory cache
const cache = new Map();
const cacheKey = `posts_page_${page}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const data = await fetchPosts(page);
cache.set(cacheKey, data);
return data;
Error Handling
Always handle pagination errors:
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
// Process data...
} catch (error) {
console.error('Pagination failed:', error);
// Handle error (retry, show message, etc.)
}
Pagination Metadata
REST API Meta Fields
| Field | Description |
|---|---|
per_page |
Items per page |
next_cursor |
Cursor for next page (null if no more pages) |
prev_cursor |
Cursor for previous page (null if on first page) |
GraphQL PaginatorInfo Fields
| Field | Description |
|---|---|
count |
Number of items on current page |
currentPage |
Current page number |
firstItem |
Index of first item |
lastItem |
Index of last item |
lastPage |
Total number of pages |
hasMorePages |
Whether more pages exist |
perPage |
Items per page |
total |
Total number of items across all pages |
Framework Examples
See pagination examples for specific frameworks:
Combining with Filters
Pagination works seamlessly with filters:
REST:
# Page 2 of image posts
GET /api/v1/me?api_key=YOUR_API_KEY&filter[type]=IMAGE&page[size]=50&page[cursor]=eyJpZCI6NTB9
GraphQL:
query {
posts(first: 50, page: 2, type: "IMAGE") {
data { id mediaUrl }
paginatorInfo { currentPage hasMorePages }
}
}
Rate Limiting Considerations
Each pagination request counts toward your rate limit:
- Fetching 100 posts in 2 pages (50 each) = 2 API requests
- Fetching 100 posts in 10 pages (10 each) = 10 API requests
Use larger page sizes to minimize API calls and stay within rate limits.
Next Steps
- Filtering & Sorting - Filter paginated results
- Rate Limiting - Understand rate limits
- REST API - Complete REST reference
- GraphQL API - Complete GraphQL reference