PUBLIC API

PVZ:远征 · API 文档

接口已就绪,各取所需。

公开接口面向启动器、Bot、镜像站、资料工具和 DIY 关卡客户端。调用方应发送可识别来源的 User-Agent,尊重 Cache-Control 与 CDN-Cache-Control,避免高频轮询,并按 HTTP 状态码处理失败;不符合调用约定的请求可能收到 404 或 429。

命令行、脚本、官方游戏客户端和服务端客户端应发送可识别的 User-Agent;默认工具 UA 可能收到普通 404。 查看请求头策略

三步接入

大多数客户端可以先读取 API 索引,再跟随最新版本和下载入口。

  1. 步骤 1

    发现接口

    读取 API 索引,确认接口族、schema、语言和缓存策略。

    /api/v1/index.json
  2. 步骤 2

    读取最新版本

    请求版本详情,并选择与设备匹配的平台 key。

    /api/v1/releases/latest.json
  3. 步骤 3

    下载并校验

    使用 builds.*.url 下载文件,完成后用 builds.*.sha256 确认本地文件与发布构建一致。

    /download/0.17q/windows

端点目录

按用途筛选。每个卡片都可复制真实示例 URL。

发现

接口发现

低成本入口,列出接口族、schema、版本、语言和文档链接。

版本

版本与更新

当前版本、各平台构建、校验值和更新日志。

内容

图鉴与世界资料

图鉴条目、世界区域、你知道吗冷知识以及本地化正文。

DIY

DIY 关卡

公开关卡库、详情 JSON、GDF 下载与提交入口。

搜索/动态

搜索与动态

稳定的搜索语料、公告流,以及适合机器人的订阅入口。

下载

下载路由

由本站下载服务提供的二进制文件,支持 HEAD 和 Range 请求。

RSS

RSS 订阅

面向阅读器的 RSS 2.0 发布源。

GET · 发现

API 索引

面向启动器、Bot、镜像站和代理的机器可读发现入口,避免抓取页面或硬编码路径。

/api/v1/index.json

GET · 版本

最新版本详情

适用于启动器、镜像站和机器人的完整版本详情,包含所有平台的构建信息。

/api/v1/releases/latest.json

GET · 版本

自动更新清单

这份更新清单兼容旧客户端;顶层字段保留 Windows 64 位主构建,builds 则包含所有平台。

/api/v1/updates/latest.json

GET · 版本

版本索引

列出已公开版本,并提供当前 latest 字段和版本详情链接。

/api/v1/releases.json

GET · 版本

结构化更新日志

一次返回中英两种更新日志内容,适合公告机器人和启动器更新页。

/api/v1/changelog/0.17q.json

GET · 内容

世界区域索引

本地化世界设定索引;目前没有单独的区域详情 JSON,区域完整段落直接放在 regions 中。

/api/v1/world/zh/index.json

GET · 内容

你知道吗目录

本地化冷知识与玩法贴士目录;首页区块与外部工具都读取这套人工校验内容。

/api/v1/did-you-know/zh/index.json

POST · DIY

提交 DIY 关卡

提交 .gdf 文件与公开元数据;成功响应表示文件已接收并返回所有者凭证。

/api/v1/diy/levels

GET · 搜索/动态

稳定搜索语料

这是面向外部工具的站内搜索文档封装;不要依赖根目录的 search-index 文件。

/api/v1/search/zh.json

GET · 搜索/动态

动态与公告 JSON

面向启动器、机器人和聚合器的本地化新闻流。

/api/v1/news/zh.json

GET · HEAD · 下载

二进制下载

用于下载 Windows 和 Android 构建;支持 HEAD 预检和单段 Range 请求,文件名会通过 Content-Disposition 返回。

/download/0.17q/windows

GET · RSS

RSS 发布源

RSS 2.0 发布源;每个版本对应一个 item,并链接到本地化更新日志。

/zh/feed.xml

端点详情

展开后可查看字段摘要、精简响应示例,以及带标准 User-Agent 的 curl 和服务端 fetch 示例。

GET · HEAD

DIY 运行时配置

/api/v1/diy/runtime-config.json
适用场景
网站上传控件、游戏客户端
缓存
Cache-Control: private, no-store
示例路径
/api/v1/diy/runtime-config.json

字段摘要

字段摘要
字段类型说明
mode string disabled、local 或 production。
turnstileSiteKey string | null 公开 Turnstile site key。
turnstileRequired boolean 当前请求是否必须提供 Turnstile token。

运行时配置样例

{
  "schemaVersion": 1,
  "kind": "diyRuntimeConfig",
  "mode": "production",
  "turnstileSiteKey": "0x4AAAAAADr9j5Z_Is8qzs20",
  "turnstileRequired": true
}

fetch

const response = await fetch("https://pvzvoyage.com/api/v1/diy/runtime-config.json");
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const config = await response.json();
  • 不要把 turnstileRequired 从 levels.json 缓存到公共 CDN;列表响应只包含稳定目录数据。
GET

API 索引

/api/v1/index.json
适用场景
启动器、Bot、镜像站、代理
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=3600; CDN-Cache-Control: public, max-age=3600, stale-while-revalidate=86400
示例路径
/api/v1/index.json

字段摘要

字段摘要
字段类型说明
apiVersion string 当前公开 API 主版本。
latestRelease object 最新公开版本、发布时间和 release/update/changelog 链接。
locales object 默认语言、支持语言,以及各语言页面和本地化 API。
cache object API、下载和错误响应的缓存策略摘要。
families array 接口族、schemaVersion、示例路径和文档锚点。
documentation object 中英文 API 文档和 llms.txt 链接。

API 索引片段

{
  "schemaVersion": 1,
  "kind": "apiIndex",
  "apiVersion": "v1",
  "latestRelease": {
    "version": "0.17q",
    "releasedAt": "2026-07-01",
    "detail": {
      "path": "/api/v1/releases/latest.json",
      "url": "https://pvzvoyage.com/api/v1/releases/latest.json"
    }
  },
  "locales": {
    "default": "zh",
    "supported": [
      "zh",
      "en"
    ]
  },
  "documentation": {
    "en": {
      "path": "/en/api-docs/",
      "url": "https://pvzvoyage.com/en/api-docs/"
    },
    "zh": {
      "path": "/zh/api-docs/",
      "url": "https://pvzvoyage.com/zh/api-docs/"
    },
    "openapi": {
      "path": "/api/openapi.json",
      "url": "https://pvzvoyage.com/api/openapi.json"
    }
  },
  "families": [
    {
      "key": "release",
      "schemaVersions": [
        1,
        2
      ],
      "endpoints": [
        {
          "id": "releases-latest",
          "method": "GET",
          "pathTemplate": "/api/v1/releases/latest.json",
          "schemaVersion": 1
        },
        {
          "id": "updates-latest",
          "method": "GET",
          "pathTemplate": "/api/v1/updates/latest.json",
          "schemaVersion": 2
        }
      ]
    },
    {
      "key": "diy",
      "schemaVersions": [
        1
      ],
      "endpoints": [
        {
          "id": "diy-levels-list",
          "method": "GET · HEAD",
          "pathTemplate": "/api/v1/diy/levels.json",
          "schemaVersion": 1
        },
        {
          "id": "diy-level-detail",
          "method": "GET · HEAD",
          "pathTemplate": "/api/v1/diy/levels/{public_id}.json",
          "schemaVersion": 1
        },
        {
          "id": "diy-level-icon",
          "method": "GET · HEAD",
          "pathTemplate": "/api/v1/diy/levels/{public_id}/icon",
          "schemaVersion": 1
        },
        {
          "id": "diy-runtime-config",
          "method": "GET · HEAD",
          "pathTemplate": "/api/v1/diy/runtime-config.json",
          "schemaVersion": 1
        },
        {
          "id": "diy-level-upload",
          "method": "POST",
          "pathTemplate": "/api/v1/diy/levels",
          "schemaVersion": 1
        },
        {
          "id": "diy-owner-session",
          "method": "POST",
          "pathTemplate": "/api/v1/diy/levels/{public_id}/owner/session",
          "schemaVersion": 1
        },
        {
          "id": "diy-owner-status",
          "method": "POST",
          "pathTemplate": "/api/v1/diy/levels/{public_id}/owner/status",
          "schemaVersion": 1
        },
        {
          "id": "diy-owner-edit",
          "method": "POST",
          "pathTemplate": "/api/v1/diy/levels/{public_id}/owner/edit",
          "schemaVersion": 1
        },
        {
          "id": "diy-owner-remove",
          "method": "POST",
          "pathTemplate": "/api/v1/diy/levels/{public_id}/owner/remove",
          "schemaVersion": 1
        },
        {
          "id": "diy-owner-update",
          "method": "POST",
          "pathTemplate": "/api/v1/diy/levels/{public_id}/owner/update",
          "schemaVersion": 1
        }
      ]
    }
  ]
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/index.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/index.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const apiIndex = await response.json();
  • 客户端可以先读取索引,再按用途选择 release、content、search/news、download 或 RSS 入口。
  • 索引只列出稳定入口和模板,不展开全部图鉴 slug;需要具体条目时读取对应 locale 的 index JSON。
GET

最新版本详情

/api/v1/releases/latest.json
适用场景
启动器、镜像站、下载机器人
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=3600; CDN-Cache-Control: public, max-age=3600, stale-while-revalidate=86400
示例路径
/api/v1/releases/latest.json

字段摘要

字段摘要
字段类型说明
version string 当前版本号。
platforms string[] 可用的平台 key 列表。
builds object 按平台 key 索引的构建详情。
builds.*.sha256 string 二进制文件的 SHA-256 校验值。
feeds object 中英文 RSS 订阅链接。
urls.canonical Link 固定版本详情 URL。

精简版版本详情

{
  "schemaVersion": 1,
  "kind": "release",
  "version": "0.17q",
  "releasedAt": "2026-07-01",
  "latest": true,
  "primaryPlatform": "windows",
  "platforms": [
    "windows",
    "windows-zip",
    "windows-x86",
    "windows-x86-zip",
    "android"
  ],
  "urls": {
    "self": {
      "path": "/api/v1/releases/latest.json",
      "url": "https://pvzvoyage.com/api/v1/releases/latest.json"
    },
    "canonical": {
      "path": "/api/v1/releases/0.17q.json",
      "url": "https://pvzvoyage.com/api/v1/releases/0.17q.json"
    }
  },
  "builds": {
    "windows": {
      "platformKey": "windows",
      "platform": {
        "zh": "Windows 64 位(自解压)",
        "en": "Windows x64 (self-extracting EXE)"
      },
      "path": "/download/0.17q/windows",
      "url": "https://pvzvoyage.com/download/0.17q/windows",
      "aliasPath": "/download/0.17q/windows.exe",
      "filename": "PVZ-Voyage-0.17q-Windows-x64.exe",
      "size": 151519712,
      "sizeMiB": 144.5,
      "sha256": "126f641200dba871499c929f20fe2bd92ddbc8f8b051a5f9809764d7a493af57",
      "digest": {
        "algorithm": "SHA-256",
        "hex": "126f641200dba871499c929f20fe2bd92ddbc8f8b051a5f9809764d7a493af57"
      },
      "mime": "application/vnd.microsoft.portable-executable",
      "extension": ".exe",
      "minOs": {
        "name": "Windows 7",
        "build": 7600,
        "label": "Windows 7+"
      },
      "note": "示例只展示 Windows 64 位构建;真实响应会包含所有平台。"
    }
  }
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/releases/latest.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/releases/latest.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const release = await response.json();
  • 如果需要长期引用同一版本,请保存 urls.canonical 指向的固定版本 URL。
  • 客户端应从 builds 中选择平台,而不是硬编码下载 URL。
GET

自动更新清单

/api/v1/updates/latest.json
适用场景
自动更新器、启动器
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=3600; CDN-Cache-Control: public, max-age=3600, stale-while-revalidate=86400
示例路径
/api/v1/updates/latest.json

字段摘要

字段摘要
字段类型说明
url string 主构建下载 URL,保留给 v1 客户端使用。
sha256 string 主构建的校验值。
builds object 所有平台的构建信息。
minOs object 最低系统要求。
changelog object 更新日志页面和 JSON changelog 链接。

自动更新样例

{
  "schemaVersion": 2,
  "version": "0.17q",
  "releasedAt": "2026-07-01",
  "url": "https://pvzvoyage.com/download/0.17q/windows",
  "sha256": "126f641200dba871499c929f20fe2bd92ddbc8f8b051a5f9809764d7a493af57",
  "size": 151519712,
  "filename": "PVZ-Voyage-0.17q-Windows-x64.exe",
  "minOs": {
    "name": "Windows 7",
    "build": 7600
  },
  "builds": {
    "windows": {
      "url": "https://pvzvoyage.com/download/0.17q/windows",
      "sha256": "126f641200dba871499c929f20fe2bd92ddbc8f8b051a5f9809764d7a493af57",
      "size": 151519712,
      "filename": "PVZ-Voyage-0.17q-Windows-x64.exe",
      "minOs": {
        "name": "Windows 7",
        "build": 7600
      }
    }
  }
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/updates/latest.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/updates/latest.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const manifest = await response.json();
  • 如果你维护的是新客户端,优先读取版本详情;它比自动更新清单更完整。
  • 顶层字段只是兼容层,并不代表只有这些平台可下载。
GET

版本索引

/api/v1/releases.json
适用场景
版本列表、历史版本入口
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=3600; CDN-Cache-Control: public, max-age=3600, stale-while-revalidate=86400
示例路径
/api/v1/releases.json

字段摘要

字段摘要
字段类型说明
latest string 最新版本号。
releases array 已公开版本列表。
releases[].detail string 固定版本详情 API。
releases[].latestDetail string | null 当前版本的 latest 详情别名。

版本索引样例

{
  "schemaVersion": 1,
  "latest": "0.17q",
  "releases": [
    {
      "version": "0.17q",
      "releasedAt": "2026-07-01",
      "url": "https://pvzvoyage.com/download/0.17q/windows",
      "sha256": "126f641200dba871499c929f20fe2bd92ddbc8f8b051a5f9809764d7a493af57",
      "detail": "https://pvzvoyage.com/api/v1/releases/0.17q.json",
      "latestDetail": "https://pvzvoyage.com/api/v1/releases/latest.json"
    }
  ]
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/releases.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/releases.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const index = await response.json();
  • 只需要版本列表时使用它;如果需要平台构建信息,再读取 detail 字段指向的详情。
GET

结构化更新日志

/api/v1/changelog/{version}.json
适用场景
公告机器人、更新页、社区工具
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=604800; CDN-Cache-Control: public, max-age=86400, stale-while-revalidate=604800
示例路径
/api/v1/changelog/0.17q.json

字段摘要

字段摘要
字段类型说明
version string 这份更新日志对应的版本。
locales.zh object 中文更新日志。
locales.en object 英文更新日志。
sections array 标题、段落、小节和条目。

更新日志结构样例

{
  "schemaVersion": 1,
  "version": "0.17q",
  "releasedAt": "2026-07-01",
  "locales": {
    "zh": {
      "title": "远征 0.17q 更新日志",
      "sections": [
        {
          "title": "版本亮点",
          "paragraphs": [
            "..."
          ],
          "sections": []
        }
      ]
    }
  }
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/changelog/0.17q.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/changelog/0.17q.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const changelog = await response.json();
  • 当前站点只为当前版本提供结构化 changelog。
GET

图鉴索引

/api/v1/encyclopedia/{locale}/index.json
适用场景
图鉴浏览器、Wiki 工具、站内搜索
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=21600; CDN-Cache-Control: public, max-age=21600, stale-while-revalidate=86400
示例路径
/api/v1/encyclopedia/zh/index.json

字段摘要

字段摘要
字段类型说明
locale 'zh' | 'en' 当前语言。
tabs array 植物、僵尸、Boss 分类与计数。
entries array 条目摘要、页面 URL、API URL 和肖像。
alternate object 另一语言的页面与 API。

图鉴索引样例

{
  "schemaVersion": 1,
  "kind": "encyclopediaIndex",
  "locale": "zh",
  "title": "远征图鉴",
  "counts": {
    "total": 48,
    "byKind": {
      "plant": 22,
      "zombie": 16,
      "boss": 10
    }
  },
  "entries": [
    {
      "slug": "peashooter",
      "name": "豌豆射手",
      "kind": "plant",
      "path": "/zh/encyclopedia/peashooter/",
      "api": {
        "path": "/api/v1/encyclopedia/zh/peashooter.json",
        "url": "https://pvzvoyage.com/api/v1/encyclopedia/zh/peashooter.json"
      },
      "portrait": {
        "avif": {
          "path": "/media/encyclopedia/peashooter.avif"
        },
        "width": 1200,
        "height": 1185
      }
    }
  ]
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/encyclopedia/zh/index.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/encyclopedia/zh/index.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const fieldGuide = await response.json();
  • slug 列表来自内容模块;不要照着文档硬编码。
GET

图鉴条目详情

/api/v1/encyclopedia/{locale}/{slug}.json
适用场景
资料库、机器人、图鉴镜像
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=21600; CDN-Cache-Control: public, max-age=21600, stale-while-revalidate=86400
示例路径
/api/v1/encyclopedia/zh/peashooter.json

字段摘要

字段摘要
字段类型说明
entry object 条目正文、定位、能力与高级机制。
portrait object AVIF/WebP/PNG 肖像与尺寸。
navigation object 上一个/下一个条目摘要。
alternate object | null 另一语言的条目。

条目详情样例

{
  "schemaVersion": 1,
  "kind": "encyclopediaEntry",
  "locale": "zh",
  "slug": "peashooter",
  "entry": {
    "slug": "peashooter",
    "kind": "plant",
    "name": "豌豆射手",
    "mechanics": {
      "role": "远程攻击 · 杂交基础",
      "abilities": [
        "..."
      ]
    },
    "interactions": [
      {
        "target": "坚果墙",
        "targetSlug": "wall-nut",
        "effect": "..."
      }
    ]
  },
  "portrait": {
    "png": {
      "path": "/media/encyclopedia/peashooter.png"
    },
    "width": 1200,
    "height": 1185
  }
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/encyclopedia/zh/peashooter.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/encyclopedia/zh/peashooter.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const entry = await response.json();
  • 高级机制字段只在有对应资料的条目上出现。
GET

世界区域索引

/api/v1/world/{locale}/index.json
适用场景
地图、资料库、区域浏览器
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=21600; CDN-Cache-Control: public, max-age=21600, stale-while-revalidate=86400
示例路径
/api/v1/world/zh/index.json

字段摘要

字段摘要
字段类型说明
regions array 区域 slug、编号、标题、简介、段落与视觉资源。
visual object 卡片/首屏图片与主题色。
alternate object 另一语言的世界索引。

世界索引样例

{
  "schemaVersion": 1,
  "kind": "worldIndex",
  "locale": "zh",
  "count": 4,
  "regions": [
    {
      "slug": "maple-falls",
      "number": "I",
      "title": "枫威瀑布",
      "summary": "...",
      "path": "/zh/world/maple-falls/",
      "visual": {
        "accent": "#8da66b",
        "motif": "wind / lake / stairway"
      }
    }
  ]
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/world/zh/index.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/world/zh/index.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const world = await response.json();
  • 不同语言共用稳定的区域 slug;标题和正文会随 locale 本地化。
GET

你知道吗目录

/api/v1/did-you-know/{locale}/index.json
适用场景
启动器、机器人、资料工具、首页本地选择
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=21600; CDN-Cache-Control: public, max-age=21600, stale-while-revalidate=86400
示例路径
/api/v1/did-you-know/zh/index.json

字段摘要

字段摘要
字段类型说明
counts object 总数以及按 trivia、tip、mechanic、lore 分组的计数。
filters object 可用 contexts、kinds、spoilerLevels 与 tags。
selectionDefaults.homeHero object 首页默认选择规则。
items array 本地化条目数组,含正文、来源、标签、权重和关联图鉴。
items[].sourceRefs array 发布前校验过的本地来源。

你知道吗目录样例

{
  "schemaVersion": 1,
  "kind": "didYouKnowIndex",
  "locale": "zh",
  "counts": {
    "total": 36,
    "byKind": {
      "trivia": 2,
      "tip": 22,
      "mechanic": 11,
      "lore": 1
    }
  },
  "selectionDefaults": {
    "homeHero": {
      "allowedSpoilerLevels": [
        "none",
        "mild",
        "mechanics"
      ],
      "preferredRelatedSlugs": [
        "sunflower",
        "peashooter"
      ],
      "maxRecentIds": 12
    }
  },
  "items": [
    {
      "id": "sunflower-match-inheritance",
      "kind": "mechanic",
      "text": "向日葵连线不再区分方向;搭配阳光椰瓶时,还能继承大阳光充能。",
      "spoilerLevel": "mechanics",
      "contexts": [
        "homeHero",
        "encyclopedia",
        "api"
      ],
      "relatedEntries": [
        {
          "slug": "sunflower",
          "name": "向日葵",
          "path": "/zh/encyclopedia/sunflower/"
        }
      ]
    }
  ]
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/did-you-know/zh/index.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/did-you-know/zh/index.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const didYouKnow = await response.json();
  • 条目由仓库内容文件人工维护,并在构建时校验 ID、语言、slug、长度、标签和剧透等级。
  • 如果只需要一条结果,可以使用 random.json;如果要自行缓存或筛选,请读取完整目录。
GET · HEAD

随机你知道吗

/api/v1/did-you-know/{locale}/random.json
适用场景
启动器欢迎页、机器人、轻量工具
缓存
显式 seed 使用 Cache-Control: public, max-age=300, stale-while-revalidate=86400;无 seed 使用 UTC 小时桶和 public, max-age=60, stale-while-revalidate=300;两者都会发送 CDN-Cache-Control,并使用共享缓存规范化查询。
示例路径
/api/v1/did-you-know/zh/random.json?context=homeHero&entry=sunflower&seed=session-42

字段摘要

字段摘要
字段类型说明
context query homeHero、encyclopedia、api 或 any。
kind query 逗号分隔的 trivia、tip、mechanic、lore。
entry query 关联图鉴 slug;默认作为偏好,strictEntry=1 时变为必需。
strictEntry query 0 或 1;设为 1 时必须提供 entry,并且只从关联该条目的内容中选择。
tags query 逗号分隔的 URL-safe 标签,最多 12 个;匹配任意一个标签。
spoilerMax query 允许的最高剧透等级;homeHero 默认 mechanics,其他场景默认 lateGame。
exclude query 逗号分隔的条目 ID,最多 20 个。
seed query 最长 128 个字符;不提供时使用当前 UTC 小时桶。
X-PVZV-DidYouKnow-Cache response header HIT 或 MISS,用于验证随机选择缓存是否命中。
selection object 实际使用的 seed、池大小、排除数量与过滤器。
item object 被选中的 DidYouKnow 条目。

随机选择样例

{
  "schemaVersion": 1,
  "kind": "didYouKnowSelection",
  "locale": "zh",
  "selection": {
    "strategy": "weighted-stable-random",
    "seed": "session-42",
    "context": "homeHero",
    "poolSize": 31,
    "excludedCount": 0,
    "filters": {
      "entry": "sunflower",
      "strictEntry": false,
      "spoilerMax": "mechanics"
    }
  },
  "item": {
    "id": "sunflower-match-inheritance",
    "kind": "mechanic",
    "text": "向日葵连线不再区分方向;搭配阳光椰瓶时,还能继承大阳光充能。",
    "cta": {
      "path": "/zh/encyclopedia/sunflower/",
      "url": "https://pvzvoyage.com/zh/encyclopedia/sunflower/"
    }
  }
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  "https://pvzvoyage.com/api/v1/did-you-know/zh/random.json?context=homeHero&entry=sunflower&seed=session-42"

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/did-you-know/zh/random.json?context=homeHero&entry=sunflower&seed=session-42", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const selection = await response.json();
  • 同一目录、同一查询和同一 seed 会得到稳定结果;目录内容变化后不保证仍选中同一条。
  • 查询参数会按有效语义规范化后进入共享缓存;例如 tags 的顺序不会产生新的选择缓存。
  • 错误参数返回 JSON 400;没有匹配条目返回 JSON 404,code 为 no_matching_item。
GET · HEAD

DIY 关卡列表

/api/v1/diy/levels.json
适用场景
关卡库、启动器、游戏客户端、资料工具
缓存
Cache-Control: public, max-age=60, s-maxage=300
示例路径
/api/v1/diy/levels.json?sort=hot&tag=conveyor&limit=20

字段摘要

字段摘要
字段类型说明
q · 可选 query 搜索公开标题、作者或简介;最长 60 个字符。
ids · 可选 query 逗号分隔的公开关卡 ID,最多 50 个;适合本地收藏回查。
version · 可选 query 精确匹配游戏版本,例如 0.17q。
difficulty · 可选 query 外显/设计难度整数 0..9;这是独立显示刻度,不对应游戏内环境难度。
difficultyMin / difficultyMax · 可选 query 外显/设计难度范围,整数 0..9。
background · 可选 query 背景 ID。
wavesMin / wavesMax · 可选 query 最大波数范围,1..100。
subLevelsMin / subLevelsMax · 可选 query 子关卡数量范围,1..8。
downloadsMin / downloadsMax · 可选 query 近似下载量范围,使用非负整数。
tag · 可选 query 可重复,也可用逗号分隔;多个标签按同时匹配处理。
sort · 可选 query latest、hot、name、downloads 或 difficulty;默认 latest。
cursor · 可选 query 上一页返回的不透明分页游标。
limit · 可选 query 1..50,默认 20。
mode string 接口启用状态的公开标识。
levels array 公开关卡摘要数组。
levels[].titleZh string 中文公开标题。
levels[].titleEn string 英文公开标题。
levels[].descriptionZh string 中文公开简介。
levels[].descriptionEn string 英文公开简介。
levels[].icon object | null 可公开展示的关卡图标链接和图片元数据;未上传时为空。
levels[].download Link GDF 下载入口。
pagination.nextCursor string | null 下一页游标;为空表示没有下一页。
pagination.total number | null 当前筛选条件下的总数,无法计算时为空。

DIY 关卡列表样例

{
  "schemaVersion": 1,
  "kind": "diyLevelList",
  "mode": "production",
  "levels": [
    {
      "schemaVersion": 1,
      "id": "sample-level-id",
      "title": "Sample Conveyor Trial",
      "description": "Short public description shown in the library.",
      "titleZh": "传送带试炼",
      "titleEn": "Sample Conveyor Trial",
      "descriptionZh": "在关卡库中展示的中文简介。",
      "descriptionEn": "Short public description shown in the library.",
      "author": "Example Creator",
      "gameVersion": "0.17q",
      "difficulty": 3,
      "way": 0,
      "waves": 10,
      "background": 1,
      "subLevelCount": 1,
      "modeTags": [
        "conveyor"
      ],
      "sizeBytes": 20480,
      "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "icon": {
        "path": "/api/v1/diy/levels/sample-level-id/icon",
        "url": "https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/icon",
        "contentType": "image/png",
        "width": 96,
        "height": 96,
        "sizeBytes": 8192,
        "sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
      },
      "downloadCountApprox": 42,
      "approvedAt": "2026-06-27T01:00:00.000Z",
      "detail": {
        "path": "/api/v1/diy/levels/sample-level-id.json",
        "url": "https://pvzvoyage.com/api/v1/diy/levels/sample-level-id.json"
      },
      "download": {
        "path": "/api/v1/diy/levels/sample-level-id/download.gdf",
        "url": "https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/download.gdf"
      }
    }
  ],
  "pagination": {
    "nextCursor": "opaque-cursor",
    "total": 128
  }
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  "https://pvzvoyage.com/api/v1/diy/levels.json?sort=hot&tag=conveyor&limit=20"

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/diy/levels.json?sort=hot&tag=conveyor&limit=20", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const levels = await response.json();
  • 列表只返回已经公开展示的关卡;使用 ids 批量查询时,缺失或不可公开的 ID 会被省略。
  • 分页游标是不透明字符串;翻页时保持同一 sort 与筛选条件。
  • 上传所需的请求级 Turnstile 配置位于 /api/v1/diy/runtime-config.json;列表响应只包含稳定目录数据。
  • 可用模式标签来自受控词汇表:conveyor, no-glory, sun-limit, unlimited-sun, no-mower, no-shovel, sequential-spawn, endless, boss-fight, puzzle, speedrun, beginner, extreme。
GET · HEAD

DIY 关卡详情

/api/v1/diy/levels/{public_id}.json
适用场景
关卡详情页、游戏客户端、资料工具
缓存
Cache-Control: public, max-age=300, s-maxage=3600
示例路径
/api/v1/diy/levels/sample-level-id.json

字段摘要

字段摘要
字段类型说明
public_id path 公开关卡 ID。
level.id string 公开关卡 ID。
level.title string 兼容旧客户端的公开标题投影;本地化客户端应使用 titleZh/titleEn。
level.description string 兼容旧客户端的公开简介投影;本地化客户端应使用 descriptionZh/descriptionEn。
level.titleZh string 中文公开标题。
level.titleEn string 英文公开标题。
level.descriptionZh string 中文公开简介。
level.descriptionEn string 英文公开简介。
level.author string 从 GDF 中读取的作者。
level.gameVersion string | null GDF 声明的游戏版本。
level.difficulty number | null 关卡外显/设计难度值;默认取自 GDF 解析,也可在上传或管理时手动标注。
level.environmentDifficulty number | null 游戏内环境难度值,通常为 0..4,也识别 5..7。
level.waves number | null 最大波数。
level.background number | null 首个子关卡背景 ID。
level.subLevelCount number 子关卡数量。
level.modeTags string[] 公开模式标签。
level.sizeBytes number GDF 文件大小。
level.sha256 string GDF 文件 SHA-256。
level.icon object | null 关卡图标链接和 PNG/JPEG/WebP 图片元数据;未上传时为空。
level.downloadCountApprox number 近似下载量,用于展示趋势。
level.gdfName · 可选 string | null GDF 内部名称。
level.gdfDescription · 可选 string | null GDF 内部简介。
level.seed · 可选 number | null GDF 随机种子。
level.crcValid boolean GDF CRC 校验结果。
level.subLevels · 可选 array | null 多子关卡摘要。
level.subLevels[].difficulty · 可选 number | null 该子关卡的外显/设计难度。
level.summaryJson object GDF 信息摘要对象:由 GDF 原始 JSON 提取并归一化得到,保留关卡名、作者、版本、难度、波数、背景、子关卡和出怪概要;不是可直接重新打包为 .gdf 的原始关卡 JSON。

DIY 关卡详情样例

{
  "schemaVersion": 1,
  "kind": "diyLevelDetail",
  "mode": "production",
  "level": {
    "schemaVersion": 1,
    "id": "sample-level-id",
    "title": "Sample Conveyor Trial",
    "description": "Short public description shown in the library.",
    "titleZh": "传送带试炼",
    "titleEn": "Sample Conveyor Trial",
    "descriptionZh": "在关卡库中展示的中文简介。",
    "descriptionEn": "Short public description shown in the library.",
    "author": "Example Creator",
    "gameVersion": "0.17q",
    "difficulty": 3,
    "environmentDifficulty": 2,
    "way": 0,
    "waves": 10,
    "background": 1,
    "subLevelCount": 2,
    "modeTags": [
      "conveyor"
    ],
    "sizeBytes": 20480,
    "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "icon": {
      "path": "/api/v1/diy/levels/sample-level-id/icon",
      "url": "https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/icon",
      "contentType": "image/png",
      "width": 96,
      "height": 96,
      "sizeBytes": 8192,
      "sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
    },
    "downloadCountApprox": 42,
    "approvedAt": "2026-06-27T01:00:00.000Z",
    "detail": {
      "path": "/api/v1/diy/levels/sample-level-id.json",
      "url": "https://pvzvoyage.com/api/v1/diy/levels/sample-level-id.json"
    },
    "download": {
      "path": "/api/v1/diy/levels/sample-level-id/download.gdf",
      "url": "https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/download.gdf"
    },
    "gdfName": "Sample Conveyor Trial",
    "gdfDescription": "GDF-authored description if present.",
    "seed": 123,
    "crcValid": true,
    "subLevels": [
      {
        "index": 0,
        "difficulty": 2,
        "waves": 8,
        "background": 1,
        "zombieSummary": [
          "Zombie",
          "ConeheadZombie"
        ]
      },
      {
        "index": 1,
        "difficulty": 3,
        "waves": 10,
        "background": 3,
        "zombieSummary": [
          "BucketheadZombie",
          "FlagZombie"
        ]
      }
    ],
    "summaryJson": {
      "gdf_name": "Sample Conveyor Trial",
      "extracted_author": "Example Creator",
      "gdf_description": "GDF-authored description if present.",
      "game_version": "0.17q",
      "difficulty": 3,
      "environment_difficulty": 2,
      "way": 0,
      "waves": 10,
      "waves_range": "8-10",
      "background": 1,
      "sub_level_count": 2,
      "sub_levels": [
        {
          "index": 0,
          "waves": 8,
          "background": 1,
          "difficulty": 2,
          "zombie_summary": [
            "Zombie",
            "ConeheadZombie"
          ]
        },
        {
          "index": 1,
          "waves": 10,
          "background": 3,
          "difficulty": 3,
          "zombie_summary": [
            "BucketheadZombie",
            "FlagZombie"
          ]
        }
      ]
    }
  }
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/diy/levels/sample-level-id.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/diy/levels/sample-level-id.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const level = await response.json();
  • 不可公开或不存在的 ID 会返回通用 JSON 404;客户端不应据此推断关卡状态。
  • 下载 GDF 时优先使用 level.download.url,并用 level.sha256 做完整性校验。
  • level.summaryJson 是站点提取后的 GDF 信息摘要;需要原始关卡 JSON 时,应下载 .gdf 并按 GDF 格式解包。
  • level.difficulty 是关卡外显/设计难度;level.environmentDifficulty 是包级 Dif 记录的游戏内环境难度。
GET · HEAD

DIY GDF 下载

/api/v1/diy/levels/{public_id}/download.gdf
适用场景
游戏客户端、启动器、关卡管理工具
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=300
示例路径
/api/v1/diy/levels/sample-level-id/download.gdf

字段摘要

字段摘要
字段类型说明
public_id path 公开关卡 ID。
Content-Type header application/octet-stream。
Content-Disposition header 附件文件名,通常来自公开标题。
Content-Length header GDF 文件大小。
ETag header 与详情中的 sha256 对应的文件标识。
Cache-Control header 版本化公开文件缓存策略。

HEAD 响应示例

HTTP/2 200
content-type: application/octet-stream
content-disposition: attachment; filename="Sample Conveyor Trial.gdf"
content-length: 20480
etag: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
cache-control: public, max-age=300, stale-while-revalidate=300

HEAD

curl -I \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/download.gdf

GET

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  -o "Sample Conveyor Trial.gdf" \
  https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/download.gdf
  • 下载前可先读取详情 JSON,以展示关卡信息并取得 sha256。
  • 不可公开或不存在的 ID 会返回通用 JSON 404。
GET · HEAD

DIY 关卡图标

/api/v1/diy/levels/{public_id}/icon
适用场景
关卡库、启动器、游戏客户端
缓存
Cache-Control: public, max-age=300
示例路径
/api/v1/diy/levels/sample-level-id/icon

字段摘要

字段摘要
字段类型说明
public_id path 公开关卡 ID。
Content-Type header image/png、image/jpeg 或 image/webp。
Content-Disposition header inline。
Content-Length header 图标文件大小。
ETag header 图标 SHA-256 文件标识。
X-PVZV-DIY-Icon-Width header 图标宽度,单位像素。
X-PVZV-DIY-Icon-Height header 图标高度,单位像素。
Cache-Control header 公开图标缓存策略。

HEAD 响应示例

HTTP/2 200
content-type: image/png
content-disposition: inline
content-length: 8192
etag: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
x-pvzv-diy-icon-width: 96
x-pvzv-diy-icon-height: 96
cache-control: public, max-age=300

HEAD

curl -I \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/icon

GET

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  -o "sample-level-icon.png" \
  https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/icon
  • 只有已通过审核且上传了图标的关卡才会返回图片;其他情况返回通用 JSON 404。
  • 客户端通常应优先使用 level.icon.path 或 level.icon.url,而不是手工拼接路径。
POST

提交 DIY 关卡

/api/v1/diy/levels
适用场景
网站上传控件、游戏客户端
缓存
Cache-Control: private, no-store
示例路径
/api/v1/diy/levels

字段摘要

字段摘要
字段类型说明
file multipart field 必填 .gdf 文件,最大 256 KiB。
icon · 可选 multipart field 可选关卡图标。只接受 PNG、JPEG 或 WebP,最大 96 KiB,宽高 32..512 像素;完整 multipart 请求必须小于 480 KiB。
titleZh · 可选 multipart field 中文公开标题,填写时为 2..80 个字符。
titleEn · 可选 multipart field 英文公开标题,填写时为 2..80 个字符。
descriptionZh · 可选 multipart field 中文公开简介,最多 1500 个字符。
descriptionEn · 可选 multipart field 英文公开简介,最多 1500 个字符。
modeTags · 可选 multipart field 逗号分隔或重复字段,最多 5 个受控标签。
turnstileToken · 可选 multipart field 浏览器上传控件在需要时提供的验证 token。
status string 成功提交后为 pending。
level object 新关卡的 ID、标题、作者、sha256 与提交时间。
ownerCredential object 所有者凭证文件名、文本和 secret;只返回一次,请妥善保存。

提交成功样例

{
  "schemaVersion": 1,
  "status": "pending",
  "level": {
    "id": "sample-level-id",
    "title": "Sample Conveyor Trial",
    "author": "Example Creator",
    "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "createdAt": "2026-06-27T01:00:00.000Z"
  },
  "ownerCredential": {
    "filename": "pvzvoyage-diy-sample-level-id-owner.txt",
    "text": "PVZ: Voyage DIY owner credential\nLevel ID: sample-level-id\nSecret: example_owner_secret",
    "secret": "example_owner_secret"
  }
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  -F "file=@level.gdf;type=application/octet-stream" \
  -F "icon=@icon.png;type=image/png" \
  -F "titleZh=传送带试炼" \
  -F "titleEn=Sample Conveyor Trial" \
  -F "descriptionZh=中文公开简介。" \
  -F "descriptionEn=Short public description." \
  -F "modeTags=conveyor,puzzle" \
  https://pvzvoyage.com/api/v1/diy/levels

fetch

const formData = new FormData();
formData.set("file", file, file.name);
if (iconFile) formData.set("icon", iconFile, iconFile.name);
formData.set("titleZh", "传送带试炼");
formData.set("titleEn", "Sample Conveyor Trial");
formData.set("descriptionZh", "中文公开简介。");
formData.set("descriptionEn", "Short public description.");
formData.set("modeTags", "conveyor,puzzle");
if (turnstileToken) formData.set("turnstileToken", turnstileToken);

const response = await fetch("https://pvzvoyage.com/api/v1/diy/levels", {
  method: "POST",
  body: formData,
});
const result = await response.json();
if (!response.ok) {
  throw new Error(result?.error?.message ?? "DIY upload failed");
}
  • 成功响应不表示关卡已经公开;客户端应向用户展示 pending 状态,并保存 ownerCredential。
  • GDF 必须包含有效作者、CRC 和关卡数据;最多 8 个子关卡,每个子关卡波数为 1..100。
  • titleZh 与 titleEn 至少填写一个;未填写的另一语言可以由管理员在审核时补充。旧客户端仍可提交 title/description,服务端会将其写入两种语言。
  • 成功响应为 202 Accepted,并通过 Location、owner.statusUrl、owner.editUrl、owner.removeUrl 和 owner.updateUrl 提供正式生命周期入口。所有者接口接受 Bearer owner secret;网站页面继续使用 HttpOnly session。
POST

DIY 所有者:session

/api/v1/diy/levels/{public_id}/owner/session
适用场景
游戏客户端、提交者工具
缓存
Cache-Control: private, no-store
示例路径
/api/v1/diy/levels/sample-level-id/owner/session

字段摘要

字段摘要
字段类型说明
secret string 上传时只返回一次的 owner secret。

所有者接口响应

{
  "schemaVersion": 1,
  "ok": true,
  "expiresAt": "2026-06-27T02:00:00.000Z"
}

fetch

const response = await fetch("https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/owner/session", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-PVZV-CSRF": "1" },
  body: JSON.stringify({ secret: "<owner-secret>" }),
});
const result = await response.json();
  • session 入口是 same-origin 网站流程;通用客户端可以直接在其他 owner action 使用 Bearer secret。
POST

DIY 所有者:status

/api/v1/diy/levels/{public_id}/owner/status
适用场景
游戏客户端、提交者工具
缓存
Cache-Control: private, no-store
示例路径
/api/v1/diy/levels/sample-level-id/owner/status

字段摘要

字段摘要
字段类型说明
Authorization · 可选 header Bearer owner secret;也可使用网站 owner session。
X-PVZV-CSRF · 可选 header 仅 Cookie 浏览器流程需要;Bearer 客户端不需要。
body · 可选 application/json 编辑字段或空对象。

所有者接口响应

{
  "schemaVersion": 1,
  "level": {
    "id": "sample-level-id",
    "status": "pending"
  }
}

fetch

const response = await fetch("https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/owner/status", {
  method: "POST",
  headers: { Authorization: "Bearer <owner-secret>", "Content-Type": "application/json" },
  body: JSON.stringify({}),
});
const result = await response.json();
  • Bearer 客户端不需要 Origin、Sec-Fetch-Site 或 CSRF header;Cookie 浏览器流程仍受 same-origin 和 CSRF 保护。
POST

DIY 所有者:edit

/api/v1/diy/levels/{public_id}/owner/edit
适用场景
游戏客户端、提交者工具
缓存
Cache-Control: private, no-store
示例路径
/api/v1/diy/levels/sample-level-id/owner/edit

字段摘要

字段摘要
字段类型说明
Authorization · 可选 header Bearer owner secret;也可使用网站 owner session。
X-PVZV-CSRF · 可选 header 仅 Cookie 浏览器流程需要;Bearer 客户端不需要。
body application/json 编辑字段或空对象。

所有者接口响应

{
  "schemaVersion": 1,
  "ok": true,
  "status": "updated"
}

fetch

const response = await fetch("https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/owner/edit", {
  method: "POST",
  headers: { Authorization: "Bearer <owner-secret>", "Content-Type": "application/json" },
  body: JSON.stringify({}),
});
const result = await response.json();
  • Bearer 客户端不需要 Origin、Sec-Fetch-Site 或 CSRF header;Cookie 浏览器流程仍受 same-origin 和 CSRF 保护。
POST

DIY 所有者:remove

/api/v1/diy/levels/{public_id}/owner/remove
适用场景
游戏客户端、提交者工具
缓存
Cache-Control: private, no-store
示例路径
/api/v1/diy/levels/sample-level-id/owner/remove

字段摘要

字段摘要
字段类型说明
Authorization · 可选 header Bearer owner secret;也可使用网站 owner session。
X-PVZV-CSRF · 可选 header 仅 Cookie 浏览器流程需要;Bearer 客户端不需要。
body · 可选 application/json 编辑字段或空对象。

所有者接口响应

{
  "schemaVersion": 1,
  "ok": true,
  "status": "removed"
}

fetch

const response = await fetch("https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/owner/remove", {
  method: "POST",
  headers: { Authorization: "Bearer <owner-secret>", "Content-Type": "application/json" },
  body: JSON.stringify({}),
});
const result = await response.json();
  • Bearer 客户端不需要 Origin、Sec-Fetch-Site 或 CSRF header;Cookie 浏览器流程仍受 same-origin 和 CSRF 保护。
POST

DIY 所有者:update

/api/v1/diy/levels/{public_id}/owner/update
适用场景
游戏客户端、提交者工具
缓存
Cache-Control: private, no-store
示例路径
/api/v1/diy/levels/sample-level-id/owner/update

字段摘要

字段摘要
字段类型说明
Authorization · 可选 header Bearer owner secret;也可使用网站 owner session。
X-PVZV-CSRF · 可选 header 仅 Cookie 浏览器流程需要;Bearer 客户端不需要。
body multipart/form-data 完整 .gdf 与可选元数据。

所有者接口响应

{
  "schemaVersion": 1,
  "ok": true,
  "status": "pending_update"
}

fetch

const response = await fetch("https://pvzvoyage.com/api/v1/diy/levels/sample-level-id/owner/update", {
  method: "POST",
  headers: { Authorization: "Bearer <owner-secret>", "Content-Type": "application/json" },
  body: JSON.stringify({}),
});
const result = await response.json();
  • Bearer 客户端不需要 Origin、Sec-Fetch-Site 或 CSRF header;Cookie 浏览器流程仍受 same-origin 和 CSRF 保护。
GET

稳定搜索语料

/api/v1/search/{locale}.json
适用场景
机器人、站外搜索、轻量镜像
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=21600; CDN-Cache-Control: public, max-age=21600, stale-while-revalidate=86400
示例路径
/api/v1/search/zh.json

字段摘要

字段摘要
字段类型说明
documentCount number 文档数量。
source object 站内搜索源文件说明。
documents array 标准化文档数组。
documents[].kind string nav、changelog、world、encyclopedia、mechanics、channel、faq 等。

搜索文档样例

{
  "schemaVersion": 1,
  "kind": "searchIndex",
  "locale": "zh",
  "documentCount": 100,
  "documents": [
    {
      "id": "encyclopedia:zh:peashooter",
      "kind": "encyclopedia",
      "title": "豌豆射手",
      "subtitle": "植物档案 · 初始拥有",
      "path": "/zh/encyclopedia/peashooter/",
      "url": "https://pvzvoyage.com/zh/encyclopedia/peashooter/",
      "aliases": [
        "peashooter",
        "豌豆射手",
        "plant"
      ]
    }
  ]
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/search/zh.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/search/zh.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const searchIndex = await response.json();
  • 根目录 search-index-*.json 面向站内 UI,不是稳定 API。
GET

动态与公告 JSON

/api/v1/news/{locale}.json
适用场景
启动器新闻栏、Discord/QQ 机器人、聚合器
缓存
Cache-Control: public, max-age=300, stale-while-revalidate=21600; CDN-Cache-Control: public, max-age=21600, stale-while-revalidate=86400
示例路径
/api/v1/news/zh.json

字段摘要

字段摘要
字段类型说明
latestVersion string 最新版本。
links object 首页、下载、更新日志、RSS 与 release API。
items array 公告条目。
items[].highlights array 结构化亮点。

动态流样例

{
  "schemaVersion": 1,
  "kind": "newsFeed",
  "locale": "zh",
  "latestVersion": "0.17q",
  "items": [
    {
      "id": "release-0.17q",
      "kind": "release",
      "version": "0.17q",
      "title": "PVZ: Voyage 0.17q 已发布",
      "publishedAt": "2026-07-01",
      "api": {
        "path": "/api/v1/releases/0.17q.json",
        "url": "https://pvzvoyage.com/api/v1/releases/0.17q.json"
      }
    }
  ]
}

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/api/v1/news/zh.json

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/api/v1/news/zh.json", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
if (!response.ok) {
  throw new Error(`PVZ: Voyage API failed: ${response.status}`);
}
const news = await response.json();
  • 给 RSS 阅读器请使用 RSS feed;给程序客户端请使用这个 JSON。
GET · HEAD

二进制下载

/download/{version}/{platform}
适用场景
启动器、镜像站、下载页面
缓存
public, max-age=3600, s-maxage=86400, immutable
示例路径
/download/0.17q/windows

字段摘要

字段摘要
字段类型说明
Content-Type header 构建的 MIME 类型,例如 application/zip 或 Android APK 的 MIME 类型。
Content-Disposition header 附件文件名。
Content-Length header 完整文件或单段 Range 响应的字节数。
Accept-Ranges header 表示支持 bytes 范围请求。
Range request header 请求单个字节范围。
Retry-After header 响应为 429 时返回。

HEAD 预检示例

HTTP/2 200
content-type: application/vnd.microsoft.portable-executable
content-length: 151519712
content-disposition: attachment; filename="PVZ-Voyage-0.17q-Windows-x64.exe"
accept-ranges: bytes
cache-control: public, max-age=3600, s-maxage=86400, immutable

HEAD

curl -I \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/download/0.17q/windows

Range

curl -L \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  -H "Range: bytes=0-1048575" \
  -o pvzvoyage.part \
  https://pvzvoyage.com/download/0.17q/windows

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const head = await fetch("https://pvzvoyage.com/download/0.17q/windows", {
  method: "HEAD",
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
const size = Number(head.headers.get("content-length") ?? 0);
const filename = head.headers.get("content-disposition");
  • 先读取版本详情,再使用其中的 builds.*.url。
  • 下载完成后,使用 builds.*.sha256 校验文件是否与本站发布的构建一致。
  • 只支持单段 Range;多段或非法 Range 会返回 416。遇到 429 时读取 Retry-After 后再重试。
GET

RSS 发布源

/zh/feed.xml
适用场景
RSS 阅读器、订阅服务
缓存
通过服务端响应头应用静态资源缓存
示例路径
/zh/feed.xml

字段摘要

字段摘要
字段类型说明
channel RSS 站点发布源。
item RSS 版本条目。
link RSS 本地化更新日志链接。
pubDate RSS 发布日期。

RSS 片段

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>PVZ: Voyage</title>
    <item>
      <title>PVZ: Voyage 0.17q 已发布</title>
      <link>https://pvzvoyage.com/zh/version/0.17q/</link>
    </item>
  </channel>
</rss>

curl

curl -fsSL \
  -A "PVZVoyage/0.17q (+https://pvzvoyage.com/)" \
  https://pvzvoyage.com/zh/feed.xml

fetch

// Server-side fetch example. Browser fetch sends a browser User-Agent automatically.
const response = await fetch("https://pvzvoyage.com/zh/feed.xml", {
  headers: {
    "User-Agent": "PVZVoyage/0.17q (+https://pvzvoyage.com/)",
  },
});
const xml = await response.text();
  • RSS 适合阅读器;程序化公告建议使用 /api/v1/news/{locale}.json。

客户端下载逻辑建议

不要在客户端中硬编码二进制文件的下载路径。客户端应先获取最新版本详情,再根据目标平台从 builds 中选择对应构建。下载路由支持 HEAD 预检、GET 下载和单段 Range 断点续传;非法范围会返回 416,限流会返回 429 和 Retry-After。下载完成后,使用 SHA-256 校验本地文件是否与本站发布的构建一致;该校验不代表运行环境、杀毒判断或游戏行为一定正常。

选择 JSON 还是 RSS?

两者都来自同一套静态构建流程,但适用对象不同。

使用 News JSON

News JSON 包含结构化字段、CTA、图片、tag 和 release API 链接,更适合程序读取。

  • 启动器新闻栏
  • 机器人公告
  • 站外聚合器
/api/v1/news/zh.json

使用 RSS

RSS 2.0 更适合阅读器和通用订阅服务。

  • RSS 阅读器
  • 个人订阅
  • 低维护通知
/zh/feed.xml

请求头、错误与版本策略

User-Agent

命令行工具、脚本、官方游戏客户端和服务端客户端应发送能够标识集成来源的 User-Agent,例如:PVZVoyage/0.17q (+https://pvzvoyage.com/)。如果您维护自己的客户端,可以替换 product/version 和支持 URL。公开读取 API 支持这种格式,并继续受速率限制和反滥用规则约束;浏览器 fetch 会自动携带浏览器 UA;使用默认工具 UA 的请求可能收到普通 404,而不是 JSON 格式错误。

CORS

JSON API 响应允许跨源读取:Access-Control-Allow-Origin: *。该设置只表示浏览器可以读取响应,不表示第三方页面获得本站背书。

缓存与频率

API JSON 使用 Cache-Control 表达较短的浏览器缓存,并用 CDN-Cache-Control 表达较长的共享缓存意图;固定版本 release/changelog JSON 比 latest/update JSON 更适合长时间缓存。DidYouKnow random 会按规范化后的有效查询进入共享缓存。DIY 公共 GDF/icon 的 GET 响应使用短 TTL 缓存,HEAD 请求不用于填充边缘缓存;下载文件使用带版本路径的 immutable 缓存。客户端应尊重这些响应头,避免用高频轮询代替缓存。收到 429 时,请读取 Retry-After 后再重试。

错误响应

未知 .json 路径通常返回 JSON 404,参数错误可能返回 JSON 400。下载路由的非法 Range 返回 416;请求过于频繁时返回 429。少数不符合公开接口调用约定的请求可能收到普通 404,这类响应不保证采用 JSON 错误格式。

下载校验

builds.*.sha256 用于确认下载完成后的本地文件与本站发布的构建产物一致。它不能证明文件会在所有系统上运行,也不能替代系统权限、杀毒软件和用户自己的来源判断。

兼容性

/api/v1 是破坏性变化的兼容边界。v1 内的 schemaVersion 只表示向后兼容的 payload 演进;新增字段可以提升 schemaVersion,但客户端必须忽略未知字段。任何不兼容变化都进入 /api/v2。