Web API
Petrichor のデータを、プログラムから調べたり登録したりするための API です。リクエストとレスポンスは JSON で、GUID は小文字の 32 桁の 16 進数です。
ベース URL は https://api.natsuneko.com/petrichor/v2 です。
エンドポイント
GET は認証なしで使え、結果は Cache-Control: public, s-maxage=300 でキャッシュされます。
| メソッド | パス | 内容 | ログイン | キャッシュ |
|---|---|---|---|---|
| GET | /assets/{guid} | GUID を含むパスと Release | - | 300 秒 |
| POST | /assets/lookup | GUID の一括照合 | - | なし |
| GET | /packages?q= | パッケージの検索 | - | 300 秒 |
| GET | /packages/{id} | パッケージの詳細 | - | 300 秒 |
| GET | /releases/{id} | Release の詳細 | - | 300 秒 |
| GET | /releases/{id}/assets?cursor=&path= | Release のアセット一覧 | - | 300 秒 |
| POST | /submissions | マニフェストの登録 | 必要 | なし |
| GET | /me/submissions | 自分の登録の一覧 | 必要 | なし |
Release の形
多くのレスポンスに含まれる Release は、次の形です。status は verified (検証済み)、legacy、unverified のいずれかです。
{
"id": "clx0release",
"version": "3.7.0",
"status": "verified",
"submitterCount": 2,
"assetCount": 1284,
"manifestId": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"createdAt": "2026-11-01T00:00:00.000Z",
"package": { "id": "clx0package", "name": "VRChat SDK - Base" }
}Release の配列は、検証済みと Legacy が先、未検証が後で、それぞれの中では新しい順に並びます。
GUID を調べる
GET /assets/{guid}
その GUID のパスと種類、それを含む Release を返します。大文字の GUID も受け付けます。登録がない、または GUID の形式が正しくないときは 404 です。
{
"guid": "0123456789abcdef0123456789abcdef",
"paths": [
{
"path": "Packages/com.vrchat.base/Runtime/Foo.cs",
"type": "MonoScript",
"releases": [ /* Release の配列 */ ]
}
]
}POST /assets/lookup
GUID をまとめて照合します。GUID は大文字小文字と重複をまとめてから数え、1 回に 1,000 件までです (超えると 400)。results は GUID ごとの結果、missing は見つからなかった GUID、suggestions は含む GUID の多い順に並べた Release の候補 (最大 20 件) です。 結果はキャッシュされません。
{ "guids": ["0123456789abcdef0123456789abcdef", "…"] }{
"results": {
"0123456789abcdef0123456789abcdef": { /* GET /assets/{guid} と同じ形 */ }
},
"missing": ["fedcba9876543210fedcba9876543210"],
"suggestions": [
{ "release": { /* Release */ }, "matched": 42 }
]
}パッケージと Release
GET /packages?q=
パッケージを名前・説明・識別子で検索します。最大 50 件を返します。q がなければ、最近更新された順です。
{
"items": [
{
"id": "clx0package",
"name": "VRChat SDK - Base",
"description": null,
"latestVersion": "3.7.0",
"verifiedReleaseCount": 3
}
]
}GET /packages/{id}
パッケージの名前・説明・識別子と、Release の一覧を返します。 識別子の provider は package (パッケージ名)、booth (BOOTH の商品 ID)、url のいずれかです。
{
"id": "clx0package",
"name": "VRChat SDK - Base",
"description": null,
"identifiers": [{ "provider": "package", "value": "com.vrchat.base" }],
"releases": [ /* Release の配列 */ ],
"updatedAt": "2026-11-01T00:00:00.000Z"
}GET /releases/{id}
Release を 1 つ返します。形は上の Release と同じです。
GET /releases/{id}/assets?cursor=&path=
Release に含まれるアセットを、パスの順に 100 件ずつ返します。nextCursor を次のリクエストの cursor に渡すと続きが読めます。最後のページでは null です。path を渡すと、そのパスで始まるものだけに絞り込みます。
{
"items": [
{ "guid": "0123456789abcdef0123456789abcdef", "path": "Packages/com.vrchat.base/Runtime/Foo.cs", "type": "MonoScript" }
],
"nextCursor": "…"
}マニフェストを登録する
POST /submissions
ログインが必要です。ボディは、Unity 拡張が書き出すマニフェストのファイルと同じ形式です。
{
"format": "petrichor/v1",
"package": { "identifier": { "provider": "package", "value": "com.vrchat.base" }, "name": "VRChat SDK - Base" },
"version": "3.7.0",
"assets": [
{ "guid": "0123456789abcdef0123456789abcdef", "path": "Packages/com.vrchat.base/Runtime/Foo.cs", "type": "MonoScript" }
]
}formatは"petrichor/v1"固定です。versionは 1〜64 文字のラベルです。assetsは 1〜30,000 件です。pathは 1〜1,024 文字、type(例:MonoScript) は省略できます。packageは次のいずれかの形で指定します。識別子で指定した場合、同じ識別子のパッケージがあればそれに、なければnameで新しく作ります。
// 既存のパッケージの ID を指定する
{ "id": "clx0package" }
// 識別子を指定する。同じ識別子のパッケージがなければ、name で新しく作る
{ "identifier": { "provider": "package" | "booth" | "url", "value": "com.vrchat.base" },
"name": "VRChat SDK - Base", "description": "…" }
// 識別子なしで、新しいパッケージを作る
{ "name": "…", "description": "…" }正規化のルール
- GUID は小文字にし、
^[a-f0-9]{32}$に合わないものは拒否します。 - パスは
\を/に置き換えるだけで、大文字小文字や前後の空白は変えません。U+0000〜U+001F と U+007F の制御文字を含むパスは拒否します。 - 同じ GUID に異なるパスがあれば拒否します。(GUID, パス) が同じ行は 1 つにまとめます。
typeは Manifest の同定に含めません。最初に書かれた値を使います。
レスポンス
新しく登録されたときは 201、同じユーザーが同じ Release に登録済みのときは何もせず 200 で、どちらも次の形です。verified は、異なる 2 人以上が登録した (または Legacy の) Release で true になります。
{
"releaseId": "clx0release",
"manifestId": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"verified": false,
"submitterCount": 1
}上限
1 人あたり 1 日 100 件までです。UTC の日付で数え、登録のルールに通ったリクエストは、同じ内容の再送でも 1 件に数えます。超えると 429 を返します。
自分の登録
GET /me/submissions
ログインしているユーザーの登録を、新しい順に最大 100 件返します。source は WEB、API、LEGACY のいずれかです。ボディは配列そのものです。
[
{
"id": "clx0submission",
"version": "3.7.0",
"source": "API",
"createdAt": "2026-11-01T00:00:00.000Z",
"release": { /* Release */ }
}
]Bearer トークンで登録する
登録と自分の登録の一覧は、サイトのログイン (Cookie) のほか、Egeria のアクセストークンを Authorization: Bearer ヘッダーで渡して使えます。 Cookie での登録は source: WEB、Bearer での登録は source: API として記録されます。
curl -X POST https://api.natsuneko.com/petrichor/v2/submissions \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
--data @petrichor-manifest.jsonManifest のハッシュ
Manifest の ID は、正規化した (GUID, パス) を次のように連結したものの SHA-256 を、16 進数 (小文字) で表したものです。
- (GUID, パス) を、UTF-8 のバイト列として GUID、パスの順に比較して並べる
- 各行を
${guid}\t${path}\n(タブ区切りで、末尾は改行) にして、すべてつなげる - つなげた文字列を UTF-8 で SHA-256 にかける
並べ替えは UTF-16 の順 (JavaScript の文字列比較) ではなく、UTF-8 のバイト順です。入力の順序には依存しません。ハッシュはサーバーが計算するので、リクエストに含める必要はありません。
import { createHash } from "node:crypto";
const manifestId = (assets: { guid: string; path: string }[]) => {
const bytes = (text: string) => Buffer.from(text, "utf8");
const sorted = [...assets].sort(
(a, b) => Buffer.compare(bytes(a.guid), bytes(b.guid)) || Buffer.compare(bytes(a.path), bytes(b.path)),
);
const text = sorted.map((asset) => `${asset.guid}\t${asset.path}\n`).join("");
return createHash("sha256").update(text, "utf8").digest("hex");
};エラー
| ステータス | 主な原因 |
|---|---|
| 400 | リクエストの形が正しくない、GUID の形式が正しくない、同じ GUID に異なるパスがある、パスに制御文字がある、新しいパッケージに名前がない、一括照合の GUID が多すぎる |
| 401 | 登録・自分の登録の一覧で、ログインしていない |
| 404 | 指定した GUID・パッケージ・Release が存在しない (登録では、id で指定したパッケージがない) |
| 429 | 1 日の登録数の上限を超えた |
ボディの形は次のとおりです。メッセージは日本語で返ります。
// 登録のルールに反した
{ "error": { "message": "同じ GUID に異なるパスが指定されています", "code": 400 } }
// リクエストの形が違う
{ "message": "validation failed" }
// ログインしていない
{ "message": "unauthorized" }v1
GET /petrichor/v1/asset/by/guid/{guid}は、GUID が登録されていれば 200 で{ "id": "<guid>" }、なければ 404 を返します。POST /petrichor/v1/submitは終了していて、常に 410 を返します。v2 の登録を使ってください。