API 文档
基础地址 https://yhfdghkh6544.com/v1。所有接口都是标准 HTTP,参数可以放在查询串(GET)或 JSON body(POST)里。
快速开始
- 免费注册,在控制台的「API Key」页面创建一个 Key。
- 把 Key 放进
X-Access-Key请求头,调用截图接口:
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 的明文只在创建时显示一次,请妥善保存。泄露后可以在控制台停用。
截图接口
/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 二选一。 |
| 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 秒,适合 SPA)。 |
| delay | integer | 0 | 等待完成后再延迟的毫秒数,0–10000。用于等动画或懒加载组件。 |
| block_ads | boolean | true | 拦截广告与跟踪域名的请求。 |
| block_cookie_banners | boolean | true | 隐藏 Cookie 同意弹窗。 |
| 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 返回文件地址和元数据。 |
PDF 接口
/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
想把截图地址直接写进 <img src>,又不想暴露 API Key?在你自己的服务端用 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="网页预览" />缓存
参数完全相同的请求会命中缓存(省略参数和显式传默认值视为相同;鉴权参数和缓存参数不参与比较)。命中时二进制模式返回 302 跳转到文件地址,并带 Cache-Control,浏览器会自动跟随跳转并缓存;JSON 模式返回 cache_hit: true。
命中缓存不计费。同一参数的并发请求只会渲染一次。
限额与计费
- 只对成功(2xx)且未命中缓存的请求计费:截图 1 次,PDF 2 次。
- 超时、渲染失败、目标站报错、参数错误、鉴权失败都不扣额度。
- 额度按自然月(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,或 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 没有匹配到元素 | 否 |
| 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}一次提交 1–100 个网址,接口立即返回任务 ID(HTTP 202),截图在后台逐个完成。适合批量截图、不想一直等待连接的场景。可以轮询查询接口,也可以填 webhook_url,全部完成后我们会主动通知你。鉴权方式和同步接口相同(只支持请求头或 access_key 参数,不支持签名 URL)。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| type | enum | screenshot | screenshot(截图)或 pdf。 |
| urls | string[] | — | 要处理的网址,1–100 个。任何一个不合法时整个请求返回 400,message 指出是第几个。 |
| options | object | {} | 所有网址共用的参数,和同步接口的参数相同(如 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(全部完成,无论成功还是失败)。每个网址的状态是 pending、running、succeeded 或 failed;成功的在 result 里给出文件地址,失败的在 error 里给出错误码(和同步接口的错误码一致)。
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" }
}
]
}- 计费规则和同步接口相同:每个成功的网址截图计 1 次、PDF 计 2 次,命中缓存和失败的不扣额度。
- 提交时额度已经用完会直接返回
quota_exceeded;处理过程中额度用完,剩下的网址会以quota_exceeded失败。 - 每个账号同时最多 5 个未完成的批量任务,超过返回
too_many_batches。 - 结果文件的有效期由
options.cache_ttl决定(默认 1 天),请及时下载保存。 - 查询结果和回调里的
webhook.url只显示到路径,不回显查询串。
完成回调
填了 webhook_url 时,任务全部完成后我们会向它发送一个 POST,请求体是 JSON:{ "event": "batch.completed", "batch": { … } },其中 batch 和查询接口的返回完全相同。请求头:
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 对应的签名 secret(控制台 API Key 页面查看)- 请在 10 秒内返回 2xx,否则视为失败。失败后会在 10 秒、1 分钟、5 分钟后各重试一次(共最多 4 次)。
- 极少数情况下同一个任务可能收到重复通知,请按
batch.id去重。 - 验签时用收到的原始字节计算,并检查
t与当前时间相差不超过几分钟,防止重放。 - 回调地址必须是公网可访问的 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无需鉴权,返回节点状态和浏览器池状态(总数、忙碌、空闲)。