Skip to content

Pagination

Search and curated methods return a PaginatedResponse<T> with normalized items and pagination fields. Both Pexels and Pixabay share the same pagination shape so your navigation logic stays provider-neutral.

Installation

bash
pnpm add @media-sdk/core@^0.3.0

PaginatedResponse

ts
interface PaginatedResponse<T> {
  items: T[];
  pagination: Pagination;
}

searchPhotos, searchVideos, and getCuratedPhotos all return this type.

Pagination shape

ts
interface Pagination {
  page: number;
  perPage: number;
  totalResults?: number;
  hasNext: boolean;
  hasPrevious: boolean;
  nextPage?: string;
  prevPage?: string;
}
FieldTypeDescription
pagenumberCurrent page number
perPagenumberItems per page
totalResultsnumber?Total matching results (when provider reports it)
hasNextbooleanWhether a next page exists
hasPreviousbooleanWhether a previous page exists
nextPagestring?Raw Pexels next-page URL (optional)
prevPagestring?Raw Pexels previous-page URL (optional)

Use hasNext / hasPrevious for navigation logic. The URL fields are preserved for advanced use cases but are not required for page-based navigation.

Page-based navigation

This pattern works for both providers:

ts
import { ApiKeyProvider, PexelsMediaClient } from "@media-sdk/core";

const client = new PexelsMediaClient(
  new ApiKeyProvider(process.env.PEXELS_API_KEY!),
);

const query = "nature";
const perPage = 20;
let page = 1;

const first = await client.searchPhotos({ query, page, perPage });
console.log(first.items);

if (first.pagination.hasNext) {
  const second = await client.searchPhotos({
    query,
    page: first.pagination.page + 1,
    perPage: first.pagination.perPage,
  });
  console.log(second.items);
}

if (first.pagination.hasPrevious) {
  // page 1 has no previous page — hasPrevious is false
}

Stateful pagination helper

ts
import type { MediaClient, Photo, PaginatedResponse } from "@media-sdk/core";

async function fetchPage(
  client: MediaClient,
  query: string,
  page: number,
  perPage = 20,
): Promise<PaginatedResponse<Photo>> {
  return client.searchPhotos({ query, page, perPage });
}

async function fetchAllPages(
  client: MediaClient,
  query: string,
  maxPages = 3,
): Promise<Photo[]> {
  const photos: Photo[] = [];
  let page = 1;
  let hasNext = true;

  while (hasNext && page <= maxPages) {
    const result = await fetchPage(client, query, page);
    photos.push(...result.items);
    hasNext = result.pagination.hasNext;
    page = result.pagination.page + 1;
  }

  return photos;
}

Pexels vs Pixabay divergence

Both clients normalize to the same Pagination interface, but the providers compute fields differently:

BehaviorPexelsPixabay
hasNextBoolean(next_page URL)page * perPage < totalHits
hasPreviousBoolean(prev_page URL)page > 1
nextPage / prevPagePopulated from APIAlways undefined
totalResultsFrom total_resultsFrom totalHits
Recommended navigationpage ± 1page ± 1
ts
const { pagination } = await pexels.searchPhotos({
  query: "nature",
  page: 1,
  perPage: 20,
});

console.log(pagination.nextPage);  // "https://api.pexels.com/v1/search?..."
console.log(pagination.hasNext); // true
ts
const { pagination } = await pixabay.searchPhotos({
  query: "nature",
  page: 1,
  perPage: 20,
});

console.log(pagination.nextPage);  // undefined
console.log(pagination.hasNext); // true when more results exist

Prefer incrementing page over following raw URLs — that keeps code portable across providers.

Curated pagination

getCuratedPhotos uses the same Pagination shape (Pexels only):

ts
import { getCapabilities } from "@media-sdk/core";

if (getCapabilities(client).operations.curatedPhotos) {
  const { items, pagination } = await client.getCuratedPhotos({
    page: 2,
    perPage: 15,
  });

  if (pagination.hasNext) {
    await client.getCuratedPhotos({
      page: pagination.page + 1,
      perPage: pagination.perPage,
    });
  }
}

React hooks

useMediaSearch and useMediaVideos from @media-sdk/react@^0.3.0 expose pagination from the latest response and re-fetch when page or perPage changes. See React hooks.