API 문서
기본 주소는 https://yhfdghkh6544.com/v1입니다. 모든 API는 표준 HTTP를 사용하며, 매개변수는 쿼리 문자열(GET) 또는 JSON 본문(POST)으로 전달할 수 있습니다.
빠른 시작
- 무료 가입 후 콘솔의 ‘API Key’ 페이지에서 API Key를 생성하세요.
X-Access-Key요청 헤더에 API Key를 넣어 스크린샷 API를 호출하세요.
curl "https://yhfdghkh6544.com/v1/screenshot?url=https://example.com&full_page=true" \
-H "X-Access-Key: $SNAPI_KEY" \
-o screenshot.png인증
모든 요청에 API Key가 필요합니다. 다음 중 한 가지 방식으로 전달할 수 있습니다.
- 요청 헤더
X-Access-Key: snp_…(권장, 서버 측 호출용) - 쿼리 매개변수
access_key=snp_… - 서명된 URL(웹페이지에 주소를 삽입할 때 사용하며, secret이 노출되지 않습니다)
API Key 원문은 생성 시 한 번만 표시되므로 안전하게 보관하세요. 유출된 경우 콘솔에서 비활성화할 수 있습니다.
스크린샷 API
/v1/screenshot/v1/screenshot스크린샷 전용 매개변수:
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
| format | enum | png | png, jpeg 또는 webp입니다. |
| device_scale_factor | number | 1 | 배율입니다. 범위는 1–3이며, 2로 설정하면 Retina 수준의 선명도를 얻을 수 있지만 파일 크기는 약 4배가 됩니다. |
| quality | integer | 80 | JPEG / WebP 품질입니다. 범위는 1–100입니다. |
| full_page | boolean | false | 전체 페이지를 캡처합니다. 화면 단위로 스크롤하여 지연 로딩을 유도하며, 최대 높이는 20,000픽셀입니다. |
| selector | string | — | 이 CSS 선택자와 일치하는 첫 번째 요소만 캡처합니다. full_page와 함께 사용할 수 없습니다. |
스크린샷과 PDF의 공통 매개변수:
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
| url | string | — | 캡처할 웹페이지 주소입니다. http 또는 https를 사용하며, html과 둘 중 하나만 지정할 수 있습니다. URL의 # 프래그먼트가 유지되므로 해시 라우팅을 사용하는 단일 페이지 앱(예: https://app.com/#/report/1)도 해당 페이지를 바로 캡처할 수 있습니다. |
| html | string | — | HTML을 직접 전달합니다(최대 2 MB). POST만 지원합니다. |
| viewport_width | integer | 1280 | 뷰포트 너비입니다. 범위는 320–3840이며, 768 미만이면 모바일 기기로 렌더링합니다. |
| viewport_height | integer | 800 | 뷰포트 높이입니다. 범위는 200–3840입니다. |
| wait_until | enum | networkidle | 대기 방식입니다. domcontentloaded(가장 빠름, 정적 페이지에 적합), load, networkidle(페이지를 여는 시점부터 모든 요청을 추적하며, 페이지 로드 후 진행 중인 요청이 없는 상태가 500밀리초 동안 유지되면 캡처합니다. 최대 8초 더 대기하며, 단일 페이지 앱에 적합합니다). |
| wait_for_selector | string | — | 페이지 로드 후 이 CSS 선택자와 일치하는 요소가 나타나고 화면에 표시될 때까지 최대 10초 대기한 뒤 캡처합니다. 시간 내에 표시되지 않으면 selector_not_found를 반환하며 과금하지 않습니다. 네트워크 요청이 아닌 타이머나 애니메이션으로 콘텐츠를 지연 렌더링하는 단일 페이지 앱(예: .chart-ready)에 적합합니다. |
| delay | integer | 0 | 대기가 끝난 뒤 추가로 기다릴 시간입니다. 밀리초 단위로 0–10000을 지정합니다. 애니메이션이나 지연 로딩 컴포넌트를 기다릴 때 사용합니다. |
| block_ads | boolean | true | 광고 및 추적 도메인으로의 요청을 차단합니다. |
| block_cookie_banners | boolean | true | 쿠키 동의 팝업을 숨깁니다. |
| dark_mode | boolean | false | prefers-color-scheme: dark를 에뮬레이션합니다. |
| hide_selectors | string | — | 숨길 요소의 CSS 선택자 목록입니다. 예: .ad,#popup. |
| cache | boolean | true | 캐시 조회 여부입니다. false로 설정하면 강제로 다시 렌더링하며, 결과는 캐시에 저장됩니다. |
| cache_ttl | integer | 86400 | 캐시 유효 기간입니다. 초 단위로 60–2592000을 지정합니다. |
| response_type | enum | binary | binary는 파일을 직접 반환하고, json은 파일 주소와 메타데이터를 반환합니다. |
단일 페이지 앱(React, Vue 등) 캡처:실제 브라우저에서 JavaScript를 실행한 뒤 캡처합니다. 기본값인 networkidle은 페이지의 데이터 요청이 끝날 때까지 기다립니다. 요청 완료 후 타이머나 애니메이션으로 콘텐츠를 렌더링한다면 wait_for_selector로 콘텐츠가 준비되었음을 나타내는 요소를 지정하세요. 적절한 요소가 없다면 delay로 대기 시간을 추가하세요.
PDF API
/v1/pdf/v1/pdfPDF는 요청당 한도에서 2회분을 차감합니다. 인쇄 전에 페이지를 한 번 스크롤하여 지연 로딩 콘텐츠를 불러옵니다. 공통 매개변수 외에 다음 매개변수를 지원합니다.
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
| paper | enum | a4 | 용지 크기입니다. a4 또는 letter를 사용합니다. 페이지에 @page 규칙이 있으면 해당 설정을 우선 적용합니다. |
| print_background | boolean | true | 배경색과 배경 이미지를 인쇄합니다. |
| pdf_media | enum | print는 인쇄용 스타일을, screen은 화면용 스타일을 사용합니다(화면에 보이는 그대로 출력). |
curl -X POST "https://yhfdghkh6544.com/v1/pdf" \
-H "X-Access-Key: $SNAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"html": "<h1>분기 보고서</h1><p>본문…</p>", "paper": "a4"}' \
-o report.pdf응답
기본값(response_type=binary)은 파일을 직접 반환하며, Content-Type은 해당 파일 형식으로 설정됩니다. 응답 헤더:
X-Request-Id: 요청 ID입니다. 문제 해결을 위해 문의 시 함께 알려주세요.X-Snapi-Cache:HIT또는MISSX-Snapi-Truncated: true: 전체 페이지가 20,000픽셀을 초과하여 잘렸음을 나타냅니다.X-Snapi-Format-Changed: webp: PNG가 5 MB를 초과하여 WebP로 자동 변환되었음을 나타냅니다(명시적으로 format=png를 지정하면 변환하지 않습니다).
response_type=json으로 설정하면 파일 주소와 메타데이터를 반환합니다.
curl -X POST "https://yhfdghkh6544.com/v1/screenshot" \
-H "X-Access-Key: $SNAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "response_type": "json"}'
# 응답
{
"url": "https://yhfdghkh6544.com/i/3f/3f9c….png",
"cache_hit": false,
"duration_ms": 1284,
"format": "png",
"width": 1280,
"height": 800,
"request_id": "req_01j…"
}서명된 URL
API Key를 노출하지 않고 스크린샷 주소를 <img src>에 직접 넣고 싶으신가요? 자체 서버에서 API Key에 연결된 secret으로 서명을 계산하세요. secret은 콘솔의 API Key 페이지에서 확인할 수 있습니다.
canonical = signature를 제외한 모든 매개변수를 이름의 사전순으로 정렬합니다.
각 항목을 k=urlencode(v) 형식으로 구성하고(RFC 3986, 공백은 %20로 인코딩), &로 연결합니다.
signature = hex(HMAC-SHA256(secret, canonical))서명이 올바르면 인증된 요청으로 처리합니다. 매개변수가 하나라도 바뀌면(예: url 변경) 서명이 무효화됩니다. 선택적 매개변수인 expires(Unix 초)도 서명에 포함되며, 만료 후에는 signature_expired를 반환합니다. 캐시를 새로 고치려면 의미 없는 매개변수(예: v=2)를 변경한 뒤 다시 서명하면 됩니다. 서명된 URL은 GET만 지원합니다.
import crypto from "node:crypto";
// RFC 3986 인코딩(공백은 %20)을 사용하며, 서버 측 규칙과 일치해야 합니다.
const enc = (v) =>
encodeURIComponent(v).replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase());
function signedUrl(params, accessKey, secret) {
const all = { ...params, access_key: accessKey };
const canonical = Object.keys(all).sort().map((k) => `${k}=${enc(String(all[k]))}`).join("&");
const signature = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
return `https://yhfdghkh6544.com/v1/screenshot?${canonical}&signature=${signature}`;
}
const src = signedUrl({ url: "https://example.com", viewport_width: 1200 }, KEY, SECRET);
// <img src={src} alt="웹페이지 미리보기" />캐시
매개변수가 완전히 같은 요청은 캐시를 사용합니다. 매개변수를 생략한 경우와 기본값을 명시한 경우는 동일하게 처리하며, 인증 및 캐시 매개변수는 비교에서 제외합니다. 캐시 적중 시 바이너리 모드에서는 Cache-Control과 함께 302 리디렉션으로 파일 주소를 반환합니다. 브라우저는 자동으로 리디렉션을 따라가 파일을 캐시합니다. JSON 모드에서는 cache_hit: true를 반환합니다.
캐시 적중 시 과금하지 않습니다.매개변수가 같은 동시 요청은 한 번만 렌더링합니다.
한도 및 과금
- 성공(2xx)하고 캐시에 적중하지 않은 요청만 과금합니다. 스크린샷은 1회분, PDF는 2회분을 차감합니다.
- 시간 초과, 렌더링 실패, 대상 사이트 오류, 매개변수 오류, 인증 실패 시에는 한도를 차감하지 않습니다.
- 한도는 매월 1일(UTC)에 초기화됩니다. 무료 플랜은 월 100회를 제공하며, 모두 사용하면 quota_exceeded를 반환합니다.
- 초당 요청 수 또는 동시 요청 수를 초과하면 429를 반환합니다. 응답 헤더 Retry-After에 권장 대기 시간(초)이 표시됩니다.
- 렌더링은 요청당 최대 30초이며, 페이지당 다운로드 한도는 50 MB, 출력 파일 한도는 50 MB입니다.
플랜별 자세한 한도는 요금 안내에서 확인하세요.
오류 코드
모든 오류 응답은 JSON 형식입니다.
{
"error": {
"code": "render_failed",
"message": "target responded with an error status",
"upstream_status": 404,
"request_id": "req_01j…"
}
}| HTTP | code | 의미 | 과금 여부 |
|---|---|---|---|
| 400 | invalid_params | 매개변수가 누락되었거나 범위를 벗어났습니다. message에 해당 매개변수가 표시됩니다. | 아니요 |
| 400 | html_too_large | html이 2 MB를 초과했습니다. | 아니요 |
| 401 | unauthorized | API Key가 없거나, 유효하지 않거나 비활성화되었습니다. | 아니요 |
| 401 | signature_invalid | 서명이 일치하지 않습니다(매개변수 변경 또는 잘못된 secret). | 아니요 |
| 401 | signature_expired | 서명된 URL의 expires 시간이 지났습니다. | 아니요 |
| 403 | url_blocked | 접근이 허용되지 않는 대상 주소입니다(예: 내부 네트워크 주소, localhost). | 아니요 |
| 408 | render_timeout | 렌더링 시간이 30초를 초과했습니다. | 아니요 |
| 422 | render_failed | 대상 사이트가 4xx/5xx를 반환했거나, DNS 또는 인증서 오류가 발생했습니다. upstream_status가 포함됩니다. | 아니요 |
| 422 | selector_not_found | selector와 일치하는 요소가 없거나 wait_for_selector의 대기 시간이 초과되었습니다. | 아니요 |
| 422 | too_many_redirects | 리디렉션이 5회를 초과했습니다. | 아니요 |
| 429 | rate_limited | 초당 요청 수를 초과했습니다. Retry-After에 따라 재시도하세요. | 아니요 |
| 429 | concurrency_exceeded | 동시 요청 수를 초과했습니다. | 아니요 |
| 429 | quota_exceeded | 이번 달 한도를 모두 사용했습니다(무료 플랜). | 아니요 |
| 429 | too_many_batches | 완료되지 않은 일괄 작업이 동시에 5개를 초과했습니다. | 아니요 |
| 500 | internal | 서버 오류입니다. request_id를 포함하여 문의해 주세요. | 아니요 |
비동기 일괄 처리
/v1/batch/v1/batch/{id}URL 1–100개를 한 번에 제출하면 API가 즉시 작업 ID(HTTP 202)를 반환하고, 백그라운드에서 순차적으로 캡처합니다. 여러 페이지를 캡처하면서 연결을 계속 유지하고 싶지 않을 때 적합합니다. 조회 API를 폴링하거나 webhook_url을 지정하면 모든 작업이 완료된 후 알림을 받을 수 있습니다. 인증 방식은 동기 API와 같지만, 요청 헤더 또는 access_key 매개변수만 지원하며 서명된 URL은 지원하지 않습니다.
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
| type | enum | screenshot | screenshot(스크린샷) 또는 pdf입니다. |
| urls | string[] | — | 처리할 URL 목록입니다. 1–100개를 지정할 수 있습니다. 하나라도 유효하지 않으면 전체 요청에 400을 반환하며, message에 몇 번째 URL인지 표시합니다. |
| options | object | {} | 모든 URL에 공통으로 적용할 매개변수입니다. 동기 API의 매개변수와 동일하며(예: full_page, format, viewport_width, cache_ttl), url, html, response_type는 포함할 수 없습니다. |
| webhook_url | string | — | 선택 사항입니다. 모든 작업이 완료되면 알림을 보낼 주소입니다. ‘완료 콜백’을 참고하세요. |
curl -X POST "https://yhfdghkh6544.com/v1/batch" \
-H "X-Access-Key: $SNAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "screenshot",
"urls": ["https://example.com", "https://example.org"],
"options": { "full_page": true, "format": "webp" },
"webhook_url": "https://your-app.com/hooks/snapi"
}'
# 202 응답
{
"id": "bat_01j…",
"type": "screenshot",
"status": "queued",
"total": 2, "succeeded": 0, "failed": 0,
"created_at": "2026-10-18T09:30:00Z", "completed_at": null,
"webhook": { "url": "https://your-app.com/hooks/snapi", "status": "none", "attempts": 0 },
"items": [
{ "index": 0, "url": "https://example.com", "status": "pending", "result": null, "error": null },
{ "index": 1, "url": "https://example.org", "status": "pending", "result": null, "error": null }
]
}진행 상황 조회: 작업 상태 status는 queued(대기 중), running(처리 중), completed(성공 여부와 관계없이 모두 완료) 순으로 바뀝니다. 각 URL의 상태는 pending, running, succeeded 또는 failed입니다. 성공하면 result에 파일 주소가, 실패하면 error에 오류 코드가 표시됩니다. 오류 코드는 동기 API와 동일합니다.
curl "https://yhfdghkh6544.com/v1/batch/bat_01j…" -H "X-Access-Key: $SNAPI_KEY"
{
"id": "bat_01j…",
"status": "completed",
"total": 2, "succeeded": 1, "failed": 1,
"completed_at": "2026-10-18T09:30:12Z",
"items": [
{
"index": 0, "url": "https://example.com", "status": "succeeded", "duration_ms": 1830,
"result": { "url": "https://yhfdghkh6544.com/i/3f/3f9c….webp", "mime": "image/webp", "width": 1280, "height": 2410, "cache_hit": false },
"error": null
},
{
"index": 1, "url": "https://example.org", "status": "failed", "duration_ms": 640,
"result": null,
"error": { "code": "render_failed", "message": "target responded with an error status" }
}
]
}- 과금 규칙은 동기 API와 같습니다. 성공한 URL마다 스크린샷은 1회분, PDF는 2회분을 차감하며, 캐시 적중 및 실패 시에는 차감하지 않습니다.
- 제출 시 한도를 이미 모두 사용한 경우 즉시
quota_exceeded를 반환합니다. 처리 도중 한도를 모두 사용하면 나머지 URL은quota_exceeded로 실패 처리됩니다. - 계정당 완료되지 않은 일괄 작업은 동시에 최대 5개까지 허용되며, 초과 시
too_many_batches를 반환합니다. - 결과 파일의 유효 기간은
options.cache_ttl로 결정됩니다(기본값 1일). 만료 전에 다운로드하여 보관하세요. - 조회 결과와 콜백의
webhook.url에는 경로까지만 표시되며, 쿼리 문자열은 포함되지 않습니다.
완료 콜백
webhook_url을 지정하면 모든 작업이 완료된 후 해당 주소로 POST 요청을 보냅니다. 요청 본문은 JSON 형식의 { "event": "batch.completed", "batch": { … } }이며, batch는 조회 API의 응답과 완전히 동일합니다. 요청 헤더:
X-Snapi-Signature: t=1760779812,v1=5d3f…:t는 전송 시각(Unix 초),v1은 서명입니다.X-Snapi-Event: batch.completed,X-Snapi-Delivery-Attempt: 1(전송 시도 횟수)
v1 = hex(HMAC-SHA256(secret, t + "." + 원본 요청 본문))
secret은 작업 제출 시 사용한 API Key의 서명 시크릿입니다(콘솔의 API Key 페이지에서 확인).- 10초 이내에 2xx를 반환해야 합니다. 그렇지 않으면 실패로 처리됩니다. 실패 시 10초, 1분, 5분 후에 한 번씩 재시도합니다(최대 총 4회 시도).
- 드물게 같은 작업에 대한 알림이 중복 수신될 수 있습니다.
batch.id를 기준으로 중복을 제거하세요. - 서명을 검증할 때는 수신한 원본 바이트로 계산하고, 재전송 공격을 방지하기 위해
t와 현재 시간의 차이가 몇 분 이내인지 확인하세요. - 콜백 URL은 외부에서 접근할 수 있는 http(s) 주소여야 하며, 내부 네트워크를 가리킬 수 없습니다.
import crypto from "node:crypto";
// rawBody는 수신한 원본 바이트여야 합니다. JSON.parse 후 다시 직렬화하면 안 됩니다.
function verifySnapiWebhook(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // 재전송 공격 방지
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const got = Buffer.from(parts.v1 ?? "");
return got.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), got);
}
// verifySnapiWebhook(req.rawBody, req.headers["x-snapi-signature"], SNAPI_SECRET)사용량 조회
/v1/usagecurl "https://yhfdghkh6544.com/v1/usage" -H "X-Access-Key: $SNAPI_KEY"
{ "month": "202610", "used": 37, "limit": 100, "remaining": 63, "overage": 0, "resets_at": "2026-11-01T00:00:00Z", ... }상태 확인
/v1/health인증 없이 노드 상태와 브라우저 풀 상태(전체, 사용 중, 유휴)를 반환합니다.