@pang3fan-creator/analytics-kit
v0.7.0
Published
Typed clients for querying Google Search Console, Google Analytics 4, Microsoft Clarity, Bing Webmaster Tools, and Ahrefs. Runtime-agnostic (Node.js / Cloudflare Workers / edge).
Maintainers
Readme
analytics-kit
类型安全的网站分析数据查询客户端,用于在 Node.js / Cloudflare Workers / Deno / Bun 中查询五个平台的数据:
- Google Search Console(搜索表现:点击/展示/排名/CTR)
- Google Analytics 4(runReport 报表)
- Microsoft Clarity(仪表盘指标 + 实验性会话列表)
- Bing Webmaster Tools(排名与流量 / 关键词 / 页面)
- Ahrefs(免费公开端点:域名权重 DR 查询)
零 Node 专有依赖(仅 jose),基于全局 fetch,可在 Node.js 18+ / Cloudflare Workers / Deno / Bun 中运行。刻意不使用 googleapis 等官方包,因为它们依赖的 google-auth-library/gaxios 栈无法可靠运行在 Workers 等 edge 运行时。
平台与入口
| 平台 | 客户端 | 主要 API | 必需配置 |
|---|---|---|---|
| Google Search Console | createGscClient | searchAnalytics / searchAnalyticsAll / inspectUrl / inspectUrls | GSC_SITE_URL + Google 服务账号 |
| Google Analytics 4 | createGa4Client | runReport / runReportAll / toRowObjects | GA4_PROPERTY_ID + Google 服务账号 |
| Bing Webmaster Tools | createBingClient | getRankAndTrafficStats / getQueryStats / getPageStats | BING_API_KEY + BING_SITE_URL |
| Ahrefs | createAhrefsClient | getDomainRating / getLimitsAndUsage | AHREFS_API_KEY |
| Microsoft Clarity | createClarityClient | getLiveInsights / getRawLiveInsights / listSessions | CLARITY_API_TOKEN |
目录
安装
# 从 npm 安装(推荐)
pnpm add @pang3fan-creator/analytics-kit
# 需要跟仓库里尚未发布的改动时:
pnpm add git+ssh://[email protected]/pang3fan-creator/analytics-kit.git前置配置
Google(GSC + GA4 共用一套服务账号)
- 在 Google Cloud Console 创建项目,启用两个 API:
- Search Console API
- Google Analytics Data API
- 创建服务账号(IAM → Service Accounts),生成 JSON 密钥并下载。密钥里的
client_email对应GOOGLE_SERVICE_ACCOUNT_EMAIL,private_key对应GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY。 - 授权访问数据(最常见踩坑点,漏掉必 403):
- GSC:设置 → 用户和权限 → 添加服务账号邮箱为用户。仅用
searchAnalytics时 Restricted 即可;使用inspectUrl(URL Inspection API)必须是 Owner 或 Full,否则会 403 - GA4:管理 → 属性访问管理 → 添加服务账号邮箱为 Viewer
- GSC:设置 → 用户和权限 → 添加服务账号邮箱为用户。仅用
Microsoft Clarity
- Clarity 项目 → Settings → Data Export → Generate new API Token(仅项目管理员可操作)
- Token 与单个项目绑定;每个项目需要各自的 token
Bing Webmaster Tools
- Bing Webmaster → 右上角 Settings → API Access → 接受条款 → API Key → Generate API Key
- API Key 按账号生成(不是站点),一个 Key 可用于账号下所有已验证站点;删除后必须更新所有引用它的项目
- 站点标识为 URL-prefix 形式(如
https://example.com/),Bing 不支持 Google 的sc-domain:网域资源语法
Ahrefs(当前仅免费公开端点)
- Ahrefs → Account settings → API keys → 生成 APIv3 Key(无需付费订阅;公共端点免费、消耗 0 units)
- 注意:
site-audit/*、keywords-explorer/*等订阅端点在公共 Key 下会 401(返["Error","Unauthorized"]),仅subscription-info/limits-and-usage在付费计划下可用;本库目前只实现免费能力,付费端点待升级 Lite+ 后按需扩展 - 域名权重数据公开显示需标注 "Domain Rating by Ahrefs"(许可条款)
环境变量
| 变量 | 说明 |
|---|---|
| GOOGLE_SERVICE_ACCOUNT_EMAIL | 服务账号邮箱(client_email) |
| GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY | 服务账号私钥(private_key),单行 \n 转义自动还原 |
| GSC_SITE_URL | GSC 属性:https://example.com/ 或 sc-domain:example.com |
| GA4_PROPERTY_ID | GA4 纯数字 Property ID(不是 G- 开头的数据流 ID) |
| CLARITY_API_TOKEN | Clarity 项目 API Token |
| BING_API_KEY | Bing Webmaster API Key(设置 → API Access → API Key,账号级) |
| BING_SITE_URL | Bing 站点标识,URL-prefix 形式,如 https://example.com/ |
| AHREFS_API_KEY | Ahrefs APIv3 Key(Account settings → API keys;免费公共端点无需订阅) |
所有变量都可以不设——改为在创建客户端时显式传参(Cloudflare Workers 无 process.env 时用这种方式)。
快速开始
以 GSC 为例(其他平台替换工厂函数即可):
import { createGscClient } from '@pang3fan-creator/analytics-kit'
const gsc = createGscClient() // 从 process.env 读取 GSC_SITE_URL + Google 服务账号
const result = await gsc.searchAnalytics({
startDate: '2026-08-01',
endDate: '2026-08-21',
dimensions: ['query', 'page'],
rowLimit: 1_000,
})所有客户端都支持显式传参(Workers 等无 process.env 的运行时必须这样用),也支持 timeoutMs 和 fetchImpl:
// Cloudflare Workers 等运行时把 bindings 作为 env 传进来
const gsc = createGscClient({
siteUrl: env.GSC_SITE_URL,
clientEmail: env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
privateKey: env.GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY,
timeoutMs: 30_000,
})timeoutMs 不传则不加超时;超时以 AnalyticsKitError 抛出,kind: 'timeout'。
显式传参速查
| 客户端 | 主要参数 |
|---|---|
| createGscClient | siteUrl / clientEmail / privateKey |
| createGa4Client | propertyId / clientEmail / privateKey |
| createBingClient | apiKey / siteUrl |
| createAhrefsClient | apiKey |
| createClarityClient | apiToken |
所有客户端还都接受 env(自定义环境变量记录)、fetchImpl(自定义 fetch)和 timeoutMs。GSC / GA4 还接受 auth(自定义 GoogleTokenProvider,例如复用业务侧已有的 access token)。
平台用法
Google Search Console
import { createGscClient } from '@pang3fan-creator/analytics-kit'
const gsc = createGscClient() // 自动读环境变量
const result = await gsc.searchAnalytics({
startDate: '2026-08-01',
endDate: '2026-08-21',
dimensions: ['query', 'page'],
rowLimit: 1000,
})
// 自动分页拉全量(内部循环 startRow)
const allRows = await gsc.searchAnalyticsAll({
startDate: '2026-08-01',
endDate: '2026-08-21',
dimensions: ['query'],
}, { maxRows: 50_000 })结果固定按 clicks 降序;keys[] 的顺序与请求的 dimensions 一致。
URL 检查(单个 URL 的索引裁决)
searchAnalytics 之外,GSC 客户端还封装了 URL Inspection API,用于查询单个 URL 在 Google 索引中的状态:
const inspection = await gsc.inspectUrl({
inspectionUrl: 'https://example.com/blog/post',
siteUrl: 'sc-domain:example.com', // 可选,默认取客户端的 GSC_SITE_URL
languageCode: 'en-US', // 可选,影响 coverageState 的文案语言
})
const status = inspection.indexStatusResult
status.verdict // 'PASS' | 'NEUTRAL' | 'FAIL' | 'PARTIAL' | 'VERDICT_UNSPECIFIED'
status.coverageState // 如 'Crawled - currently not indexed'(本地化文案;缺失时字段不存在)
status.googleCanonical // Google 选定的 canonical(未收录时可能缺省)
status.userCanonical // 页面声明的 canonical(未声明时缺省)
status.lastCrawlTime // 最后一次成功抓取时间(从未抓取时缺省)
status.robotsTxtState // 'ALLOWED' | 'DISALLOWED' | ...
status.indexingState // 'INDEXING_ALLOWED' | 'BLOCKED_BY_META_TAG' | ...
status.pageFetchState // 'SUCCESSFUL' | 'SOFT_404' | 'NOT_FOUND' | ...
status.referringUrls // 指向该 URL 的链接(缺省为 [])
status.sitemap // 包含该 URL 的 sitemap(缺省为 [])
inspection.raw // 完整 inspectionResult,含可选的 ampResult / richResultsResult / mobileUsabilityResult
// 批量:先校验全部 URL,再顺序请求;任一失败立即抛 AnalyticsKitError
const results = await gsc.inspectUrls(
[url1, url2, url3, url4],
{ siteUrl: 'sc-domain:example.com', languageCode: 'en-US' },
)本客户端只归一化 indexStatusResult;AMP、Rich Results、Mobile Usability 等分析结果不丢弃,原样放在 inspection.raw 里,需要时直接取。
使用注意:
- 返回的是 Google 索引中的版本,不是实时抓取;
lastCrawlTime可能落后于当前页面。 coverageState是本地化展示文案,不适合做精确字符串匹配;判断是否收录请用verdict,要稳定文案请固定languageCode: 'en-US'。inspectUrls会先校验批量里的每一个 URL 再发第一个请求,坏 URL 不会白白消耗配额;请求顺序执行,任一失败立即抛错,已成功的前序结果不会返回。需要部分成功请自行循环inspectUrl并捕获错误。- 配额为 2,000 次/天、600 次/分钟(按资源);服务账号在该资源上必须是 Owner 或 Full,Restricted 会 403。
- 这个 API 只能逐个 URL 查询,不能列出所有未收录 URL,也拿不到 GSC「网页索引编制」报告的桶计数——那些只能在 GSC 界面导出。
Google Analytics 4
import { createGa4Client, toRowObjects } from '@pang3fan-creator/analytics-kit'
const ga4 = createGa4Client()
const report = await ga4.runReport({
dateRanges: [{ startDate: '29daysAgo', endDate: 'today' }],
dimensions: [{ name: 'country' }],
metrics: [{ name: 'sessions' }, { name: 'activeUsers' }],
returnPropertyQuota: true,
})
// 行转对象:{ country: 'China', sessions: '123', ... }
const rows = toRowObjects(report)
// 自动分页(按 rowCount 循环 offset)
const all = await ga4.runReportAll({
dateRanges: [{ startDate: '90daysAgo', endDate: 'today' }],
dimensions: [{ name: 'pagePath' }],
metrics: [{ name: 'screenPageViews' }],
})GA4 与 GSC 共享同一服务账号凭据时,access token 自动复用缓存:两个客户端默认都以合并后的只读 scopes(webmasters.readonly + analytics.readonly)请求 token,同凭据每小时只换一次。
Bing Webmaster Tools
import { createBingClient } from '@pang3fan-creator/analytics-kit'
const bing = createBingClient() // 自动读环境变量
// 排名与流量(按天,近一个月)
const traffic = await bing.getRankAndTrafficStats()
// [{ date: '2026-08-21', clicks: 16, impressions: 202 }, ...]
// 关键词明细(官方扁平结构:每行一个查询词)
const queries = await bing.getQueryStats()
// [{ query: 'schedule maker', clicks: 15, impressions: 100, avgClickPosition: 18, avgImpressionPosition: 17, date: '2011-09-16' }, ...]
// 页面流量(官方 Query 字段承载页面 URL)
const pages = await bing.getPageStats()Bing 走官方 JSON/HTTP 协议(其 SOAP/POX 协议已于 2026-08-31 退役,本库只使用 JSON 端点)。单个 API Key 可查询账号下所有已验证站点,按 BING_SITE_URL 区分目标站。注意:
- 位置字段粒度:
avgClickPosition/avgImpressionPosition与 GSC 的position不同,是 Bing 原始刻度(如18对应 UI 显示的1.8),展示前请自行 ÷10 - 日期格式:微软序列化为
/Date(毫秒数±时区)/,本库已按带时区的本地日历日期归一化为YYYY-MM-DD(不会因 UTC 截断出现跨日偏移) - 日期范围:
GetQueryStats/GetPageStats不支持服务端日期参数,返回全量数据,需要窗口时请客户端过滤 - 误传
sc-domain:形式的站点标识会抛出带说明的AnalyticsKitConfigError
Ahrefs
import { createAhrefsClient } from '@pang3fan-creator/analytics-kit'
const ahrefs = createAhrefsClient() // 自动读环境变量
// 任意域名/URL 的 Domain Rating(0 units,免费)
const dr = await ahrefs.getDomainRating('tanship.app')
console.log(dr.domainRating) // 例如 22
console.log(dr.license) // 署名条款 URL(公开显示时需标注 "Domain Rating by Ahrefs")
// 查看当前 Key 的计划/额度(0 units;公共 Key 会 401,需 Lite+ 计划)
const limits = await ahrefs.getLimitsAndUsage()
console.log(limits.subscription, limits.unitsUsageApiKey, limits.unitsLimitApiKey, limits.usageResetDate)当前覆盖官方免费公开端点;site-audit / keywords-explorer 等订阅端点消耗 API units(50/次起),需要升级到 Lite 及以上后才可用(届时再按需扩展方法)。
Microsoft Clarity
import { createClarityClient } from '@pang3fan-creator/analytics-kit'
const clarity = createClarityClient()
// 官方 Export API:仅最近 1-3 天,10 次/项目/天
const insights = await clarity.getLiveInsights({
numOfDays: 3,
dimensions: ['Device', 'Country/Region'], // 最多 3 个
})
for (const metric of insights) {
console.log(metric.metricName) // 如 "Traffic"、"RageClickCount"
for (const row of metric.rows) {
console.log(row.dimensions) // { Device: 'Mobile', ... }
console.log(row.values) // 计数字符串已转为数字
}
}归一化处理了官方响应的已知陷阱:
- 计数字段是 JSON 字符串 → 已转为 number
- 维度回显键名大小写不稳定(如 URL 回显为
Url)→ 统一映射回请求时的维度名 - 注意:
totalSessionCount包含 bot 会话(totalBotSessionCount),人类会话需自行相减;摩擦类指标没有原始计数,只有发生率与subTotal/sessionsCount - 需要原始结构时改用
getRawLiveInsights()
实验性:会话列表(走未文档化的 clarity.microsoft.com/mcp/recordings/sample 端点,即官方 MCP 服务器所用接口;无 SLA,可能随官方变更失效):
const sessions = await clarity.listSessions({
start: new Date('2026-08-20T00:00:00Z'),
end: new Date('2026-08-21T00:00:00Z'),
sortBy: 'SessionDuration_DESC', // 可选,默认 SessionStart_DESC
count: 100, // 上限 250
filters: {
deviceType: ['Mobile'],
rageClickPresent: true,
deadClickPresent: true,
javascriptErrors: [''],
},
})响应结构未文档化,返回解析后的 JSON 原样值。
Cloudflare Workers 中使用
Workers 没有 process.env,创建客户端时显式传配置即可:
export default {
async fetch(request: Request, env: Env) {
const gsc = createGscClient({
siteUrl: env.GSC_SITE_URL,
clientEmail: env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
privateKey: env.GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY,
})
// ...
},
}错误处理
所有失败都抛出统一的 AnalyticsKitError——HTTP 错误、网络失败、超时、外部中断和响应解析失败都不会漏网,调用方只需 instanceof 一种判断:
import { AnalyticsKitError } from '@pang3fan-creator/analytics-kit'
try {
await gsc.searchAnalytics({ ... })
} catch (error) {
if (error instanceof AnalyticsKitError) {
error.platform // 'gsc' | 'ga4' | 'clarity' | 'bing' | 'ahrefs' | 'google-auth'
error.kind // 'http' | 'network' | 'timeout' | 'abort' | 'parse'
error.status // HTTP 状态码(仅 kind === 'http' 时存在)
error.body // 服务端错误体(仅 HTTP 错误)
error.url // 请求地址(Bing 这类把 apikey 放在 query 上的平台已脱敏)
error.cause // 原始错误(仅非 HTTP 错误)
}
}已知边界:本库不做自动重试/退避(由调用方决定策略)、不对响应做运行时 schema 校验(类型安全覆盖输入与文档化契约);配置类问题抛 AnalyticsKitConfigError。
常见 HTTP 状态:
| status | 常见原因与处理 |
|---|---|
| 401 | 凭据无效或已过期;重新生成 token / 检查服务账号密钥 |
| 403 | 服务账号未被加入对应资源;GSC 的 inspectUrl 需要 Owner 或 Full;也可能是对应的 Google API 未启用 |
| 404 | 资源不存在(GSC_SITE_URL / GA4_PROPERTY_ID / Bing 站点);GSC 待查 URL 不属于资源时更常见 403 |
| 429 | 触发限速或日额度用尽 |
error.body 保留服务端原始错误体;排查时优先看 body.error.message。
平台限制速查
| 限制 | GSC | GA4 | Clarity | Bing | Ahrefs | |---|---|---|---|---|---| | 单请求行数上限 | 25,000 | 250,000 | 1,000(不可分页) | 近月按天聚合,无分行参数 | 单域名 DR 一次查询 | | 数据窗口 | 16 个月 | 视属性配置 | 仅最近 3 天 | 约一个月(按天) | 实时 | | 频率限制 | 1,200 QPM/站点(searchAnalytics);URL Inspection 2,000 QPD / 600 QPM | 按 token 配额(标准属性约 200k tokens/天) | 10 次/天/项目 | 官方未公开限额(宽松) | 免费公开端点 0 units,无公布限额 | | 数据延迟 | 2–3 天 | 数小时–1 天 | 数小时 | 4–5 天 | 实时 |
GA4 注意:单请求最多返回 250,000 行;runReportAll 通过 offset 深翻页拉取更大结果集时,官方服务端在大 offset 下可能出现不稳定或超时,建议用 maxRows 控制总量或缩小维度基数。
Clarity 官方 API 只覆盖近 72 小时且每天仅 10 次调用——若需历史留存,请在业务侧定时调用并将结果持久化到自己的存储。
开发
pnpm test # vitest 单测(全部 mock fetch,不发真实网络请求)
pnpm typecheck # tsc --noEmit
pnpm build # tsdown 构建 dist/
pnpm exec vitest run tests/gsc.test.ts # 只跑单个平台的测试真实凭据冒烟:node scripts/smoke.mjs 会依次打五个平台的真实接口(GSC 另含一条 URL Inspection 用例;需先 pnpm build,且根目录 .env 里是真值)。所有 Google 端点(GSC / GA4 的 token 交换与查询、GSC URL Inspection)需要代理:
NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://127.0.0.1:8118 node scripts/smoke.mjs只跑其中一条(避免消耗其他平台额度;SMOKE_ONLY 按用例名精确匹配,多个用逗号分隔):
SMOKE_ONLY='GSC URL 检查' NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://127.0.0.1:8118 node scripts/smoke.mjsNODE_USE_ENV_PROXY=1 需要 Node ≥ 24;Clarity / Bing / Ahrefs 可直连。该脚本不打进发布包(files: ["dist"])。
发布:npm publish(需先登录 npm 账号 pang3fan-creator;prepublishOnly 钩子会自动跑 typecheck + test + build)。
