Карточка пула и её JSON
Две вещи на этом сайте сделаны для других сайтов: карточка с предложенным диапазоном одного пула, которую любая страница может поместить во фрейм, и те же цифры в JSON, которые может читать код любого сайта. Ни для одной не нужны ни ключ, ни учётная запись. Обе описаны здесь так, как их отдаёт код, и тесты привязывают эту страницу к этому коду: каждый параметр в ней — тот, по которому действительно читается адрес, а каждое поле — то, с чем ответ сверяется перед отправкой.
Карточка
/embed/pool — небольшая страница, написанная вручную, а не фреймворком сайта. На ней пара с протоколом, комиссией и сетью; предложенный диапазон с горизонтом и шириной, для которых он построен; текущая цена; строка о том, что это не финансовый совет; и ссылка обратно на страницу пула здесь, которая открывается в новой вкладке. Диапазон всегда строится по умолчаниям сайта — 30 дн. при 1σ, — какими бы ни были чьи-то собственные настройки. Пока текущая цена вне диапазона, добавляется примечание, и ещё одно — для пула v4, чей hook может изменить цену свопа.
В ней нет ни скрипта, ни формы, и она ничего не загружает: её собственная Content-Security-Policy разрешает только её встроенный стиль. Цвета у неё — цвета сайта, светлые или тёмные, как настроена система читателя. Страницу вокруг себя она не видит, и параметра, чтобы это выбрать, нет.
Дайте ей фрейм шириной 360 пикселей и высотой 220, или высотой 260 для пула, чей hook может изменить цену свопа: такая карточка несёт на одну строку больше. Ровно это предлагает страница каждого пула в разделе «Встроить этот пул», уже с нужной для этого пула высотой, и предложенный фрейм никогда не становится шире колонки, в которую его вставили.
Это единственная страница сайта, которую другой сайт может поместить во фрейм. Любой другой адрес, включая этот, отдаётся с 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 в сети, где v3 не читается (Unichain):
400, и ничего не читается. id- Принимает Id пула v4, хеш его ключа:
0xи 64 шестнадцатеричные цифры, в верхнем или нижнем регистре. - Иначе Всё остальное не называет пул:
400, и ничего не читается. Как иidрядом сaddress. chain- Принимает Короткое имя сети из таблицы ниже. Если его нет, сеть — Ethereum.
- Иначе Имя, которое сайт не читает, отклоняется и никогда не читается как Ethereum:
400. lang- Принимает Код языка из списка ниже — для слов карточки и
disclaimerв JSON; цифры на всех языках одни и те же. Если его нет — английский. - Иначе Любое другое значение или
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.quote стоит один price.base.
Перед отправкой ответ сверяется со своей схемой, и ответ, который ей не соответствует, не отправляется: вместо этого пул отвечается как непрочитанный. Поэтому в ответе со статусом 200 всегда ровно поля ниже — не больше и не меньше.
Его может читать скрипт любого сайта. Каждый ответ, включая ошибки и отказы, отдаётся с Access-Control-Allow-Origin: *. Он не ставит cookie и не требует учётных данных. Простому GET не нужен preflight, и на 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"v3для пула, названного поaddress,v4— для названного поid.chain.idinteger > 0- Chain id сети.
chain.slugstring- Короткое имя сети — в том виде, в каком его принимает
chain. chain.namestring- Название сети.
poolstring- Адрес (v3) или id (v4) пула, в нижнем регистре.
pair.token0string- Символ первого токена пула — так, как его указывает контракт: это текст из контракта, который может развернуть кто угодно, поэтому экранируйте его, прежде чем вставлять в страницу.
pair.token1string- Символ второго токена, так же.
lpFeePpminteger ≥ 0 | null- Комиссия, которую пул платит поставщикам ликвидности за своп, в миллионных долях:
3000— это 0,3%.null, когда hook пула v4 задаёт комиссию для каждого свопа отдельно. price.basestring- Токен, за одну единицу которого дана каждая цена.
price.quotestring- Токен, в котором считается каждая цена.
price.currentnumber > 0- Цена пула в момент чтения.
range.lowernumber > 0- Нижняя граница предложенного диапазона, как цена.
range.uppernumber > 0- Его верхняя граница.
range.currentInRangeboolean- Находится ли текущая цена внутри диапазона. Если нет, карточка добавляет примечание.
range.lowerTruncatedbooleantrue, когда сетка tick’ов пула не дотягивается до нижней границы полосы, и диапазон кончается раньше неё.range.upperTruncatedboolean- То же — для верхней границы.
parameters.horizonDaysinteger > 0- Горизонт, для которого построен диапазон, в днях: всегда значение по умолчанию, 30.
parameters.standardDeviationMultipliernumber > 0- Ширина, для которой он построен, в стандартных отклонениях: всегда значение по умолчанию, 1σ.
hookMayAlterSwapsbooleantrueдля пула v4, чей hook может изменить цену свопа, — это читается из адреса hook’а;falseдля v3, для пула v4 без hook’а и для hook’а, чьи разрешения не затрагивают свопы. Где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, или есть оба, или один из них искажён либо повторён, или это сеть, которую сайт не читает, или пул v3 там, где читается только v4 (Unichain). Ничего не прочитано, и повторный запрос ответа не изменит.
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 говорит, когда она прочитана.
Запрос, называющий правильно записанный пул, читает этот пул и поэтому считается в тот же лимит, что и собственные страницы пулов на сайте: 10 запросов на клиента в окне 60 с, которое начинается с первого запроса клиента; клиент — это 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. Показанные рядом с цифрами, эти два поля говорят читателю, откуда цифры и что они такое.
Нет ни номера версии, ни ключа, ни обещания, что форма ответа останется прежней или что сайт будет доступен в любой момент. Действует более узкое: ответ сверяется со своей схемой перед отправкой, а тесты привязывают эту страницу к этой схеме и к коду, который читает адрес, так что она описывает то, что отдаётся сейчас. Изменение любого из них видно в публичной истории кода.
Код открыт, под лицензией MIT.