webPulse

Developer API

API Key로 Monitor를 프로그래밍 방식으로 관리하고, Webhook으로 변경 이벤트를 자신의 시스템에 연동하세요.

Overview

webPulse Developer API는 웹페이지 변경 감지 Monitor를 코드로 생성·조회·수정·삭제하고, 변경이 감지되면 Webhook으로 이벤트를 받을 수 있는 REST API입니다. 대시보드(웹 UI)와 동일한 백엔드 로직을 공유하므로 어느 쪽으로 만든 Monitor든 서로 조회/관리할 수 있습니다.

CI/CD 파이프라인에서 배포 후 특정 페이지가 실제로 바뀌었는지 확인하거나, 자동화 워크플로우·AI Agent가 웹페이지 변경을 트리거로 사용하거나, 대시보드 없이 여러 Monitor를 코드로 일괄 관리하고 싶을 때 사용합니다.

Base URLhttps://your-app.com/api/v1
형식요청/응답 바디는 모두 JSON (Content-Type: application/json)

Quick Start

1

회원가입

회원가입 페이지에서 이메일로 가입하고 인증을 완료합니다. 신규 계정은 Free 플랜으로 시작합니다.

2

API Key 발급

대시보드의 API Keys 페이지에서 키를 발급합니다. 키 원문은 발급 시 한 번만 표시되며 이후에는 다시 확인할 수 없으니 바로 안전한 곳에 저장해두세요.

3

첫 요청 보내기

발급받은 키로 Monitor 목록을 조회해봅니다(계정을 막 만들었다면 빈 배열이 정상입니다).

curl https://your-app.com/api/v1/monitors \
  -H "Authorization: Bearer wt_xxxxxxxxxxxxxxxxxxxxxxxx"
4

응답 확인

200 OK와 함께 아래와 같은 응답이 오면 정상적으로 연동된 것입니다. 이제 API Reference의 Monitor 생성 예제로 넘어가세요.

{ "monitors": [] }

Authentication

API Keys 페이지에서 발급한 키를 Authorization 헤더에 담아 보냅니다.

Authorization: Bearer wt_xxxxxxxxxxxxxxxxxxxxxxxx

키가 없거나, 형식이 올바르지 않거나, 폐기(revoke)된 키로 요청하면 모두 401이 반환됩니다.

키 재발급/폐기

키를 회전(rotate)하려면 API Keys 페이지에서 새 키를 먼저 발급하고, 새 키로 전환이 끝난 뒤 기존 키를 폐기하세요. 폐기는 즉시 반영되며 되돌릴 수 없습니다. 별도의 만료 기간은 없고, 직접 폐기하기 전까지 계속 유효합니다.

키 노출 주의사항

API Key는 서버 환경변수 등 안전한 곳에만 보관하고, 프런트엔드 코드나 브라우저에 노출되는 곳(클라이언트 번들, 공개 저장소, 공개 로그)에는 절대 포함하지 마세요. 원문은 발급 시 한 번만 보여주고 DB에는 해시만 저장하므로, 분실하면 재확인이 아니라 재발급만 가능합니다. 노출이 의심되면 지체 없이 폐기하세요.

API Reference

모든 요청/응답 바디는 JSON입니다. 목록 조회는 페이지네이션 없이 해당 계정의 전체 Monitor를 최신순으로 반환합니다.

GET/api/v1/monitors목록 조회
POST/api/v1/monitors생성
GET/api/v1/monitors/:id단건 조회
PATCH/api/v1/monitors/:id수정
DELETE/api/v1/monitors/:id삭제
POST/api/v1/monitors/:id/check즉시 체크 실행
GET/api/v1/monitors/:id/changes변경 이력 목록 조회
GET/api/v1/monitors/:id/changes/:changeEventId변경 이력 본문 조회
POST/api/v1/monitors/:id/changes/:changeEventId/summarize변경 diff를 AI로 요약

Monitor 객체

생성/조회/수정 응답은 아래 필드를 가진 monitor 객체를 반환합니다. 목록 조회는 page, pageSize,tag, status 쿼리를 지원하며 목록 항목에서는lastHash, webhookSecret 등 상세 전용 필드를 제외합니다.

idstringMonitor 고유 ID
namestring이름 (최대 100자)
urlstring감시 대상 URL (최대 2048자)
cssSelectorstring | null감시 영역을 좁히는 CSS Selector (최대 500자)
renderingMode"static" | "dynamic"정적 HTML 또는 Playwright 렌더링. dynamic은 Dynamic 애드온 슬롯 구매 필요
intervalMinutesnumber체크 주기(분). 5 ~ 10080(7일). 플랜별 최소값 있음, dynamic은 최소 30분
status"active" | "paused" | "error"연속 5회 체크 실패 시 자동으로 paused로 전환됨
lastHashstring | null가장 최근 콘텐츠의 SHA256 해시
lastCheckedAtstring | null마지막 체크 시각 (ISO 8601)
lastErrorstring | null마지막 체크 실패 사유
consecutiveFailuresnumber연속 실패 횟수
notifyEmailboolean변경 시 이메일 알림 여부
webhookUrlstring | null변경 이벤트를 받을 Webhook URL
webhookSecretstring | nullWebhook 서명 검증용 Secret. webhookUrl 설정 시 자동 발급됨
createdAtstring생성 시각 (ISO 8601)
updatedAtstring마지막 수정 시각 (ISO 8601)

Change Event 객체

변경 이력 조회 응답은 아래 필드를 가진 changeEvent 배열과 pagination을 반환합니다. 목록은 Snapshot 본문을 제외하며, 본문은 Change Event 단건 조회에서 반환합니다.

idstringChange Event 고유 ID
monitorIdstring소속 Monitor ID
createdAtstring변경이 감지된 시각 (ISO 8601)
snapshotobject목록에서는 { id, hash, isInitial, createdAt }, 단건 조회에서는 content 포함
previousSnapshotobject | null목록에서는 메타데이터만, 단건 조회에서는 content 포함
addedLinesnumber | null추가된 줄 수
removedLinesnumber | null삭제된 줄 수
aiSummarystring | nullAI 요약 결과. 아직 요약하지 않았으면 null
notifiedEmailAtstring | null이메일 알림 발송 시각
notifiedWebhookAtstring | nullWebhook 발송 시각
notifiedPushAtstring | nullWeb Push 발송 시각
notifyErrorstring | null알림 발송 중 발생한 오류

Monitor 생성

curl -X POST https://your-app.com/api/v1/monitors \
  -H "Authorization: Bearer wt_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "채용 공고",
    "url": "https://example.com/careers",
    "renderingMode": "static",
    "intervalMinutes": 60,
    "cssSelector": ".job-list",
    "notifyEmail": true,
    "webhookUrl": "https://your-app.com/webhooks/webpulse"
  }'

# 201 Created
{
  "monitor": {
    "id": "b3f1c2d4-...",
    "name": "채용 공고",
    "url": "https://example.com/careers",
    "cssSelector": ".job-list",
    "renderingMode": "static",
    "intervalMinutes": 60,
    "status": "active",
    "lastHash": null,
    "lastCheckedAt": null,
    "lastError": null,
    "consecutiveFailures": 0,
    "notifyEmail": true,
    "webhookUrl": "https://your-app.com/webhooks/webpulse",
    "webhookSecret": "whsec_...",
    "createdAt": "2026-08-14T02:00:00.000Z",
    "updatedAt": "2026-08-14T02:00:00.000Z"
  }
}

Monitor 수정 (일시정지)

curl -X PATCH https://your-app.com/api/v1/monitors/b3f1c2d4-... \
  -H "Authorization: Bearer wt_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "status": "paused" }'

생성과 동일한 필드를 부분적으로 보낼 수 있습니다(name, url, cssSelector, renderingMode, intervalMinutes, notifyEmail, webhookUrl). status는 active/paused만 직접 바꿀 수 있고, error는 연속 실패 시 시스템이 자동으로 설정합니다.

즉시 체크 실행

curl -X POST https://your-app.com/api/v1/monitors/b3f1c2d4-.../check \
  -H "Authorization: Bearer wt_xxxxxxxxxxxxxxxxxxxxxxxx"

# 200 OK
{ "monitorId": "b3f1c2d4-...", "changed": false }

예약된 체크를 기다리지 않고 즉시 실행합니다. 일시정지된 Monitor는409를 반환합니다.

변경 이력 조회

curl https://your-app.com/api/v1/monitors/b3f1c2d4-.../changes \
  -H "Authorization: Bearer wt_xxxxxxxxxxxxxxxxxxxxxxxx"

# 200 OK
{
  "changeEvents": [
    {
      "id": "9ac2...",
      "monitorId": "b3f1c2d4-...",
      "createdAt": "2026-08-14T02:00:00.000Z",
      "snapshot": {
        "id": "snap_2...",
        "content": "...",
        "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
        "isInitial": false,
        "createdAt": "2026-08-14T02:00:00.000Z"
      },
      "previousSnapshot": {
        "id": "snap_1...",
        "content": "...",
        "hash": "275be29b9d8e...",
        "createdAt": "2026-08-13T02:00:00.000Z"
      },
      "aiSummary": null,
      "notifiedEmailAt": "2026-08-14T02:00:05.000Z",
      "notifiedWebhookAt": null,
      "notifiedPushAt": null,
      "notifyError": null
    }
  ]
}

webhookUrl 페이로드에는 changeEventId만 담기므로, 실제 변경 내용(before/after)이 필요하면 이 엔드포인트로 조회하세요.

변경 diff를 AI로 요약

curl -X POST https://your-app.com/api/v1/monitors/b3f1c2d4-.../changes/9ac2.../summarize \
  -H "Authorization: Bearer wt_xxxxxxxxxxxxxxxxxxxxxxxx"

# 200 OK
{ "aiSummary": "채용 공고 페이지에 '백엔드 엔지니어' 포지션이 새로 추가되고 마감일이 8/30로 변경되었습니다." }

해당 changeEventId의 이전/이후 스냅샷 diff를 LLM으로 요약합니다. 한 번 생성된 요약은 저장되어 이후 같은 이벤트를 다시 요청해도 재생성 없이 그대로 반환됩니다. 이 기능은 플랜과 별도로 운영되는 애드온이라 크레딧 또는 무제한 구독이 있어야 하며, 부족하면 402가 반환됩니다.

Monitor 삭제

curl -X DELETE https://your-app.com/api/v1/monitors/b3f1c2d4-... \
  -H "Authorization: Bearer wt_xxxxxxxxxxxxxxxxxxxxxxxx"

# 204 No Content

Webhook 페이로드

변경이 감지되면 등록한 webhookUrl로 아래와 같은 JSON을 POST합니다. Monitor 생성/조회 응답의 webhookSecret으로 서명한 값이 X-WebPulse-Signature 헤더에 담깁니다.

POST /webhooks/webpulse
Content-Type: application/json
X-WebPulse-Signature: <HMAC-SHA256(secret, body)>

{
  "event": "monitor.changed",
  "monitorId": "b3f1...",
  "monitorName": "채용 공고",
  "url": "https://example.com/careers",
  "changeEventId": "9ac2...",
  "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "timestamp": "2026-08-14T02:00:00.000Z"
}

이 payload에는 실제로 무엇이 바뀌었는지(before/after 내용)는 포함되지 않습니다. 변경 내용은 changeEventId로 GET /api/v1/monitors/:id/changes를 조회하거나 대시보드의 Monitor 상세 페이지에서 확인해야 합니다. 또한 현재 Webhook 전송은 실패 시 자동 재시도하지 않습니다 — 응답 코드가 2xx가 아니면 실패로 기록되고 다음 변경 감지까지 재전송되지 않으니, 수신 엔드포인트를 안정적으로 유지하는 게 중요합니다.

서명 검증 예시 (Node.js):

import { createHmac, timingSafeEqual } from "node:crypto";

function isValidSignature(body: string, signature: string, secret: string) {
  const expected = createHmac("sha256", secret).update(body).digest("hex");
  return timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
}

URL 제약 (SSRF 방어)

url과 webhookUrl 모두 내부망 접근을 막기 위해 localhost, 127.0.0.1, 169.254.0.0/16 등 사설 IP 대역으로는 등록할 수 없습니다. 로컬 개발 서버로 Webhook을 테스트하려면 ngrok 같은 터널링 도구로 공인 URL을 발급받아 사용하세요. 리다이렉트가 발생하는 경우 최종 목적지 주소에도 동일한 제약이 적용됩니다.

Errors

실패한 요청은 아래 형식으로 응답합니다. 입력값 검증 실패 시에는details에 필드별 오류가 함께 담깁니다.

{ "error": "Free 플랜은 Monitor를 최대 3개까지 등록할 수 있습니다." }
400Bad Request입력값 검증 실패 또는 안전하지 않은 URL(SSRF 위험) — error 메시지와 details를 참고해 요청 바디를 고치세요.
401UnauthorizedAPI Key 누락/형식 오류/폐기됨 — Authorization 헤더와 키 상태를 확인하세요.
402Payment RequiredAI 요약 크레딧 부족(무제한 구독 없음) — 대시보드 요금제 페이지에서 충전/구독하세요.
403Forbidden플랜 한도 초과(Monitor 개수, 최소 주기) 또는 Dynamic 애드온 슬롯 부족/미구매 — 플랜을 올리거나 값을 한도 안으로 줄이세요.
404Not Found존재하지 않거나 다른 계정 소유의 Monitor — id 값을 다시 확인하세요.
409Conflict일시정지된 Monitor에 즉시 체크 요청 — status를 active로 바꾼 뒤 다시 시도하세요.
429Too Many RequestsRate Limit 초과 — 아래 Rate Limits를 참고해 요청 빈도를 줄이고 잠시 후 재시도하세요.
500Internal Server Error서버 내부 오류 — 잠시 후 재시도해도 반복되면 Support로 문의하세요.

Rate Limits

남용과 비용 폭탄을 막기 위해 Developer API는 두 단계로 요청 빈도를 제한합니다. 한도를 넘으면 429와 함께 아래 메시지가 반환됩니다.

{ "error": "요청이 너무 많습니다. 잠시 후 다시 시도해주세요." }
IP 기준분당 30회API Key 인증 이전 단계에 적용됩니다(키 스캐닝 방지 목적). 유효하지 않은 키로 반복 요청하면 여기에 먼저 걸립니다.
API Key 기준분당 60 ~ 1,500회인증에 성공한 뒤 그 키의 모든 요청(엔드포인트 무관)에 공통으로 적용됩니다. 정확한 값은 아래 Developer 요금제 구독 여부에 따라 달라집니다.

Developer 요금제를 구독하지 않은 계정은 기본값인 분당 60회가 적용되고, 구독 중이면 등급별로 상향된 한도(Basic 200회 / Pro 500회 / Scale 1,500회)가 적용됩니다 — 자세한 내용은 아래 Usage / Billing을 참고하세요. 대량의 요청이 필요하면 짧은 간격의 폴링 대신 Webhook 연동을 사용하세요 — 변경이 실제로 발생했을 때만 알림을 받으므로 요청 횟수를 크게 줄일 수 있습니다.

Usage / Billing

API로 생성/수정하는 Monitor는 기본적으로 대시보드와 동일한 플랜 제한을 받습니다. 한도를 넘으면 403과 함께 사유가 담긴 메시지를 반환합니다. 아래 Developer 요금제를 별도로 구독하면, API Key로 만드는 Monitor에 한해 이 플랜 한도 대신 Developer 요금제 한도가 적용됩니다(대시보드에서 직접 만드는 Monitor는 이 요금제와 무관하게 그대로 Dashboard 플랜을 따릅니다).

PlanMonitor 최대최소 체크 주기월 요금
Free3개60분무료
Starter5개10분3,900원
Pro15개5분9,900원
Business40개5분19,900원

Developer 요금제 (API 전용)

Dashboard 플랜과 완전히 별개로, API로 더 많은 Monitor를 더 짧은 주기·더 높은 호출 빈도로 다루고 싶은 계정을 위한 요금제입니다. API Keys 페이지에서 구독할 수 있고, Dashboard 유료 플랜을 구독 중이 아니어도 바로 가입할 수 있습니다. 구독 중이면 API Key로 만드는 Monitor의 개수/최소 주기/Rate Limit이 아래 값으로 대체됩니다 — 미구독 시에는 위 Dashboard 플랜 표를 그대로 따릅니다. Dynamic 렌더링은 이 요금제의 범위 밖이라 여전히 Dashboard의 Dynamic 애드온으로만 이용할 수 있습니다.

PlanMonitor 최대최소 체크 주기Rate Limit월 요금
API Basic20개15분분당 200회9,900원
API Pro60개10분분당 500회39,900원
API Scale150개5분분당 1,500회149,900원

Dynamic 렌더링 애드온

Dynamic(Playwright) 렌더링은 플랜에 포함되지 않고, 유료 플랜을 구독 중인 계정만 대시보드의 요금제 페이지에서 슬롯 단위로 별도 구매하는 애드온입니다. 구매한 슬롯 개수만큼만 renderingMode: "dynamic"으로 Monitor를 등록할 수 있고, 구매 가능한 슬롯 상한은 구독 중인 플랜의 Monitor 최대 개수와 같습니다. Dynamic Monitor는 원가 때문에 최소 체크 주기가 30분으로 별도 적용됩니다. 결제 수단은 유료 플랜 구독 시 등록된 카드를 그대로 재사용합니다.

AI 요약 애드온

AI 요약은 Monitor 플랜과 무관하게 계정 단위로 별도 과금되는 애드온입니다. 신규 가입 시 체험용 크레딧이 자동 지급되며, 이후에는 대시보드의 요금제 페이지에서 크레딧 팩을 충전하거나(요약 1회 = 1크레딧 소진) 무제한 정액 구독으로 전환할 수 있습니다. 결제 수단은 유료 플랜 구독 시 등록된 카드를 그대로 재사용합니다.

SDK / Examples

공식 SDK는 아직 제공하지 않습니다. REST API라 언어에 상관없이 HTTP 클라이언트로 바로 연동할 수 있습니다 — 아래는 Monitor를 생성하는 동일한 요청을 curl/JavaScript/Python으로 작성한 예시입니다.

curl

curl -X POST https://your-app.com/api/v1/monitors \
  -H "Authorization: Bearer wt_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "채용 공고",
    "url": "https://example.com/careers",
    "intervalMinutes": 60
  }'

JavaScript (fetch)

const res = await fetch("https://your-app.com/api/v1/monitors", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WEBPULSE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "채용 공고",
    url: "https://example.com/careers",
    intervalMinutes: 60,
  }),
});

if (!res.ok) {
  const { error } = await res.json();
  throw new Error(error);
}

const { monitor } = await res.json();

Python (requests)

import os
import requests

res = requests.post(
    "https://your-app.com/api/v1/monitors",
    headers={"Authorization": f"Bearer {os.environ['WEBPULSE_API_KEY']}"},
    json={
        "name": "채용 공고",
        "url": "https://example.com/careers",
        "intervalMinutes": 60,
    },
)
res.raise_for_status()
monitor = res.json()["monitor"]

Changelog

현재 API는 /api/v1/로 버전이 경로에 명시되어 있습니다. 기존 요청을 깨는 변경(breaking change)이 필요해지면/api/v2/처럼 새 버전 경로를 추가하고 v1은 일정 기간 함께 유지할 예정입니다. 아직 초기 단계라 별도 변경 이력 문서는 운영하지 않으며, 주요 변경사항이 생기면 이 섹션에 기록하겠습니다.

Support

API 사용 중 궁금한 점이나 버그 제보는 taewok0205@gmail.com으로 보내주세요. 재현 가능한 요청(엔드포인트, 요청 바디, 받은 응답)을 함께 보내주시면 더 빠르게 확인할 수 있습니다. 별도의 서비스 상태 페이지는 아직 제공하지 않으며, 장애가 발생하면 등록된 이메일로 안내드립니다.