Skip to content

Provider comparison

@media-sdk/core ships two built-in MediaClient implementations: PexelsMediaClient and PixabayMediaClient. Both normalize responses into the same Photo, Video, and Pagination types so your application logic stays provider-neutral.

Quick summary

PexelsPixabay
Best forCurated feeds, size filter, video orientationCategory/dimension filters, editor's choice
AuthAuthorization headerkey= query parameter
Curated photos
Hotlinking rulesStandard API termsMust serve API-returned URLs
Pagination URLsnextPage / prevPage preservedPage-number only

Operation capability matrix

Operation / filterPexels photosPexels videosPixabay photosPixabay videos
searchPhotos / searchVideos
getPhoto / getVideo
curatedPhotos
trackView / trackDownload
orientation filter
size filter
color filter
category filter
minWidth / minHeight
editorsChoice
locale filter

Use getCapabilities to read this matrix at runtime instead of hardcoding provider names.

For multi-provider setup (env vars, createMediaClient, runtime swap), see Provider configuration.

Authentication

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

const client = new PexelsMediaClient(
  new ApiKeyProvider(process.env.PEXELS_API_KEY!),
);
ts
import { ApiKeyProvider, PixabayMediaClient } from "@media-sdk/core";

const client = new PixabayMediaClient(
  new ApiKeyProvider(process.env.PIXABAY_API_KEY!),
);

Both clients accept the same ApiKeyProvider type. Wire formatting is internal to each client.

Pagination differences

Both providers return the same normalized Pagination shape, but behavior diverges:

FieldPexelsPixabay
hasNext / hasPreviousDerived from next_page / prev_page URLsComputed from page * perPage < totalHits
nextPage / prevPagePopulated with raw API URLsAlways undefined
Recommended navigationpage ± 1 (URLs optional)page ± 1 only
ts
// Works for both providers — prefer page numbers
if (pagination.hasNext) {
  await client.searchPhotos({
    query,
    page: pagination.page + 1,
    perPage: pagination.perPage,
  });
}

When to pick each provider

Choose Pexels when you need:

  • Curated photo feeds (getCuratedPhotos)
  • size filter on photos or videos
  • orientation filter on videos
  • locale on video search

Choose Pixabay when you need:

  • category, minWidth, minHeight, or editorsChoice filters
  • A second catalog without maintaining separate integration code
  • Photo search with dimension constraints

Use both when you want runtime provider switching. Instantiate clients directly — there is no ProviderFactory in v0.3.x:

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

type ProviderName = "pexels" | "pixabay";

function createClient(
  provider: ProviderName,
  apiKey: string,
): MediaClient {
  const auth = new ApiKeyProvider(apiKey);

  switch (provider) {
    case "pexels":
      return new PexelsMediaClient(auth);
    case "pixabay":
      return new PixabayMediaClient(auth);
  }
}

Capability-driven UI (required pattern)

Gate features with capabilities, not provider identity strings.

Anti-pattern: provider-string branching

tsx
// ❌ Do not gate UI features on provider name
if (provider === "pexels") {
  showCuratedTab();
}

if (selectedProvider === "pixabay") {
  hideSizeFilter();
}

Provider names may appear in a selector label or env-setup copy. They must not drive feature visibility.

Correct pattern: capabilities

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

function buildFilterUI(client: MediaClient) {
  const caps = getCapabilities(client);

  return {
    showCurated: caps.operations.curatedPhotos,
    showSizeFilter: caps.photoFilters.size,
    showCategoryFilter: caps.photoFilters.category,
    showOrientationFilter: caps.photoFilters.orientation,
  };
}
tsx
import { useMediaCapabilities } from "@media-sdk/react";

function SearchFilters() {
  const caps = useMediaCapabilities();

  return (
    <>
      {caps.operations.curatedPhotos && <CuratedTab />}
      {caps.photoFilters.size && <SizeFilter />}
      {caps.photoFilters.category && <CategoryFilter />}
      {caps.photoFilters.orientation && <OrientationFilter />}
    </>
  );
}

See Capabilities for the full MediaCapabilities type and React cross-link.

Error behavior

ScenarioError
Curated on PixabayUNSUPPORTED_CAPABILITY
Unsupported filter for providerUNSUPPORTED_FILTER
Invalid filter enum valueINVALID_FILTER_VALUE

Filters are never silently ignored. See Errors.