池子卡片及其 JSON
本站有两样东西是为其他网站准备的:一张显示某个池子建议区间的卡片,任何页面都可以把它放进框架里;以及同样数字的 JSON 版本,任何网站的代码都可以读取。两者都不需要密钥或账号。这里按代码实际提供的样子描述它们,并由测试把本页和那份代码绑在一起:本页列出的每个参数,都是读取地址时真正用到的参数;每个字段,都是响应发出前要核对的字段。
卡片
/embed/pool 是一个手写的小页面,不是由网站的框架生成的。它显示交易对及其协议、手续费和网络;建议区间,以及绘制它所用的时间跨度和宽度;当前价格;一行说明这不构成财务建议;以及一个返回本站该池子页面的链接,会在新标签页中打开。区间总是按本站的默认值绘制——30 天、1σ——不管谁自己的设置如何。当前价格在区间外时会加一条提示;对于 hook 可能改变兑换代价的 v4 池子,还会再加一条。
它不含任何脚本和表单,也不加载任何东西:它自己的 Content-Security-Policy 只允许它的内联样式。它的配色就是本站的配色,浅色或深色取决于读者系统的设置。它看不到周围的页面,也没有参数可以选择。
给它一个宽 360 像素、高 220 像素的框架;如果池子的 hook 可能改变兑换代价,高度用 260 像素,因为那张卡片多一行。每个池子的页面在“把这个池子嵌入你的网站”下提供的正是这个,高度已经按该池子调好;提供的框架也永远不会比粘贴进去的那一栏更宽。
这是本站唯一允许其他网站放进框架的页面。其他每个地址(包括本页)发送时都带有 X-Frame-Options: DENY 和 frame-ancestors 'none';卡片发送时不带前者,并带有 frame-ancestors *。
<iframe src="https://liquiditywise.com/embed/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8" title="USDC / WETH on LiquidityWise" width="360" height="220" style="border:0;max-width:100%" loading="lazy"></iframe>Content-Type: text/html; charset=utf-8
Cache-Control: public, max-age=300, s-maxage=300
Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors *参数
卡片和 JSON 接受相同的参数,并像本站自己的页面那样指定池子:v3 池子用它的 address,与 /pool 相同;v4 池子用它的 id,与 /v4 相同。两者必须恰好给出一个,给出的是哪一个,就说明是哪种协议。同一个池子或网络给出两次会被拒绝,而不是从中挑一个值;这里没有列出的参数都会被忽略。
语言只取自地址,从不取自读者的浏览器或 cookie:由放置卡片的网站来选择;同一个地址对每个加载它的人都是同一个响应,这样缓存才能保存它。卡片有本站全部 10 种语言的版本,阿拉伯语版从右往左排。
address- 接受 v3 池子的合约地址:
0x加 40 个十六进制数字,大小写均可。 - 否则 其他任何内容都不指向池子;不读取 v3 的网络(Unichain)上的 v3 池子也一样:
400,什么也不读取。 id- 接受 v4 池子的 id,即其 PoolKey 的哈希:
0x加 64 个十六进制数字,大小写均可。 - 否则 其他任何内容都不指向池子:
400,什么也不读取。与address一起给出的id也一样。 chain- 接受 下表中某个网络的简称。不给出时,网络为 Ethereum。
- 否则 本站不读取的简称会被拒绝,绝不会当成 Ethereum 读取:
400。 lang- 接受 下面列表中的语言代码,用于卡片上的文字和 JSON 的
disclaimer;各语言中的数字完全相同。不给出时为英语。 - 否则 其他任何值,或给出两次的
lang,都会得到英语。它永远不会被拒绝。
网络,按 chain 接受的简称:
chain | 网络 | v3 池子 | v4 池子 |
|---|---|---|---|
ethereum | Ethereum | 读取 | 读取 |
base | Base | 读取 | 读取 |
arbitrum | Arbitrum One | 读取 | 读取 |
unichain | Unichain | 不读取 | 读取 |
optimism | OP Mainnet | 读取 | 读取 |
polygon | Polygon | 读取 | 读取 |
语言,按 lang 接受的代码:
enEnglishtrTürkçedeDeutschesEspañolarالعربيةhiहिन्दीzh中文ruРусскийptPortuguêszh-Hant繁體中文
JSON
GET /api/embed/pool 用相同的参数,以 JSON 返回卡片上的数字,供宁愿自己绘制的网站使用:同一次读取,保存同样久。每个数字都是 JSON 数值,每个价格都按池子页面的方向书写:一个 price.base 值多少个 price.quote。
响应发出前会与其 schema 核对,不符合的不会发出:这时池子会被回答为无法读取。所以状态为 200 的响应总是恰好包含下面这些字段,不多也不少。
任何网站的脚本都可以读取它。每个响应(包括错误和拒绝)发送时都带有 Access-Control-Allow-Origin: *。它不设置 cookie,也不需要凭证。普通的 GET 不需要预检(preflight),而且不会回应任何预检,所以请不要附加自定义标头。
Content-Type: application/json; charset=utf-8
Cache-Control: public, max-age=300, s-maxage=300
Access-Control-Allow-Origin: *200 响应的每个字段及其 JSON 类型:
protocol"v3" | "v4"- 用
address指定的池子为v3,用id指定的为v4。 chain.idinteger > 0- 网络的 chain id。
chain.slugstring- 网络的简称,即
chain接受的写法。 chain.namestring- 网络的名称。
poolstring- 池子的地址(v3)或 id(v4),小写。
pair.token0string- 池子第一个代币的符号,按其合约所写:这是来自任何人都能部署的合约的文本,放进页面前请先转义。
pair.token1string- 第二个代币的符号,同上。
lpFeePpminteger ≥ 0 | null- 池子在每笔兑换中付给流动性提供者的手续费,以百万分之一计:
3000即 0.3%。当 v4 池子的 hook 逐笔设定手续费时为null。 price.basestring- 每个价格以其一个单位为准的代币。
price.quotestring- 每个价格所用的计价代币。
price.currentnumber > 0- 读取时池子的价格。
range.lowernumber > 0- 建议区间的下沿,以价格表示。
range.uppernumber > 0- 建议区间的上沿。
range.currentInRangeboolean- 当前价格是否在区间内。不在时,卡片会加一条提示。
range.lowerTruncatedboolean- 当池子的 tick 网格够不到价格带下沿、区间因此在它之前就截止时为
true。 range.upperTruncatedboolean- 同上,针对上沿。
parameters.horizonDaysinteger > 0- 绘制区间所用的时间跨度,以天计:始终是默认值 30。
parameters.standardDeviationMultipliernumber > 0- 绘制区间所用的宽度,以标准差计:始终是默认值 1σ。
hookMayAlterSwapsboolean- 对于 hook 可能改变兑换代价的 v4 池子为
true,从 hook 的地址读出;对于 v3、没有 hook 的 v4 池子,以及权限不涉及兑换的 hook,为false。为true时卡片会带一条提示。 analysedAtstring (ISO 8601)- 价格被读取的时间,UTC,精确到毫秒——不是这次响应发出的时间,后者可能晚几分钟。
poolUrlstring- 该池子在本站的页面,使用相同的时间跨度和宽度。
disclaimerstring- 这些数字是什么、不是什么,使用
lang指定的语言。
{
"protocol": "v3",
"chain": {
"id": 1,
"slug": "ethereum",
"name": "Ethereum"
},
"pool": "0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
"pair": {
"token0": "USDC",
"token1": "WETH"
},
"lpFeePpm": 3000,
"price": {
"base": "WETH",
"quote": "USDC",
"current": 3000
},
"range": {
"lower": 2824.2704157479307,
"upper": 3184.336896959513,
"currentInRange": true,
"lowerTruncated": false,
"upperTruncated": false
},
"parameters": {
"horizonDays": 30,
"standardDeviationMultiplier": 1
},
"hookMayAlterSwaps": false,
"analysedAt": "2026-08-21T09:15:00.000Z",
"poolUrl": "https://liquiditywise.com/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8&days=30&sigma=1",
"disclaimer": "For information only, not financial advice. The range is worked out from how far this pool's price moved over the last 30 days: it is not a forecast and not a recommendation."
}状态码与错误
卡片和 JSON 使用相同的状态码。两者都从不以本站自己的错误页面或内部出错的细节作答:卡片显示一句话和返回链接,JSON 则返回一个脚本可以检测的 error,以及 disclaimer——拒绝除外,它带的是等待时间。
JSON 的响应,以及可以保存多久:
200
数字:带数字的卡片,或上面的 JSON。
Cache-Control: public, max-age=300, s-maxage=300400 not-a-pool
地址没有指向本站读取的池子:既没有 address 也没有 id,或两者都有,或其中一个格式错误或出现两次,或是本站不读取的网络,或是只读取 v4 的网络(Unichain)上的 v3 池子。什么也没有读取,再问一次也不会改变答案。
Cache-Control: public, max-age=3600, s-maxage=3600
{
"error": "not-a-pool",
"disclaimer": "For information only, not financial advice. The range is worked out from how far this pool's price moved over the last 30 days: it is not a forecast and not a recommendation."
}503 unreadable
格式正确、但此刻无法读取的池子:某个数据源没有及时回应,或该网络上没有这个池子。poolUrl 仍然指向它的页面。一分钟后再问,也许就能读到。
Cache-Control: public, max-age=60, s-maxage=60
{
"error": "unreadable",
"poolUrl": "https://liquiditywise.com/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8&days=30&sigma=1",
"disclaimer": "For information only, not financial advice. The range is worked out from how far this pool's price moved over the last 30 days: it is not a forecast and not a recommendation."
}429 rate-limited
同一客户端的请求过多:见下文的请求限制。Retry-After 和 retryAfterSeconds 都说明要等待多少秒。卡片会用一个简短页面以自己的语言说明这一点。
Cache-Control: no-store
Retry-After: 42
{
"error": "rate-limited",
"retryAfterSeconds": 42
}缓存与请求限制
每个响应都说明自己可以被保存多久,对浏览器和共享缓存都一样:数字为 300 秒;无法读取的池子为 60 秒,以免恢复的池子长时间显示为无法读取;不指向池子的地址为 3,600 秒,因为以后任何时候都不会改变。拒绝从不保存。用同一个地址询问的每个人得到的响应都相同,这正是保存它是安全的原因。
在此之后,服务器会把每个池子的数字从读取那一刻起保存 300 秒,卡片和 JSON 共用,所有语言共用:同一个池子的每张卡片,无论放在哪个页面上,在过期之前都是同一次读取。无法读取的池子不保存,下一次请求时会重新读取。所以响应中的价格可能比响应本身更早:analysedAt 说明它是何时读取的。
指向格式正确的池子的请求会读取该池子,因此会计入与本站自己的池子页面相同的额度:每个客户端在 60 秒的窗口内最多 10 次请求,窗口从该客户端的第一次请求开始;客户端以请求到达本站时的 IP 地址区分。超出后,响应为 429,并附 Retry-After。每个到达本站的此类请求都会计数,包括由服务器保存的数据作答的请求;不指向池子的请求不计数。
在浏览器中,卡片或 fetch 计入加载它的读者,而不是放置它的网站——所以一个放了超过 10 张卡片的页面,超出的部分会对每位读者被拒绝。在服务器上,你发出的所有请求共用该服务器的同一个额度:请按响应允许的 300 秒保存每个响应,这几乎没有代价,因为在这段时间的大部分里,服务器本来也会用你手上那次读取作答。这个限制是为了压低读取池子的成本,本页没有任何内容承诺它会保持不变。
示例
curl -s "https://liquiditywise.com/api/embed/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8"const response = await fetch(
"https://liquiditywise.com/api/embed/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
);
const body = await response.json();
if (response.ok) {
// The range, in body.price.quote per one body.price.base
console.log(body.range.lower, body.range.upper, body.price.quote, body.price.base);
console.log(body.disclaimer);
} else if (response.status === 429) {
console.log(`Asked too often: wait ${body.retryAfterSeconds} seconds`);
} else {
console.log(body.error); // "not-a-pool" or "unreadable"
}<iframe src="https://liquiditywise.com/embed/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8" title="USDC / WETH on LiquidityWise" width="360" height="220" style="border:0;max-width:100%" loading="lazy"></iframe>单击即可选中整个代码块,方便复制。
条款,用大白话说
这些数字是测量,不是建议。区间是根据池子价格在过去 30 天里的波动幅度算出的:它不是预测,也不是推荐,每个响应都会自己说明这一点——卡片写在正面,JSON 写在 disclaimer 里。每个数字是怎么算出来的、各自漏掉了什么,见“工作原理”页面。
卡片带有自己的返回链接“由 LiquidityWise 分析”。JSON 没有署名字段,代码中也没有任何要求署名的内容;它带的是 poolUrl(该池子在本站的页面)和 disclaimer。把这两项放在数字旁边,读者就能知道数字从哪里来、是什么。
没有版本号,没有密钥,也不承诺响应的结构会保持不变,或网站在任何时刻都能访问。能保证的要窄得多:响应在发出前会与其 schema 核对,而测试把本页与该 schema 以及读取地址的代码绑在一起,所以本页描述的是当前实际提供的内容。两者的任何改动都会出现在代码的公开历史里。
代码是开源的,采用 MIT 许可证。