跳转到主要内容
返回文档

Lunidex 公共 API v1

通过服务器端集成访问已同步到 Lunidex 账户的数据。创建密钥并发送第一个请求,然后了解卡牌、未开封商品、分页和安全写入。

基础 URLhttps://lunidex.app/api/v1
管理 API 密钥打开 OpenAPI 规范

密钥仅限服务器使用

切勿将 API 密钥放入浏览器代码、移动应用、公开仓库、URL、截图或共享日志。请将密钥保存在服务器端密钥管理器中,并且只通过 Authorization 请求头发送。

快速开始

集成需要 API 密钥、保存在服务器上的密钥值,以及发送到 API 基础 URL 的 HTTPS 请求。

  1. 1

    创建密钥

    打开 Lunidex 控制面板中的 API 密钥卡片,为密钥命名并选择只读或读写权限。完整密钥只显示一次;关闭提示前请先复制。

  2. 2

    在服务器上保存密钥

    在托管服务的密钥管理器中将密钥保存为 LUNIDEX_API_KEY。不要将其提交到仓库,也不要发送给浏览器。

  3. 3

    发送第一个请求

    从服务器调用 GET /me。私有端点要求在 Authorization: Bearer 后附上密钥。

cURL · 在可信的服务器环境中运行
curl --fail-with-body \
  -H "Authorization: Bearer $LUNIDEX_API_KEY" \
  https://lunidex.app/api/v1/me
Node.js · 服务器端 fetch
const API_BASE_URL = "https://lunidex.app/api/v1";
const apiKey = process.env.LUNIDEX_API_KEY;

if (!apiKey) throw new Error("Missing LUNIDEX_API_KEY");

const response = await fetch(API_BASE_URL + "/me", {
  headers: { Authorization: "Bearer " + apiKey },
});
const result = await response.json();

if (!response.ok) {
  throw new Error(result.error?.code ?? "REQUEST_FAILED");
}

console.log(result.data);

身份验证与权限

每个私有 /api/v1 请求都要在 HTTP Authorization 请求头中发送密钥。API 使用 data 封套返回 JSON;私有响应使用 Cache-Control: private, no-store。

读取

可以调用 GET 端点。仅需显示或导出账户数据的集成应选择此权限。

读写

可以调用 GET 和写入端点。仅当可信的服务器集成需要修改收藏或未开封商品流水账时才选择此权限。

API 只能读取 Neon 中已同步到 Lunidex 账户的数据。仅保存在设备上的数据不可用。账户身份信息不包含电子邮件地址。

分页与筛选

列表返回 data 和 meta.nextCursor。请求下一页时原样传递游标;游标为 null 时结束。

  • 默认每页 25 条,最多 100 条。目录搜索固定为每页最多 24 条。
  • 卡牌支持 language 和 set 筛选。未开封商品持仓支持 language 和 productId;交易支持 language、productId、type、includeVoided 和 voided。目录搜索支持 q 和 cursor。
  • 收藏、流水账、价格或筛选条件变化后,游标可能过期。此时 API 返回 409;请使用最新状态并移除游标重新开始。
在服务器上读取所有卡牌分页
const API_BASE_URL = "https://lunidex.app/api/v1";
const apiKey = process.env.LUNIDEX_API_KEY;
let cursor = null;

if (!apiKey) throw new Error("Missing LUNIDEX_API_KEY");

do {
  const query = new URLSearchParams({ limit: "100" });
  if (cursor) query.set("cursor", cursor);

  const response = await fetch(API_BASE_URL + "/cards?" + query, {
    headers: { Authorization: "Bearer " + apiKey },
  });
  const result = await response.json();
  if (!response.ok) throw new Error(result.error?.code ?? "REQUEST_FAILED");

  console.log(result.data);
  cursor = result.meta?.nextCursor ?? null;
} while (cursor);

路由

以下路径均相对于 API 基础 URL。每一行都会显示所需权限以及 OpenAPI 契约中列出的参数。机器可读规范包含完整架构、请求和响应示例以及各操作的错误;以下章节说明主要使用流程。

账户与契约

  • GET公开 · 无需密钥
    /openapi.json

    公开的机器可读契约,无需 API 密钥。

    参数:无
  • GET只读密钥
    /me

    有限的账户身份信息,不包含电子邮件地址。

    参数:无
  • GET只读密钥
    /summary

    主要账户统计、实物与不同卡牌数量,以及以欧分计价的未开封商品汇总。

    参数:无

卡牌收藏

  • GET只读密钥
    /cards

    分页列出持有的卡牌,可按语言和系列筛选。

    参数:
    • cursor(查询)
    • limit(查询)
    • language(查询)
    • set(查询)
  • GET只读密钥
    /cards/{cardId}

    TCGdex 元数据和本账户持有信息,不包含卡牌估值。

    参数:
    • cardId(路径)· 必填
    • language(查询)
  • PUT读写密钥
    /cards/{cardId}

    为一种卡牌语言和变体设置绝对数量。

    参数:
    • cardId(路径)· 必填

未开封商品与交易

  • GET只读密钥
    /sealed/catalogue

    搜索现有 Cardmarket 未开封商品目录。

    参数:
    • q(查询)
    • cursor(查询)
  • GET只读密钥
    /sealed/positions

    分页列出当前持仓、成本及已存储的市场价值。

    参数:
    • cursor(查询)
    • limit(查询)
    • language(查询)
    • productId(查询)
  • GET只读密钥
    /sealed/positions/{productId}

    查看一个已持有商品按语言划分的当前持仓。

    参数:
    • productId(路径)· 必填
  • GET只读密钥
    /sealed/transactions

    带修订号和筛选条件的私有交易流水账。

    参数:
    • cursor(查询)
    • limit(查询)
    • language(查询)
    • productId(查询)
    • type(查询)
    • includeVoided(查询)
    • voided(查询)
  • POST读写密钥
    /sealed/transactions

    创建购买、出售或交换;必须提供幂等键。

    参数:
    • Idempotency-Key(请求头)· 必填
  • GET只读密钥
    /sealed/transactions/{id}

    读取属于此账户的一笔交易。

    参数:
    • id(路径)· 必填
  • PATCH读写密钥
    /sealed/transactions/{id}

    通过修订号检查修改交易。

    参数:
    • id(路径)· 必填
  • POST读写密钥
    /sealed/transactions/{id}/void

    通过修订号检查作废交易。

    参数:
    • id(路径)· 必填

卡牌

GET /cards 列出持有的卡牌变体,不会加载完整的 TCGdex 目录。每项包含 cardId、setId、language、variant 和 quantity;可选筛选项为 language 和 set。

GET /cards/{cardId} 返回有用的 TCGdex 元数据和账户内该卡牌的持有信息。可使用 language 查询参数筛选持有记录。

PUT /cards/{cardId} 接收 language、variant(unspecified、normal、reverse 或 holo)以及 0 到 10,000 的绝对数量。数量为 0 时删除该变体。

PUT /cards/{cardId} · application/json
{
  "language": "en",
  "variant": "reverse",
  "quantity": 2
}

没有语言信息的旧持有记录仍可见;language 可能为 null,也可能标记为 legacy。

此 API 不会返回卡牌估值。

未开封商品

未开封商品持仓包含按语言划分的数量、成本,以及基于 Lunidex 已存储价格计算的当前价值。金额为整数欧分。缺少价格时 valueCents 为 null,并由 missingPrice 标记;summary 还会返回 missingPrices。

在 GET /summary 中,只要任一持有商品缺少价格,未开封商品总价值就为 null;missingPrices 提供缺价数量。

GET /sealed/catalogue

使用 q(最多 150 个字符)和 cursor 搜索已存在于 Cardmarket 目录中的商品。每页最多 24 项;后续请求可使用返回的商品 ID。

GET /sealed/positions

GET /sealed/positions 支持 language 和 productId 筛选。GET /sealed/positions/{productId} 返回一个商品的当前持仓。这些端点只提供当前汇总,不提供完整价格历史序列。

GET /sealed/transactions

私有流水账支持 language、productId、type(buy、sell、exchange)、includeVoided 和 voided 筛选,并返回修订号以便安全修改。

写入、幂等性与修订号

设置卡牌数量

PUT /cards/{cardId} 设置绝对数量,而不是增加数量。重复发送相同请求会保持相同数量;发送 0 可删除该变体。写入前 API 会验证卡牌、系列、语言和可用变体。

创建未开封商品交易

POST /sealed/transactions 接受 buy、sell 和 exchange。必须提供 Idempotency-Key 请求头(8–200 个字符)和 expectedRevision。重试相同请求时重复使用同一个键,避免生成第二笔流水。首次请求创建交易;重复请求返回原始结果。

购买请求正文示例 · 将商品 ID 和修订号替换为当前值
curl --request POST "https://lunidex.app/api/v1/sealed/transactions" \
  -H "Authorization: Bearer $LUNIDEX_API_KEY" \
  -H "Idempotency-Key: lunidex-import-example-001" \
  -H "Content-Type: application/json" \
  --data '{
    "expectedRevision": 0,
    "kind": "buy",
    "cardmarketProductId": 12345,
    "language": "en",
    "date": "2026-09-27",
    "quantity": 1,
    "unitPriceCents": 2500
  }'

写入前,从 GET /sealed/transactions 的元数据读取最新流水账修订号,并将其作为 expectedRevision。PATCH /sealed/transactions/{id} 还要求该交易的 revision。修订号过期时返回 409;决定重试前请重新读取交易和流水账。

PATCH /sealed/transactions/{id}

POST /sealed/transactions/{id}/void
POST /sealed/transactions/{id}/void 要求 revision 和 expectedRevision。创建、修改和作废均使用 Lunidex 现有的交易验证、分配和审计流程。

application/json
{
  "revision": 1,
  "expectedRevision": 0
}

错误与响应格式

成功响应格式为 { "data": ..., "meta": ... }(meta 可选)。错误格式为 { "error": { "code": ..., "message": ..., "details": ... } }(details 可选)。

常见响应:400 交易数据无效;401 密钥无效;403 权限不足;404 资源不存在或不属于此账户;409 游标或修订号过期;410 账户正在删除;422 查询参数或请求正文无效;429 超出配额;500 意外的服务器错误;502 卡片数据不可用;503 API 不可用。

收到 409 时,请读取最新资源或移除游标重新获取列表。不要盲目将冲突转换为新的交易请求。

429 响应包含 Retry-After。请至少等待指定秒数后再重试。

所有私有 API 响应均使用 Cache-Control: private, no-store。公开 OpenAPI 文档单独发布于 /api/v1/openapi.json。

配额

限制按 Lunidex 账户计算,同一账户的所有 API 密钥共享配额。

配额
每分钟读取次数60
每分钟写入次数10
每日读取次数1,000
每日写入次数100
每账户每日卡牌详情读取次数100
每账户每日未开封商品计算次数100
每种高成本类别的全局每日上限5,000

卡牌详情与未开封商品计算各自拥有独立的全局上限,每类每天 5,000 次。429 响应包含 Retry-After。

打开 OpenAPI 规范