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