본문 바로가기

업무 자동화

Pexels API 사용과 이미지 라이선스 주의사항

이 글은 2026년 9월 2일 Pexels 공식 API 문서, 라이선스, 이용약관을 기준으로 확인했습니다. 라이선스와 API 조건은 바뀔 수 있으므로 서비스에 적용하기 전 원문을 다시 확인합니다.

검색 요청 전에 알아둘 내용

Pexels API를 사용하면 사진과 동영상을 키워드로 검색해 서비스에 표시할 수 있습니다. 요청의 Authorization 헤더에 API 키를 넣어 인증하며, 키는 브라우저 코드에 포함하지 않고 자신의 서버에서 관리하는 편이 안전합니다.

GET https://api.pexels.com/v1/search?query=workspace&per_page=12
Authorization: YOUR_PEXELS_API_KEY

다만 ‘Pexels 이미지는 무료입니다’라는 한 문장만으로 운영 조건을 설명할 수는 없습니다. 일반 Pexels 라이선스와 API 이용 지침을 함께 확인해야 합니다.

  • 일반 라이선스는 사진과 동영상을 개인·상업 목적으로 무료 사용할 수 있고 저작자 표기를 필수로 요구하지 않습니다.
  • API 문서는 Pexels로 연결되는 눈에 잘 띄는 링크를 요구하고, 가능하면 사진가를 표기해 해당 Pexels 페이지로 연결하도록 안내합니다.
  • Pexels의 핵심 기능을 복제하거나 콘텐츠를 사실상 원본 그대로 재판매·재배포하는 서비스는 허용되지 않을 수 있습니다.
  • 기본 API 제한은 공식 문서상 시간당 200회, 월 20,000회이며 우회 시도는 계정 접근 중단으로 이어질 수 있습니다.

브라우저와 자체 백엔드, Pexels API 사이에서 API 키를 서버에 두고 이미지와 출처 링크를 돌려주는 호출 구조

Pexels API와 라이선스를 분리해 이해합니다

Pexels 라이선스는 콘텐츠를 어떤 방식으로 사용할 수 있는지 설명합니다. API 문서는 프로그램으로 콘텐츠를 검색하고 표시할 때 지켜야 할 인증, 호출 제한, 링크와 표기 방식을 추가로 설명합니다. API를 사용한다면 둘 중 하나만 읽지 않습니다.

구분 확인할 내용
Pexels License 사진·동영상의 허용 용도와 금지 사례를 확인합니다.
API Documentation 인증, 엔드포인트, 쿼터, 링크와 사진가 표기 지침을 확인합니다.
Terms of Service standalone 배포, 서비스 복제 등 상세 제한을 확인합니다.

무료라는 표현은 가격이 없다는 뜻이지 아무 조건 없이 원본 콘텐츠를 재배포할 수 있다는 뜻이 아닙니다. 특히 이미지 모음, 배경화면, 스톡 이미지 검색처럼 Pexels 자체의 핵심 기능과 경쟁할 수 있는 서비스를 만들 때는 약관을 세밀하게 검토합니다.

1단계: API 키를 발급합니다

Pexels 계정으로 로그인한 뒤 공식 API 페이지에서 키 발급 절차를 진행합니다.

아래 화면은 발급 당시 기준입니다.

1. 로그인 후 Developer 페이지로 이동합니다.

Pexels 로그인 후 Developer 메뉴로 이동한 화면

2. Image & Video API 메뉴를 선택합니다.

Pexels Developer 페이지에서 Image and Video API를 선택한 화면

3. Your API Key 버튼을 눌러 키를 확인합니다.

Pexels에서 발급된 API 키가 표시된 화면

발급 화면의 버튼 이름과 승인 절차는 바뀔 수 있습니다. 화면을 그대로 외우기보다 공식 API 페이지에서 현재 안내를 따릅니다.

키를 받은 뒤 프로젝트 루트의 로컬 환경 파일에 저장할 수 있습니다.

PEXELS_API_KEY=발급받은_키를_입력합니다

실제 키가 들어 있는 파일은 Git에 포함하지 않습니다.

.env
.env.local

팀원이 필요한 변수 이름을 알 수 있도록 값이 비어 있는 예시 파일을 별도로 둡니다.

# .env.example
PEXELS_API_KEY=

이미 노출된 키는 Git 기록에서 문자열만 숨기는 것으로 끝내지 않습니다. Pexels 계정에서 해당 키를 교체할 수 있는지 확인하고, 배포 환경의 비밀 값도 새 키로 갱신합니다.

브라우저가 아니라 서버에서 호출합니다

다음과 같은 프론트엔드 코드는 빌드 결과나 개발자 도구에서 API 키가 보일 수 있으므로 피합니다.

// 사용하지 않는 예시입니다.
fetch("https://api.pexels.com/v1/search?query=office", {
  headers: {
    Authorization: "실제_API_KEY",
  },
});

대신 브라우저는 자신의 백엔드에 검색어를 전달하고, 백엔드가 Pexels API를 호출합니다. 이 구조는 키를 서버에 보관할 수 있고, 검색어 검증·캐시·호출 제한·오류 처리를 한곳에서 적용할 수 있습니다.

브라우저
  → 자신의 /api/photos 엔드포인트
    → 서버의 PEXELS_API_KEY로 Pexels 호출
      → 필요한 사진 메타데이터만 브라우저에 반환합니다.

서버 프록시를 두더라도 Pexels의 쿼터를 우회하거나 콘텐츠 출처를 숨기기 위한 용도로 사용하지 않습니다.

Node.js 서버 검색 예제를 구현합니다

다음 예제는 Node.js 18 이상에서 사용할 수 있는 전역 fetch를 사용합니다. 검색어를 URLSearchParams로 인코딩하고, 응답 상태와 쿼터 헤더를 확인한 뒤 필요한 필드만 반환합니다.

const PEXELS_SEARCH_URL = "https://api.pexels.com/v1/search";

export async function searchPexelsPhotos({
  query,
  page = 1,
  perPage = 12,
}) {
  if (typeof query !== "string" || query.trim().length < 2) {
    throw new Error("검색어는 두 글자 이상 입력합니다.");
  }

  const safePage = Math.max(1, Number(page) || 1);
  const safePerPage = Math.min(80, Math.max(1, Number(perPage) || 12));

  const params = new URLSearchParams({
    query: query.trim(),
    page: String(safePage),
    per_page: String(safePerPage),
  });

  const response = await fetch(`${PEXELS_SEARCH_URL}?${params}`, {
    headers: {
      Authorization: process.env.PEXELS_API_KEY,
    },
    signal: AbortSignal.timeout(8_000),
  });

  if (response.status === 429) {
    throw new Error("Pexels API 호출 한도를 초과했습니다.");
  }

  if (!response.ok) {
    const body = await response.text();
    throw new Error(
      `Pexels API 오류: ${response.status} ${body.slice(0, 200)}`,
    );
  }

  const data = await response.json();

  return {
    page: data.page,
    perPage: data.per_page,
    totalResults: data.total_results,
    nextPage: data.next_page ?? null,
    quota: {
      limit: response.headers.get("x-ratelimit-limit"),
      remaining: response.headers.get("x-ratelimit-remaining"),
      reset: response.headers.get("x-ratelimit-reset"),
    },
    photos: data.photos.map((photo) => ({
      id: photo.id,
      width: photo.width,
      height: photo.height,
      alt: photo.alt,
      photographer: photo.photographer,
      photographerUrl: photo.photographer_url,
      pexelsUrl: photo.url,
      src: {
        medium: photo.src.medium,
        large: photo.src.large,
      },
    })),
  };
}

per_page의 허용 범위와 응답 필드는 변경될 수 있으므로 공식 문서의 현재 정의를 확인합니다. 예제에서 상한을 두는 목적은 한 화면에 지나치게 많은 결과를 요청하지 않도록 애플리케이션 정책을 적용하는 것입니다.

Express 라우트로 브라우저에 제공합니다

다음처럼 자신의 API 라우트를 만들 수 있습니다.

import express from "express";
import { searchPexelsPhotos } from "./pexels.js";

const app = express();

app.get("/api/photos", async (req, res) => {
  try {
    const result = await searchPexelsPhotos({
      query: req.query.q,
      page: req.query.page,
      perPage: 12,
    });

    res.set("Cache-Control", "public, max-age=300");
    res.json(result);
  } catch (error) {
    const isRateLimit = error.message.includes("호출 한도");
    res.status(isRateLimit ? 429 : 502).json({
      error: isRateLimit
        ? "이미지 검색 요청이 많습니다. 잠시 뒤 다시 시도합니다."
        : "이미지 검색을 완료하지 못했습니다.",
    });
  }
});

app.listen(3000);

운영 환경에서는 다음 보호 장치를 추가합니다.

  • 자신의 API 라우트에 사용자별 요청 제한을 적용합니다.
  • 동일 검색어와 페이지 결과를 짧게 캐시합니다.
  • 허용할 검색어 길이와 문자 범위를 검증합니다.
  • Pexels의 상세 오류 본문을 브라우저에 그대로 노출하지 않습니다.
  • 로그에 Authorization 헤더나 환경 변수 값을 기록하지 않습니다.
  • 타임아웃과 재시도 횟수를 제한합니다.

이미지와 출처 링크를 함께 표시합니다

API 응답에는 사진가 이름, 사진가의 Pexels URL, 사진 상세 페이지 URL이 포함됩니다. 이 값을 버리지 않고 UI에 전달합니다.

<figure>
  <a href="PEXELS_PHOTO_URL">
    <img src="PHOTO_SRC" alt="API 응답과 문맥에 맞게 검토한 대체 텍스트" />
  </a>
  <figcaption>
    <a href="PHOTOGRAPHER_URL">사진가 이름</a>의 사진 ·
    <a href="PEXELS_PHOTO_URL">Pexels에서 보기</a>
  </figcaption>
</figure>

페이지의 푸터나 이미지 목록 주변에는 Pexels 제공 콘텐츠임을 알 수 있는 링크도 배치합니다.

<a href="https://www.pexels.com">Photos provided by Pexels</a>

Pexels 일반 라이선스는 저작자 표기를 의무로 요구하지 않지만, API 문서는 Pexels로 돌아가는 눈에 잘 띄는 링크를 요구하고 가능하면 사진가 표기를 안내합니다. 따라서 API 기반 서비스에서는 위와 같이 Pexels 및 사진가 페이지 링크를 제품 요구사항으로 구현하는 편이 안전합니다.

alt 속성에는 사진가 이름이나 ‘Pexels 이미지’만 반복하지 않습니다. 실제 화면에서 이미지가 전달하는 의미를 설명하고, 장식용 이미지라면 빈 대체 텍스트를 검토합니다. API의 alt 값도 서비스 문맥에 맞는지 사람이 확인합니다.

페이지네이션을 안전하게 처리합니다

사진 검색 응답에는 현재 페이지와 다음 페이지 정보가 포함될 수 있습니다. 다음 페이지 URL을 그대로 클라이언트에 전달해 브라우저가 Pexels를 직접 호출하게 만들지 않습니다. 자신의 페이지 번호만 받아 서버가 새 요청을 구성합니다.

const nextPage = result.nextPage ? result.page + 1 : null;

무한 스크롤에서는 사용자가 화면을 내릴 때마다 중복 요청이 발생하기 쉽습니다. 요청 중 상태를 두고, 같은 검색어·페이지 조합을 다시 호출하지 않으며, 검색어가 바뀌면 이전 요청을 취소합니다.

쿼터 헤더와 429 오류를 처리합니다

Pexels 공식 API 문서가 안내하는 기본 제한은 시간당 200회와 월 20,000회입니다. 더 높은 제한이 필요하면 사용 사례와 표시 방식을 준비해 Pexels에 요청합니다. 여러 키를 만들거나 프록시를 바꿔 제한을 우회하지 않습니다.

성공한 2xx 응답에는 다음 월간 쿼터 헤더가 포함됩니다.

응답 헤더 의미
X-Ratelimit-Limit 월간 전체 요청 한도를 나타냅니다.
X-Ratelimit-Remaining 월간 남은 요청 수를 나타냅니다.
X-Ratelimit-Reset 월간 기간이 초기화되는 Unix 시각을 나타냅니다.

공식 문서에 따르면 이 헤더는 성공 응답에만 포함되며 429 Too Many Requests 응답에는 포함되지 않습니다. 따라서 429가 발생한 뒤 reset 헤더를 읽어 복구 시간을 계산하는 코드에 의존하지 않습니다. 성공 응답에서 관측한 값과 자체 사용량을 모니터링합니다.

재시도는 모든 오류에 즉시 적용하지 않습니다.

상태 처리 방법
401 또는 403 키, 권한, 이용 조건을 확인하며 자동 반복 호출하지 않습니다.
429 요청을 중단하고 캐시 결과나 안내 메시지를 사용합니다.
5xx 짧은 지수 백오프와 제한된 횟수로만 재시도합니다.
타임아웃 요청을 취소하고 사용자에게 다시 시도할 수 있음을 안내합니다.

라이선스와 이용 조건을 확인합니다

Pexels 콘텐츠를 API로 표시하기 전에 다음 항목을 검토합니다.

  • 서비스 화면에 Pexels로 연결되는 눈에 잘 띄는 링크를 제공합니다.
  • 가능하면 사진가 이름을 표시하고 해당 Pexels 페이지로 연결합니다.
  • 사진 상세 URL과 사진가 URL을 API 응답에서 보존합니다.
  • 인물이나 브랜드가 포함된 콘텐츠를 모욕적·오해를 부르는 방식으로 사용하지 않습니다.
  • 사진 속 인물이나 브랜드가 제품을 보증한다고 암시하지 않습니다.
  • 원본 콘텐츠를 사실상 그대로 판매하거나 재배포하지 않습니다.
  • Pexels의 핵심 검색·스톡·배경화면 기능을 복제하는 서비스가 아닌지 검토합니다.
  • 적용 시점의 Pexels License와 Terms of Service 원문을 다시 확인합니다.

이미지에 인물, 상표, 건축물, 작품이 포함되어 있으면 Pexels 라이선스만으로 모든 초상권·상표권·재산권 문제가 자동 해결된다고 단정하지 않습니다. 광고, 의료, 정치, 민감한 주제처럼 맥락에 따라 오해가 커질 수 있는 용도는 별도로 검토합니다.

자주 발생하는 오류를 해결합니다

브라우저에서 401 오류가 발생합니다

API 키를 프론트엔드에 추가하는 방식으로 해결하지 않습니다. 서버 환경 변수에 키가 실제로 설정되었는지, 서버 요청의 Authorization 헤더 값이 비어 있지 않은지 확인합니다.

로컬에서는 되지만 배포 환경에서 실패합니다

.env 파일은 보통 배포 서버에 자동으로 전달되지 않습니다. 호스팅 서비스의 비밀 또는 환경 변수 설정에 PEXELS_API_KEY를 등록하고 애플리케이션을 다시 배포합니다. 값 자체는 빌드 로그에 출력하지 않습니다.

같은 검색이 쿼터를 빠르게 소모합니다

검색어를 입력할 때마다 요청한다면 디바운스를 적용하고, 동일한 검색어·페이지 결과를 캐시합니다. 화면이 다시 렌더링될 때 중복 호출되지 않는지도 확인합니다.

이미지가 깨지거나 레이아웃이 흔들립니다

응답의 widthheight를 사용해 표시 비율을 미리 확보합니다. 화면 크기에 맞는 src 변형을 선택하고 지나치게 큰 원본을 모든 목록 카드에 사용하지 않습니다.

출처 표기 없이 사용해도 되는지 헷갈립니다

웹에서 직접 내려받은 콘텐츠의 일반 라이선스와 API를 통한 통합 지침을 구분합니다. API 기반 화면이라면 공식 API 문서에 맞춰 Pexels 링크와 가능한 사진가 표기를 구현합니다.

서비스에 적용하기 전에 확인합니다

  • API 키를 서버 비밀 값으로 보관합니다.
  • .env와 실제 키가 Git에 포함되지 않는지 확인합니다.
  • 검색어와 페이지 값을 서버에서 검증합니다.
  • 타임아웃, 캐시, 자신의 라우트 요청 제한을 적용합니다.
  • 성공 응답의 월간 쿼터 헤더를 모니터링합니다.
  • 429 발생 시 반복 호출을 멈추고 사용자 메시지를 제공합니다.
  • Pexels와 사진가 링크를 UI에 유지합니다.
  • 이미지 문맥과 대체 텍스트를 사람이 검토합니다.
  • 정책 검증일과 적용한 약관 버전을 기록합니다.

검색 성공 뒤에 챙길 일

Pexels API 통합의 핵심은 검색 요청 하나를 성공시키는 것이 아닙니다. API 키를 서버에서 보호하고, 검색어와 호출량을 통제하며, 이미지와 함께 Pexels 및 사진가 링크를 제공해야 합니다. 무료 콘텐츠라는 이유로 라이선스와 API 조건을 생략하지 않습니다.

운영 환경에서는 성공 응답의 쿼터 헤더를 기록하고 429를 정상적인 장애 시나리오로 처리합니다. 서비스가 Pexels의 핵심 기능을 복제하거나 원본 콘텐츠를 standalone 형태로 배포하지 않는지도 출시 전에 확인합니다.

공식 참고 자료

반응형

'업무 자동화' 카테고리의 다른 글

Jira MCP 사용 가이드와 안전한 권한 설계  (0) 2025.12.13