跳到正文
归海

基于 归海 开发

一套 HTTP API,直取 Robinhood Chain 最优价格。报价任意交易对,拿到模拟过的交易交由你自己的钱包签名,并追踪到结算。

总览

The same API that runs the 归海 swap page is open to everyone: list tokens, read balances, get the best price across Uniswap v3, Uniswap v4 and the outside pools that Aggregator Hooks plug into, then build a ready-to-sign Universal Router transaction. Trades are non-custodial: the API never holds funds or keys, your wallet signs and sends every transaction itself.

Base URL

https://hookline.trade/api

Conventions

Format基于 HTTPS 的 JSON
POST bodies are JSON with Content-Type: application/json. Every answer is JSON, never cached when it depends on the caller.
代币symbol or address
Pass a listed symbol (ETH, usdg, case does not matter) or a 0x address. Native ETH is 0x0000000000000000000000000000000000000000. Ambiguous symbols need the address.
Amountsdecimal strings
Requests take human decimals ("0.5"). Responses give both: amount is human, fields ending in Atomic and balances are integer strings in the token's smallest unit. Keep them as bigints.
USDnumber
USD values are plain numbers rounded to cents, for display only. null means the price is unknown, never zero.
ChainRobinhood Chain
Chain id 4663. Explorer: robinhoodchain.blockscout.com.

限流与密钥

Limits count requests per fixed one-minute window. Without a key they apply per IP address.

Default limits

报价与构建60 per minute
/api/quote and /api/swap, the calls that route and simulate.
Reads240 per minute
Tokens, balances, hook pools, stats, tracking and transaction status.
X-Api-Keyheader
A key lifts the quote and build limit to its own rate (reads get four times that rate). Keys start with hkl_; create one instantly on the Build on 归海 page (see API keys). An invalid or revoked key gets 401, it does not fall back to the IP limit.
429Retry-After
Over the limit, the answer is 429 rate_limited with a Retry-After header in seconds. Wait that long before the next call.
CORSopen
Every public endpoint answers any origin, accepts the Content-Type and X-Api-Key headers and exposes Retry-After, so a browser app can call it directly. Keep keys on your server.
带密钥调用
curl -s "https://hookline.trade/api/quote?sell=ETH&buy=USDG&amount=0.5" \
  -H "X-Api-Key: hkl_your_key"
429 Too Many Requests
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry in 17s."
  }
}

端点

Fifteen public endpoints, plus the key endpoint under API keys. Reads are cached for a few seconds at the edge; anything that depends on the caller is never cached.

代币

GET/api/tokens

Every token you can trade, with its USD price. kind is base (ETH, WETH, USDG), pons (a Pons launchpad token) or listed (listed automatically from Aggregator Hook pools: check its contract before buying). viaHooks is true when some Aggregator Hook pool trades it. Cached 60 seconds.

请求

curl -s "https://hookline.trade/api/tokens"

响应

200 OK
{
  "tokens": [
    {
      "address": "0x0000000000000000000000000000000000000000",
      "symbol": "ETH",
      "name": "Ether",
      "decimals": 18,
      "kind": "base",
      "priceUsd": 2405.05,
      "viaHooks": true
    },
    {
      "address": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
      "symbol": "USDG",
      "name": "Global Dollar",
      "decimals": 6,
      "kind": "base",
      "priceUsd": 1,
      "viaHooks": true
    },
    {
      "address": "0x07ebb29a38fbcb41563817e5e19f2cec619c90d2",
      "symbol": "BUN",
      "name": "Bundle Cat",
      "decimals": 18,
      "kind": "pons",
      "priceUsd": 0.0456633451,
      "viaHooks": true
    }
  ],
  "updatedAt": 1792681673
}

余额

GET/api/balances?address=0x…

A wallet's balance of every listed token, read in one call. Keys are lowercase token addresses, ETH sits under the zero address, values are atomic integer strings.

参数

addressquery, 0x address, required
The wallet to read.

请求

curl -s "https://hookline.trade/api/balances?address=0x1111111111111111111111111111111111111111"

响应

200 OK
{
  "address": "0x1111111111111111111111111111111111111111",
  "balances": {
    "0x0000000000000000000000000000000000000000": "60679951590011889",
    "0x5fc5360d0400a0fd4f2af552add042d716f1d168": "500000",
    "0x39dbed3a2bd333467115de45665cc57f813c4571": "43789961722725797152"
  },
  "updatedAt": 1792681673
}

Quote

POST / GET/api/quote

The best exact-input price for selling amount of sell. The order can be split across up to 4 paths (one or two pools each), Aggregator Hook pools included, with gas priced in. GET takes the same fields as query parameters. The quote is stored for about 60 seconds so it can be built.

参数

sellstring, required
Token sold: symbol or 0x address.
buystring, required
Token bought: symbol or 0x address.
amountdecimal string, required
Human amount of the sold token, above 0, no more decimals than the token has.
taker0x address, optional
The wallet that will swap. Needed to get steps, the balance warning and to build the transaction.
slippageBpsinteger, optional
Tolerance in basis points, 1 to 500. Default 50 (0.5%).
partnerFeeBpsinteger, optional
With an API key that has a payout address: your own fee in bps, up to your key's cap (50 at most), paid on chain to your payout address. See 合作伙伴抽成.

Response fields

idstring
Quote id for /api/swap and tracking.
taker0x address or null
The wallet the quote and its steps were made for; null for a price-only quote. Check it matches your signer.
sell, buyobject
Token, symbol, decimals, amount (human), amountAtomic and USD value. buy is what you receive after the fee.
minReceivedobject
Least you receive after slippage and fee. The transaction reverts below it.
priceImpactBpsinteger
Price impact against the mid price. Above 3% the quote carries a warning; at 20% it is refused.
feeobject
归海 fee in basis points and in the bought token; holder (waivedBps, token, minBalance) when it was waived because the taker holds at least 1,000,000 GUIHAI, then bps is 0, else null; and partner: the builder fee (bps, amount, amountAtomic, recipient) when the call carried a partnerFeeBps, else null.
partsarray
The split: shareBps of the amount down each path, with its legs (venue, pool, fee label).
sourcesstring[]
Every venue the order touches, in route order.
vsUniswapOnly对象或 null
Best result with Uniswap pools alone (split included, no 归海 fee) and how much more this quote gives after the fee, in bps; the same baseline as savings. null when no Aggregator Hook pool is used.
vsSinglePath对象或 null
Best single path and what splitting adds. null when the order is not split.
gasobject
Estimated gas units and their USD cost.
savings对象或 null
Proof of Savings projection. baseline is the best route using Uniswap pools alone, split included (no 归海 fee); saved is what this quote gives after the fee minus that baseline, never below 0, with savedUsd and savedBps. Small orders often save 0.
routesarray
Every route the engine evaluated for the whole amount, best first: legs, venues, viaHooks, amountOut alone (null without liquidity at this size), vsBestBps against the chosen result, usedShareBps (above 0 when the route is part of the result) and rank.
searchobject
What was looked at: routesEvaluated, routesQuotable, routesViaHooks, poolsConsidered, hookPoolsConsidered and hookPoolsOnChain.
stepsarray
Approvals the taker still needs: approve (a transaction) then permit (a signature). Empty when ready to build, and always empty without taker.
warningsstring[]
Readable warnings: high price impact, automatically listed token, balance below the amount.
expiresAtunix seconds
After this the quote cannot be built.

请求

curl -s -X POST "https://hookline.trade/api/quote" \
  -H "Content-Type: application/json" \
  -d '{"sell":"USDG","buy":"PONS","amount":"20000","taker":"0x1111111111111111111111111111111111111111","slippageBps":50}'

响应

200 OK
{
  "id": "q_cjrwryzgblvfyihvhbahfqcp",
  "taker": "0x1111111111111111111111111111111111111111",
  "sell": {
    "token": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
    "symbol": "USDG",
    "decimals": 6,
    "amount": "20000",
    "amountAtomic": "20000000000",
    "usd": 20000
  },
  "buy": {
    "token": "0x39dbed3a2bd333467115de45665cc57f813c4571",
    "symbol": "PONS",
    "decimals": 18,
    "amount": "77880.86043419",
    "amountAtomic": "77880860434195477994280",
    "usd": 19959.42
  },
  "minReceived": {
    "amountAtomic": "77491456132024500604308",
    "amount": "77491.45613202"
  },
  "slippageBps": 50,
  "priceImpactBps": 30,
  "fee": {
    "bps": 0,
    "amountAtomic": "0",
    "amount": "0",
    "holder": null,
    "partner": null
  },
  "parts": [
    {
      "shareBps": 8000,
      "amountIn": "16000",
      "amountOut": "62323.22364486",
      "legs": [
        {
          "from": "USDG",
          "to": "PONS",
          "venue": "Slipstream",
          "pool": "0xf7e11343da17c02cf40edb94ae634c349ecf6d15559048ca1a8f6222a0209052",
          "feeLabel": "outside pool fee"
        }
      ]
    },
    {
      "shareBps": 1000,
      "amountIn": "2000",
      "amountOut": "7785.72818795",
      "legs": [
        {
          "from": "USDG",
          "to": "WETH",
          "venue": "Uniswap v3",
          "pool": "0x52e65b17fb6e5ba00ed806f37afcd2daa50271ca",
          "feeLabel": "0.01%"
        },
        {
          "from": "WETH",
          "to": "PONS",
          "venue": "Uniswap v3",
          "pool": "0xed50bdeea8adc232f159486192a4157281d722ff",
          "feeLabel": "0.3%"
        }
      ]
    },
    {
      "shareBps": 1000,
      "amountIn": "2000",
      "amountOut": "7771.90860137",
      "legs": [
        {
          "from": "USDG",
          "to": "PONS",
          "venue": "Uniswap v4",
          "pool": "0x4be9657ec9002e528f4f17a5c43edc525a07f888f7b180c2afbf75e096c4f38a",
          "feeLabel": "0.3%"
        }
      ]
    }
  ],
  "sources": [
    "Slipstream",
    "Uniswap v3",
    "Uniswap v4"
  ],
  "vsUniswapOnly": {
    "amountAtomic": "77375900250980508271801",
    "amount": "77375.90025098",
    "gainBps": 65
  },
  "vsSinglePath": {
    "amountAtomic": "77851683723637453929419",
    "amount": "77851.68372363",
    "gainBps": 3
  },
  "gas": {
    "units": "503488",
    "usd": 0.02459
  },
  "savings": {
    "baselineAtomic": "78408162801775847113053",
    "baseline": "78408.16280177",
    "savedAtomic": "422980575370604276142",
    "saved": "422.98057537",
    "savedUsd": 107.03,
    "savedBps": 53
  },
  "routes": [
    {
      "legs": [
        {
          "from": "USDG",
          "to": "PONS",
          "venue": "Slipstream",
          "pool": "0xf7e11343da17c02cf40edb94ae634c349ecf6d15559048ca1a8f6222a0209052",
          "feeLabel": "outside pool fee"
        }
      ],
      "venues": [
        "Slipstream"
      ],
      "viaHooks": true,
      "amountOut": "78797.8606506",
      "vsBestBps": -4,
      "usedShareBps": 7000,
      "rank": 1
    },
    {
      "legs": [
        {
          "from": "USDG",
          "to": "WETH",
          "venue": "Uniswap v3",
          "pool": "0x69bfaf19c9f377bb306a89aed9f6b07e2c1a8d9a",
          "feeLabel": "0.05%"
        },
        {
          "from": "WETH",
          "to": "PONS",
          "venue": "Uniswap v3",
          "pool": "0xed50bdeea8adc232f159486192a4157281d722ff",
          "feeLabel": "0.3%"
        }
      ],
      "venues": [
        "Uniswap v3"
      ],
      "viaHooks": false,
      "amountOut": "78242.20783838",
      "vsBestBps": -74,
      "usedShareBps": 0,
      "rank": 3
    },
    {
      "legs": [
        {
          "from": "USDG",
          "to": "WETH",
          "venue": "Slipstream",
          "pool": "0x007768bf8a7686118066323a69e35e3facc76c6b61cb530f95581a5f6aa47b97",
          "feeLabel": "outside pool fee"
        },
        {
          "from": "WETH",
          "to": "PONS",
          "venue": "Uniswap v4",
          "pool": "0x4ce1ef533e1535b04b47326514b3d214a13b48f0dc37a54e28ae2fc0bf85784d",
          "feeLabel": "1%"
        }
      ],
      "venues": [
        "Slipstream",
        "Uniswap v4"
      ],
      "viaHooks": true,
      "amountOut": null,
      "vsBestBps": null,
      "usedShareBps": 0,
      "rank": null
    }
  ],
  "search": {
    "routesEvaluated": 96,
    "routesQuotable": 76,
    "routesViaHooks": 36,
    "poolsConsidered": 25,
    "hookPoolsConsidered": 6,
    "hookPoolsOnChain": 102
  },
  "steps": [
    {
      "kind": "approve",
      "token": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
      "spender": "0x000000000022d473030f116ddee9f6b43ac78ba3",
      "tx": {
        "to": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
        "data": "0x095ea7b3000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
        "value": "0"
      }
    },
    {
      "kind": "permit",
      "token": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
      "typedData": {
        "domain": {
          "name": "Permit2",
          "chainId": 4663,
          "verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3"
        },
        "types": {
          "PermitDetails": [
            {
              "name": "token",
              "type": "address"
            },
            {
              "name": "amount",
              "type": "uint160"
            },
            {
              "name": "expiration",
              "type": "uint48"
            },
            {
              "name": "nonce",
              "type": "uint48"
            }
          ],
          "PermitSingle": [
            {
              "name": "details",
              "type": "PermitDetails"
            },
            {
              "name": "spender",
              "type": "address"
            },
            {
              "name": "sigDeadline",
              "type": "uint256"
            }
          ]
        },
        "primaryType": "PermitSingle",
        "message": {
          "details": {
            "token": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
            "amount": "20000000000",
            "expiration": 1792683472,
            "nonce": 0
          },
          "spender": "0x204faca1764b154221e35c0d20abb3c525710498",
          "sigDeadline": "1792683472"
        }
      }
    }
  ],
  "warnings": [],
  "expiresAt": 1792681732
}

构建兑换

POST/api/swap

Turns a quote made with taker into one Universal Router transaction, simulated from the taker against the latest block before it is returned. gas is the estimate plus 25% (or null: let the wallet estimate). The router deadline is 5 minutes (expiresAt).

参数

quoteIdstring, required
Id of an unexpired quote made with taker.
permitSignature0x hex, 65 bytes, optional
Required when the quote had a permit step: the taker's EIP-712 signature of it.

请求

curl -s -X POST "https://hookline.trade/api/swap" \
  -H "Content-Type: application/json" \
  -d '{"quoteId":"q_cjrwryzgblvfyihvhbahfqcp","permitSignature":"0x…"}'

响应

200 OK
{
  "quoteId": "q_qzqlh4wuo3apkyktbn6y3hks",
  "tx": {
    "to": "0x204faca1764b154221e35c0d20abb3c525710498",
    "data": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000…",
    "value": "1000000000000000",
    "chainId": 4663,
    "gas": "176412"
  },
  "simulated": true,
  "expiresAt": 1792682267
}

追踪兑换

POST/api/swaps/track

Registers the hash your wallet returned for a built quote (one hash per quote) and reports its status. It counts only when the transaction comes from the taker, goes to the Universal Router and carries exactly the built calldata; anything else ends as unknown.

参数

quoteIdstring, required
The built quote.
txHash0x hash, required
Hash returned by the wallet.

请求

curl -s -X POST "https://hookline.trade/api/swaps/track" \
  -H "Content-Type: application/json" \
  -d '{"quoteId":"q_cjrwryzgblvfyihvhbahfqcp","txHash":"0x5e1f…1e0f"}'

响应

200 OK
{
  "hash": "0x5e1f0c3b9a7d4e2f8c6b1a0d9e8f7c6b5a4d3e2f1c0b9a8d7e6f5c4b3a2d1e0f",
  "status": "pending",
  "amountOut": null,
  "blockNumber": null
}

兑换状态

GET/api/swaps/:hash

Status of a tracked swap: pending, confirmed, failed (reverted) or unknown (not the built transaction, or never seen on chain). Pending swaps are re-checked on chain at most every 3 seconds, so poll every 2. amountOut is the atomic amount the taker received once confirmed.

参数

:hashpath, 0x hash, required
A hash registered with /api/swaps/track.

请求

curl -s "https://hookline.trade/api/swaps/0x5e1f0c3b9a7d4e2f8c6b1a0d9e8f7c6b5a4d3e2f1c0b9a8d7e6f5c4b3a2d1e0f"

响应

200 OK
{
  "hash": "0x5e1f0c3b9a7d4e2f8c6b1a0d9e8f7c6b5a4d3e2f1c0b9a8d7e6f5c4b3a2d1e0f",
  "status": "confirmed",
  "amountOut": "2390894",
  "blockNumber": 95206412
}

交易状态

GET/api/tx/:hash

Receipt status of any Robinhood Chain transaction: pending (no receipt yet), success or reverted. Handy to wait for an approval.

参数

:hashpath, 0x hash, required
Any transaction hash.

请求

curl -s "https://hookline.trade/api/tx/0x8a2b6c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b"

响应

200 OK
{
  "hash": "0x8a2b6c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b",
  "status": "success",
  "blockNumber": 95206390
}

省钱证明

GET/api/proof/:hash

The public Proof of Savings of a swap settled through 归海: what was sold and received, the baseline (best route using Uniswap pools alone, split included, quoted at the same moment), saved (amount, USD and bps above that baseline), the route with its legs, viaHooks, the block and an explorer link to verify it on chain. baseline and saved are null when no Uniswap-only route existed or for swaps made before savings were recorded, as in this real example. Unknown or unmatched hashes answer 404.

参数

:hashpath, 0x hash, required
A swap tracked through /api/swaps/track.

请求

curl -s "https://hookline.trade/api/proof/0x98d11e2b3d6e0118bd471f5f8301bf1574234b8c7734650920c9fa90f284955a"

响应

200 OK
{
  "hash": "0x98d11e2b3d6e0118bd471f5f8301bf1574234b8c7734650920c9fa90f284955a",
  "status": "confirmed",
  "taker": "0x03352f25c628e8f308ade3ed04b09d05a28f0f51",
  "sell": {
    "token": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
    "symbol": "USDG",
    "amount": "0.05"
  },
  "buy": {
    "token": "0x0000000000000000000000000000000000000000",
    "symbol": "ETH",
    "amount": "0.00002085"
  },
  "baseline": null,
  "saved": null,
  "route": [],
  "viaHooks": false,
  "blockNumber": 95217220,
  "createdAt": 1792683081,
  "explorerUrl": "https://robinhoodchain.blockscout.com/tx/0x98d11e2b3d6e0118bd471f5f8301bf1574234b8c7734650920c9fa90f284955a"
}

Wallet history

GET/api/swaps?taker=0x…

A wallet's swaps through 归海, newest first: status, pair, amounts, USD value, what each saved and its explorer link, plus totals (swaps, volume and savings in USD). Trimmed here to one swap.

参数

takerquery, 0x address, required
The wallet whose swaps to list.

请求

curl -s "https://hookline.trade/api/swaps?taker=0x03352f25C628e8F308adE3Ed04b09d05A28F0f51"

响应

200 OK
{
  "taker": "0x03352f25c628e8f308ade3ed04b09d05a28f0f51",
  "swaps": [
    {
      "hash": "0x76701382ece70e92e462e3308e9549deb90cd88515c3c9c0b1454095aba46750",
      "status": "confirmed",
      "sell": {
        "symbol": "ETH",
        "amount": "0.00001"
      },
      "buy": {
        "symbol": "USDG",
        "amount": "0.023883"
      },
      "usd": 0.02,
      "saved": null,
      "createdAt": 1792683106,
      "explorerUrl": "https://robinhoodchain.blockscout.com/tx/0x76701382ece70e92e462e3308e9549deb90cd88515c3c9c0b1454095aba46750"
    }
  ],
  "totals": {
    "swaps": 2,
    "volumeUsd": 0.07,
    "savedUsd": 0
  }
}

Hook 池子

GET/api/hooks

Every Aggregator Hook pool on Robinhood Chain: venue, tokens, outside reserves, USD liquidity, 24 hour volume and swaps, the protocol fee the hook takes, and routable (true when 归海 routes through it). Also per-venue totals and the hooks themselves. Cached 30 seconds.

请求

curl -s "https://hookline.trade/api/hooks"

响应

200 OK
{
  "pools": [
    {
      "id": "0xf7e11343da17c02cf40edb94ae634c349ecf6d15559048ca1a8f6222a0209052",
      "hook": "0xa117c4f25474b1283345e4df8d88b9aec4f62888",
      "venue": "Slipstream",
      "token0": {
        "address": "0x39dbed3a2bd333467115de45665cc57f813c4571",
        "symbol": "PONS",
        "decimals": 18,
        "listed": true
      },
      "token1": {
        "address": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
        "symbol": "USDG",
        "decimals": 6,
        "listed": true
      },
      "tickSpacing": 50,
      "reserve0": "2746122.8435",
      "reserve1": "249549.6741",
      "liquidityUsd": 953330.07,
      "volume24hUsd": 7088.94,
      "swaps24h": 29,
      "protocolFeeBps": 10,
      "routable": true
    }
  ],
  "venues": [
    {
      "venue": "Slipstream",
      "pools": 102,
      "wrappedLiquidityUsd": 7415896.11,
      "volume24hUsd": 20936.24
    }
  ],
  "hooks": [
    {
      "address": "0xa1ad09c98710d2c709e3bea03708823255312888",
      "venue": "Slipstream",
      "pools": 56,
      "version": "Slipstream aggregator"
    }
  ],
  "updatedAt": 1792681967
}

Stats

GET/api/stats

Network numbers: hook pools, venues, tokens, wrapped outside liquidity, 24 hour hook volume and swaps (all routers), protocol fees, swaps and volume settled through 归海, and the Proof of Savings total in USD (hooklineSavedUsd). A field is null while it cannot be established. Cached 30 seconds.

请求

curl -s "https://hookline.trade/api/stats"

响应

200 OK
{
  "hookPools": 102,
  "venues": [
    "Slipstream"
  ],
  "tokens": 36,
  "wrappedLiquidityUsd": 7437687.58,
  "hookVolume24hUsd": 20382.59,
  "hookSwaps24h": 111,
  "protocolFeesUsd": 18.36,
  "hooklineSwaps": 2,
  "hooklineVolumeUsd": 0.07,
  "hooklineSavedUsd": 0,
  "updatedAt": 1792691023
}

代币行情列表

GET/api/analytics/tokens

Every listed token with its USD price, the 24 hour change and an hourly sparkline from its most traded Aggregator Hook pool (change24h is null until that pool has a full day of trades), the liquidity of its hook pools and their 24 hour volume and swaps. coverage says since when swaps are indexed; windowed numbers are null while the indexer is more than an hour behind. Volumes are valued at current prices. Cached 60 seconds. This is what /tokens shows.

请求

curl -s "https://hookline.trade/api/analytics/tokens"

响应

200 OK
{
  "tokens": [
    {
      "address": "0x39dbed3a2bd333467115de45665cc57f813c4571",
      "symbol": "PONS",
      "name": "Pons",
      "kind": "pons",
      "priceUsd": 0.2499791586,
      "change24h": null,
      "spark": [
        0.2556,
        0.2564,
        0.252,
        0.2501,
        null,
        null,
        "…"
      ],
      "liquidityUsd": 1645000.23,
      "volume24hUsd": 15063.16,
      "swaps24h": 62,
      "hookPools": 6,
      "uniswapPools": 11
    },
    "…"
  ],
  "coverage": {
    "since": 1792581095,
    "indexedAt": 1792699426,
    "fresh": true
  },
  "updatedAt": 1792699462
}

Token analytics

GET/api/analytics/tokens/:token

One token: price and 24 hour change, a chart (chart.price names the pool whose trades give the line, base priced in quote, never in USD; it is null when no pool gives an honest price, then the chart is volume only), buys and sells over 24 hours, every pool that trades it (hook pools with their numbers, then the native Uniswap pools 归海 also routes through) and its 25 latest hook swaps. A swap is a buy when the trader received the token. 404 when the token is not listed. Cached 60 seconds.

参数

tokenpath, required
A listed symbol (PONS, ETH) or a 0x address. ETH and WETH cover the same pools.

请求

curl -s "https://hookline.trade/api/analytics/tokens/PONS"

响应

200 OK
{
  "token": {
    "address": "0x39dbed3a2bd333467115de45665cc57f813c4571",
    "symbol": "PONS",
    "name": "Pons",
    "decimals": 18,
    "kind": "pons",
    "priceUsd": 0.2499791586,
    "viaHooks": true
  },
  "change24h": null,
  "chart": {
    "price": {
      "poolId": "0xf7e11343da17c02cf40edb94ae634c349ecf6d15559048ca1a8f6222a0209052",
      "venue": "Slipstream",
      "base": {
        "address": "0x39dbed3a2bd333467115de45665cc57f813c4571",
        "symbol": "PONS",
        "decimals": 18,
        "listed": true
      },
      "quote": {
        "address": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
        "symbol": "USDG",
        "decimals": 6,
        "listed": true
      }
    },
    "bucketSeconds": 3600,
    "from": 1792580400,
    "to": 1792699462,
    "points": [
      {
        "t": 1792695600,
        "price": null,
        "volumeUsd": 14.9,
        "swaps": 1
      },
      {
        "t": 1792699200,
        "price": null,
        "volumeUsd": 0,
        "swaps": 0
      },
      "…"
    ]
  },
  "stats": {
    "liquidityUsd": 1645000.23,
    "reserve": "4854208.2944",
    "volume24hUsd": 15063.16,
    "volume7dUsd": 15063.16,
    "swaps24h": 62,
    "swaps7d": 62,
    "buys24h": 24,
    "sells24h": 38,
    "buyVolume24hUsd": 3248.91,
    "sellVolume24hUsd": 11814.25,
    "hookPools": 6,
    "uniswapPools": 11
  },
  "pools": [
    {
      "id": "0xf7e11343da17c02cf40edb94ae634c349ecf6d15559048ca1a8f6222a0209052",
      "kind": "hook",
      "venue": "Slipstream",
      "token0": {
        "address": "0x39dbed3a2bd333467115de45665cc57f813c4571",
        "symbol": "PONS",
        "decimals": 18,
        "listed": true
      },
      "token1": {
        "address": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
        "symbol": "USDG",
        "decimals": 6,
        "listed": true
      },
      "feeLabel": "0.1% Uniswap fee",
      "liquidityUsd": 646478.06,
      "volume24hUsd": 13574.29,
      "swaps24h": 43,
      "routable": true
    },
    "…"
  ],
  "recent": [
    {
      "ts": 1792698027,
      "tx": "0x368d7ca501d7a6a61d5edfb9ba954ee118105c6a88e42af267cda58ddce952c2",
      "poolId": "0x04f8b629af24112c2df52ff68de87e7565358389dbdd566da2897543d48a1db5",
      "venue": "Slipstream",
      "side": "buy",
      "token": {
        "address": "0x39dbed3a2bd333467115de45665cc57f813c4571",
        "symbol": "PONS",
        "decimals": 18,
        "listed": true
      },
      "amount": "59.601096",
      "counter": {
        "address": "0x0bd7d308f8e1639fab988df18a8011f41eacad73",
        "symbol": "WETH",
        "decimals": 18,
        "listed": true
      },
      "counterAmount": "0.006252",
      "usd": 14.9
    },
    "…"
  ],
  "coverage": {
    "since": 1792581095,
    "indexedAt": 1792699426,
    "fresh": true
  },
  "updatedAt": 1792699462
}

Pool analytics

GET/api/analytics/pools/:id

One Aggregator Hook pool: the explorer row plus the hook's verification, the last price (token0 in token1, hourly volume-weighted) and its 24 hour change, the chart, 24 hour and 7 day volume and swaps, the totals indexed since the start, and its 25 latest swaps read from token0's side. 400 for a malformed id, 404 for an unknown pool. Cached 60 seconds.

参数

idpath, required
The Aggregator Hook pool id (bytes32 hex), as /api/hooks lists it.

请求

curl -s "https://hookline.trade/api/analytics/pools/0xf7e11343da17c02cf40edb94ae634c349ecf6d15559048ca1a8f6222a0209052"

响应

200 OK
{
  "pool": {
    "id": "0xf7e11343da17c02cf40edb94ae634c349ecf6d15559048ca1a8f6222a0209052",
    "hook": "0xa117c4f25474b1283345e4df8d88b9aec4f62888",
    "venue": "Slipstream",
    "token0": {
      "address": "0x39dbed3a2bd333467115de45665cc57f813c4571",
      "symbol": "PONS",
      "decimals": 18,
      "listed": true
    },
    "token1": {
      "address": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
      "symbol": "USDG",
      "decimals": 6,
      "listed": true
    },
    "tickSpacing": 50,
    "reserve0": "1741798.3513",
    "reserve1": "211064.7774",
    "liquidityUsd": 646478.06,
    "volume24hUsd": 13574.29,
    "swaps24h": 43,
    "protocolFeeBps": 10,
    "routable": true,
    "hookVerified": true,
    "hookTrust": "known",
    "lpFee": 8388608
  },
  "price": 0.250054,
  "change24h": null,
  "chart": {
    "price": {
      "poolId": "0xf7e11343da17c02cf40edb94ae634c349ecf6d15559048ca1a8f6222a0209052",
      "venue": "Slipstream",
      "base": {
        "address": "0x39dbed3a2bd333467115de45665cc57f813c4571",
        "symbol": "PONS",
        "decimals": 18,
        "listed": true
      },
      "quote": {
        "address": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
        "symbol": "USDG",
        "decimals": 6,
        "listed": true
      }
    },
    "bucketSeconds": 3600,
    "from": 1792580400,
    "to": 1792699462,
    "points": [
      {
        "t": 1792699200,
        "price": null,
        "volumeUsd": 0,
        "swaps": 0
      },
      "…"
    ]
  },
  "stats": {
    "volume24hUsd": 13574.29,
    "volume7dUsd": 13574.29,
    "swaps24h": 43,
    "swaps7d": 43,
    "indexedSwaps": 43,
    "indexedVolumeUsd": 13574.29
  },
  "recent": [
    {
      "ts": 1792695055,
      "tx": "0x290ed4079eec9455c9d04f127763445247a1e1c80ee14ad7d92d3a8297f06b5c",
      "poolId": "0xf7e11343da17c02cf40edb94ae634c349ecf6d15559048ca1a8f6222a0209052",
      "venue": "Slipstream",
      "side": "buy",
      "token": {
        "address": "0x39dbed3a2bd333467115de45665cc57f813c4571",
        "symbol": "PONS",
        "decimals": 18,
        "listed": true
      },
      "amount": "199.384948",
      "counter": {
        "address": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
        "symbol": "USDG",
        "decimals": 6,
        "listed": true
      },
      "counterAmount": "50.013986",
      "usd": 50.02
    },
    "…"
  ],
  "coverage": {
    "since": 1792581095,
    "indexedAt": 1792699426,
    "fresh": true
  },
  "updatedAt": 1792699462
}

Health

GET/api/health

A cheap liveness probe for uptime monitors: latest block, age of the market registry in seconds and whether swaps are open. Answers 503 when the chain or the registry is unavailable. Not rate limited.

请求

curl -s "https://hookline.trade/api/health"

响应

200 OK
{
  "ok": true,
  "block": 95206308,
  "registryAge": 96,
  "swapsOpen": true
}

机器人兑换流程

Six calls take an order from a price to a settled trade. Your code holds the key and signs; the API only prepares and checks.

  1. Quote with your address

    POST /api/quote with taker set to the wallet that will sign. The answer holds the route, minReceived and the steps that wallet still needs. A quote lives for about 60 seconds (expiresAt).

  2. Send the approve step, once per token

    When steps has kind: "approve", send step.tx as is: an ERC-20 approve of Permit2 on the token you sell. Wait for the receipt (GET /api/tx/:hash or your own RPC), then quote again: the steps change once it lands. Selling ETH never needs it.

  3. Sign the permit step

    A kind: "permit" step carries a Permit2 PermitSingle as EIP-712 typed data: a short-lived allowance (30 minutes) for exactly this amount, spendable only by the Universal Router. Sign it with eth_signTypedData_v4 or viem's signTypedData: the typed data is complete, EIP712Domain included, so it goes to the wallet as is.

  4. Build the transaction

    POST /api/swap with quoteId and permitSignature when there was a permit step. The API checks the signature, encodes the Universal Router call and simulates it against the latest block. A 422 means it would fail right now; the message says why.

  5. Verify, then send

    Check tx.to is the Universal Router, tx.chainId is 4663 and tx.value equals the ETH you sell (else "0"). Send it from the taker within 5 minutes (expiresAt), without changing data.

  6. Track it

    POST /api/swaps/track with the quote id and the hash your wallet returned, then poll GET /api/swaps/:hash every 2 seconds until the status is confirmed, failed or unknown.

Signing the permit with a browser wallet

eth_signTypedData_v4
// step = quote.steps.find((s) => s.kind === "permit")
// step.typedData is complete (EIP712Domain included): pass it to the wallet as is.
const permitSignature = await provider.request({
  method: "eth_signTypedData_v4",
  params: [account, JSON.stringify(step.typedData)],
});

A complete bot with viem

bot.ts
import { createPublicClient, createWalletClient, defineChain, getAddress, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";

const BASE = "https://hookline.trade/api";
const API_KEY = process.env.API_KEY; // optional, raises your rate limit
const UNIVERSAL_ROUTER = getAddress("0x204faca1764b154221e35c0d20abb3c525710498");
const PERMIT2 = getAddress("0x000000000022d473030f116ddee9f6b43ac78ba3");
const NATIVE_ETH = "0x0000000000000000000000000000000000000000";

const robinhood = defineChain({
  id: 4663,
  name: "Robinhood Chain",
  nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
  rpcUrls: { default: { http: ["https://rpc.mainnet.chain.robinhood.com"] } },
});
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const wallet = createWalletClient({ account, chain: robinhood, transport: http() });
const client = createPublicClient({ chain: robinhood, transport: http() });

type Step =
  | { kind: "approve"; token: string; spender: string; tx: { to: Hex; data: Hex; value: "0" } }
  | { kind: "permit"; token: string; typedData: PermitTypedData };
type PermitTypedData = {
  domain: { name: string; chainId: number; verifyingContract: Hex };
  types: Record<string, { name: string; type: string }[]>;
  primaryType: "PermitSingle";
  message: { details: { token: Hex; amount: string; expiration: number; nonce: number }; spender: Hex; sigDeadline: string };
};
type Quote = { id: string; sell: { token: string; amountAtomic: string }; steps: Step[]; warnings: string[] };
type Built = { tx: { to: Hex; data: Hex; value: string; chainId: number; gas: string | null } };
type Tracked = { hash: Hex; status: "pending" | "confirmed" | "failed" | "unknown"; amountOut: string | null };

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

async function call<T>(path: string, body?: unknown): Promise<T> {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(`${BASE}${path}`, {
      method: body === undefined ? "GET" : "POST",
      headers: { "Content-Type": "application/json", ...(API_KEY ? { "X-Api-Key": API_KEY } : {}) },
      body: body === undefined ? undefined : JSON.stringify(body),
    });
    if (res.status === 429) {
      await sleep(Number(res.headers.get("Retry-After") ?? "5") * 1000);
      continue;
    }
    const data = await res.json();
    if (!res.ok) throw new Error(`${res.status} ${data.error.code}: ${data.error.message}`);
    return data as T;
  }
  throw new Error("Still rate limited after 5 attempts.");
}

export async function swap(sell: string, buy: string, amount: string): Promise<Tracked> {
  const ask = { sell, buy, amount, taker: account.address, slippageBps: 50 };
  let quote = await call<Quote>("/quote", ask);

  // 1. ERC-20 approval to Permit2 (once per token).
  const approve = quote.steps.find((s) => s.kind === "approve");
  if (approve) {
    if (getAddress(approve.spender) !== PERMIT2 || getAddress(approve.tx.to) !== getAddress(approve.token)) throw new Error("Unexpected approval");
    const hash = await wallet.sendTransaction({ to: approve.tx.to, data: approve.tx.data });
    const receipt = await client.waitForTransactionReceipt({ hash });
    if (receipt.status !== "success") throw new Error("Approval reverted");
    quote = await call<Quote>("/quote", ask); // steps change once the approval lands
  }

  // 2. Permit2 signature.
  let permitSignature: Hex | undefined;
  const permit = quote.steps.find((s) => s.kind === "permit");
  if (permit) {
    const { domain, types, message } = permit.typedData;
    if (getAddress(message.spender) !== UNIVERSAL_ROUTER || getAddress(domain.verifyingContract) !== PERMIT2 || domain.chainId !== 4663) {
      throw new Error("Unexpected Permit2 request");
    }
    permitSignature = await wallet.signTypedData({
      domain,
      types,
      primaryType: "PermitSingle",
      message: {
        details: { ...message.details, amount: BigInt(message.details.amount) },
        spender: message.spender,
        sigDeadline: BigInt(message.sigDeadline),
      },
    });
  }

  // 3. Build: the API simulates the transaction before returning it.
  const { tx } = await call<Built>("/swap", { quoteId: quote.id, permitSignature });

  // 4. Verify before signing anything.
  const expectedValue = quote.sell.token === NATIVE_ETH ? quote.sell.amountAtomic : "0";
  if (getAddress(tx.to) !== UNIVERSAL_ROUTER) throw new Error("Refusing: tx.to is not the Universal Router");
  if (tx.chainId !== 4663) throw new Error("Refusing: wrong chain");
  if (tx.value !== expectedValue) throw new Error("Refusing: unexpected ETH value");

  // 5. Send.
  const hash = await wallet.sendTransaction({ to: tx.to, data: tx.data, value: BigInt(tx.value), gas: tx.gas ? BigInt(tx.gas) : undefined });

  // 6. Track until settled.
  let status = await call<Tracked>("/swaps/track", { quoteId: quote.id, txHash: hash });
  while (status.status === "pending") {
    await sleep(2_000);
    status = await call<Tracked>(`/swaps/${hash}`);
  }
  return status;
}

AI Agent 的 MCP

归海 runs a remote Model Context Protocol server, so an AI agent can list tokens, quote, build and track swaps, and read Proof of Savings with plain tool calls. It prepares and checks; the wallet that owns the funds still signs and sends every transaction.

Server URL

https://hookline.trade/api/mcp

In production: https://hookline.trade/api/mcp

Transport

ProtocolStreamable HTTP
Stateless JSON responses: every JSON-RPC request is a POST, no session to open (GET and DELETE answer 405). Send Accept: application/json, text/event-stream.
Authnone
Open to everyone. An X-Api-Key header raises the limits exactly like on the HTTP API.
Limitsper IP
300 MCP requests per minute per IP, on top of the limit of each tool (quote and build tools count against the 60 per minute quote limit, the others against the read limit).

Add it to your client

{
  "mcpServers": {
    "hookline": {
      "url": "https://hookline.trade/api/mcp"
    }
  }
}

Cursor and most recent clients take the URL directly. In Claude Desktop you can also add it under Settings, Connectors, as a custom connector with this URL; the config above bridges it through mcp-remote for clients that only start local servers.

Tools

list_tokensno input
Tradable tokens with USD prices and whether Aggregator Hook pools trade them.
get_quotesell, buy, amount, taker?, slippage_bps?, partner_fee_bps?
Best route across every Uniswap and Aggregator Hook pool, the same answer as /api/quote: split, minimum received, savings, every route checked and, with taker, the approval steps.
build_swapquote_id, permit_signature?
The unsigned Universal Router transaction for a quote made with a taker, simulated against the live chain.
track_swapquote_id, tx_hash
Registers the hash your wallet returned and reports pending, confirmed or failed.
swap_statustx_hash
Status of a tracked swap and the atomic amount received once confirmed.
wallet_balancesaddress
A wallet's balance of every listed token, in atomic units.
hook_poolstoken?, routable_only?, limit?
Aggregator Hook pools: venue, tokens, outside liquidity, 24 hour volume, protocol fee and whether they are routed.
hookline_statsno input
The live network numbers, Proof of Savings total included.
proof_of_savingstx_hash
What a confirmed swap received above the best route using Uniswap pools alone, with its route and explorer link.

The agent flow

  1. get_quote with taker set to the user's wallet. Read the price, the savings and the steps.

  2. If steps has an approve, the wallet sends that transaction once; wait for it, then call get_quote again.

  3. If steps has a permit, the wallet signs its typed data as is (eth_signTypedData_v4).

  4. build_swap with the quote id and the permit signature. Check to, chainId and value, then the wallet sends it.

  5. track_swap with the hash the wallet returned, then swap_status until confirmed, and proof_of_savings to show what was saved.

The agent never holds keys

Tools return unsigned transactions and typed data. The user's wallet signs and sends them, after the same checks as any integration: to is the Universal Router, chainId is 4663, value is the ETH sold (else 0), and the permit spender is the router. See 安全.
Raw JSON-RPC call
curl -s -X POST "https://hookline.trade/api/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
200 OK (trimmed)
{
  "result": {
    "tools": [
      {
        "name": "list_tokens",
        "title": "List tradable tokens",
        "inputSchema": {
          "type": "object",
          "properties": {}
        }
      },
      {
        "name": "get_quote",
        "title": "Get a best-price quote",
        "inputSchema": {
          "type": "object",
          "properties": {
            "sell": {
              "type": "string"
            },
            "buy": {
              "type": "string"
            },
            "amount": {
              "type": "string"
            },
            "taker": {
              "type": "string"
            },
            "slippage_bps": {
              "type": "integer"
            }
          },
          "required": [
            "sell",
            "buy",
            "amount"
          ]
        }
      }
    ]
  },
  "jsonrpc": "2.0",
  "id": 1
}

合作伙伴抽成

Route swaps through 归海 and earn on every one of them. With an API key, add partnerFeeBps to a quote and your fee is paid on chain to your payout address, inside the same Universal Router transaction as the swap: no invoices, no claims, nothing to withdraw.

How to earn

API keyX-Api-Key
An API key with a payout address. Create one in a minute on Build on 归海; with a payout address its cap is 50 bps.
partnerFeeBps0 to 50
On /api/quote as a query parameter or in the POST body; partner_fee_bps in the MCP get_quote tool. Up to your key's cap, 50 bps (0.5%) at most. 0 or absent means no partner fee.
fee.partner对象或 null
The quote shows it: bps, amount (human), amountAtomic and recipient (your payout address). buy.amount and minReceived already account for it.
Settlementon chain
The router pays the 归海 fee first, then your fee, both with PAY_PORTION in the bought token, then sends the rest to the user. Your fee is in the transaction the user signs, visible on the explorer.

The math

Both fees are taken in the bought token. The 归海 fee applies to the gross output; your fee applies to what is left after it; the user receives the rest, and the slippage floor is computed on that net amount. Rounding is down at each step, like the router.

Worked example: 1 ETH sold for USDG with a 20 bps partner fee
Gross output of the route2,382.674140 USDG
归海 fee, 15 bps of gross3.574011 USDG
Partner fee, 20 bps of 2,379.1001294.758200 USDG
The user receives2,374.341929 USDG
Minimum received at 0.5% slippage2,362.470219 USDG

Request with your fee

curl -s "https://hookline.trade/api/quote?sell=ETH&buy=USDG&amount=1&taker=0xYourUser&partnerFeeBps=20" \
  -H "X-Api-Key: $HOOKLINE_KEY"

Response (illustrative, from the example above)

200 OK (trimmed)
{
  "id": "q_v2gesngjnovtxmhbm46x7wzq",
  "sell": {
    "token": "0x0000000000000000000000000000000000000000",
    "symbol": "ETH",
    "decimals": 18,
    "amount": "1",
    "amountAtomic": "1000000000000000000",
    "usd": 2382.44
  },
  "buy": {
    "token": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
    "symbol": "USDG",
    "decimals": 6,
    "amount": "2374.341929",
    "amountAtomic": "2374341929",
    "usd": 2374.34
  },
  "minReceived": {
    "amountAtomic": "2362470219",
    "amount": "2362.470219"
  },
  "slippageBps": 50,
  "fee": {
    "bps": 15,
    "amountAtomic": "3574011",
    "amount": "3.574011",
    "holder": null,
    "partner": {
      "bps": 20,
      "amountAtomic": "4758200",
      "amount": "4.7582",
      "recipient": "0x7a1c0d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c"
    }
  },
  "…": "parts, routes, savings, steps as in any quote"
}

When the fee is refused

Without an API key that has a payout address, a partnerFeeBps above 0 answers 403 forbidden. Above your cap it answers 400 partner_fee_too_high; a value that is not a whole number answers 400 invalid_partner_fee. Build and track work exactly as for any quote.

API keys

An API key identifies your app, raises your limits and carries your payout address and partner fee cap. It is free and instant: no application, no review. It is shown once and only its hash is stored, so keep it on your server and never ship it to a browser.

  1. Create it on Build on 归海: a name and, to earn partner fees, a payout address.

  2. Copy it right away: it is shown once. Send it as X-Api-Key on every call and check your usage with /api/builder/me.

  3. Want the showcase? Tell us about your project on the same page: approved projects appear on Built on 归海 and get higher limits.

Limits with an API key

报价与构建300 per minute
The rate of a self-serve key, instead of 60 per IP; 600 for approved showcase projects.
Reads4 × your rate
Tokens, balances, hook pools, stats, tracking, status and /api/builder/me.
Revoked401
A rejected or revoked key answers 401 everywhere; it never falls back to the IP limit.

Your usage

GET/api/builder/me

Your key (prefix, rate, payout address, partner fee cap, request count), your project, and your usage: quotes, swaps, volume and partner fees earned, all time, over the last 30 days and per day. Requires X-Api-Key; 401 without a valid key. Never cached. The dashboard on /build/dashboard reads the same data.

Headers

X-Api-Keyheader, required
Your API key.

请求

curl -s "https://hookline.trade/api/builder/me" \
  -H "X-Api-Key: $HOOKLINE_KEY"

Response (illustrative)

200 OK (days trimmed)
{
  "key": {
    "name": "Builder: Tidebot",
    "prefix": "hkl_3f9a2c",
    "ratePerMin": 600,
    "payoutAddress": "0x7a1c0d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c",
    "partnerMaxBps": 50,
    "createdAt": 1792681673,
    "requests": 18422,
    "lastUsedAt": 1792768073
  },
  "project": {
    "name": "Tidebot",
    "status": "approved",
    "featured": true
  },
  "totals": {
    "quotes": 9120,
    "swaps": 412,
    "volumeUsd": 186340.55,
    "partnerFeesUsd": 372.68
  },
  "last30d": {
    "quotes": 6034,
    "swaps": 288,
    "volumeUsd": 120415.2,
    "partnerFeesUsd": 240.83
  },
  "days": [
    {
      "day": "2026-10-21",
      "quotes": 214,
      "swaps": 9,
      "volumeUsd": 4120.4,
      "partnerFeesUsd": 8.24
    },
    {
      "day": "2026-10-22",
      "quotes": 248,
      "swaps": 12,
      "volumeUsd": 5310.75,
      "partnerFeesUsd": 10.62
    }
  ]
}

Builder toolkit

Everything a person or a coding assistant needs to integrate in an afternoon.

Machine-readable

/openapi.jsonOpenAPI 3.1
Every public endpoint with its schemas: generate a client or import it in your API tool. Open the spec.
/llms.txttext
A compact guide for AI coding assistants: the swap flow, the checks and the links. Point Cursor or Claude at it. Read it.
/api/mcpMCP server
Remote tools for agents, see AI Agent 的 MCP.
/badge.svgSVG
The "Built on 归海" badge for your site or README; ?theme=light for light backgrounds.
Built on 归海 badge, dark
Built on 归海 badge, light
<a href="https://hookline.trade" target="_blank" rel="noopener">
  <img src="https://hookline.trade/badge.svg" alt="基于 归海 构建" height="40">
</a>

<!-- light backgrounds -->
<a href="https://hookline.trade" target="_blank" rel="noopener">
  <img src="https://hookline.trade/badge.svg?theme=light" alt="基于 归海 构建" height="40">
</a>

基于 归海 开发

Get a key, earn up to 0.5% on every swap you route and get featured on the showcase: apply on the Build page, then see who already ships on Built on 归海.

Errors

Every error has a real HTTP status and the same body: a stable code for your program and a message you can show to a person.

422 Unprocessable Content
{
  "error": {
    "code": "no_route",
    "message": "No pool connects USDG and XYZ right now."
  }
}
400invalid_requestMalformed body or query: missing field, wrong type, body not JSON, wrong Content-Type.
400invalid_tokensell or buy is neither a symbol nor a 0x address.
400token_not_listedThe token is not listed, or its symbol is ambiguous (use the address).
400same_tokenBoth sides are the same asset (ETH and WETH count as one).
400invalid_amountamount is not a plain decimal above 0, or has more decimals than the token.
400invalid_slippageslippageBps is not a whole number between 1 and 500.
400invalid_takertaker is not a 0x address, or is the zero address or a precompile.
400invalid_addressaddress (balances) is not a 0x address.
400invalid_hashThe transaction hash is not 0x plus 64 hex characters.
400invalid_quote_idquoteId does not look like a quote id.
400no_takerThe quote was made without taker, so it cannot be built. Quote again with taker.
400permit_requiredThe quote has a permit step and no permitSignature was sent.
400bad_signaturepermitSignature was not signed by the taker over this quote's Permit2 request.
400invalid_partner_feepartnerFeeBps is not a whole number of bps.
400partner_fee_too_highpartnerFeeBps is above your key's partner fee cap.
400invalid_applicationA /build application field is missing or invalid; the message names it (field: reason).
401unauthorizedThe X-Api-Key header holds an invalid or revoked key, or /api/builder/me was called without one.
403forbiddenpartnerFeeBps without an API key that has a payout address.
404not_foundUnknown quote, or a hash that was never tracked.
409not_builtTracking a quote that was never built with /api/swap.
409already_trackedThis hash is already tracked for another quote.
409holder_changedThe quote waived the fee for a GUIHAI holder, but the wallet no longer holds enough. Get a fresh quote.
409name_takenA project with this name already applied to the builder program.
410quote_expiredThe quote is older than its expiresAt. Get a fresh quote.
410permit_expiredThe Permit2 request of the quote has expired. Get a fresh quote.
413payload_too_largeThe body is larger than the endpoint accepts (4 KB quote, 2 KB swap, 1 KB track).
422no_routeNo pool connects the pair, or no liquidity at this size.
422too_smallThe amount is too small to trade after slippage and fee.
422too_largeThe order is above the per-order USD cap.
422impact_too_highThe order would move the price by 20% or more. Trade a smaller amount.
422simulation_failedThe built transaction would revert right now; the message says why (price moved, allowance, balance).
422route_invalidThe stored route can no longer be encoded. Get a fresh quote.
422quote_corruptThe stored quote cannot be read. Get a fresh quote.
429rate_limitedToo many requests in this minute. Wait for the Retry-After header (seconds).
500internal_errorUnexpected failure. Retry in a moment.
503rpc_unavailableThe chain could not be read (allowances). Retry in a few seconds.
503swaps_pausedSwaps are paused while the pinned contracts are re-verified.
503swaps_not_configuredThis deployment cannot settle swaps yet.

安全

归海 never takes custody: there is no deposit, no 归海 contract holding funds, and the API never sees a private key. Still, treat every answer as untrusted input and check it before your wallet signs.

Always verify the destination

The swap transaction must go to the Uniswap Universal Router, nothing else. Refuse to send it if tx.to differs from this address.
0x204faca1764b154221e35c0d20abb3c525710498

Checklist before signing

tx.toswap
Equals the Universal Router above.
tx.chainIdswap
Equals 4663 (Robinhood Chain).
tx.valueswap
Equals the ETH you sell, in wei, when selling ETH; "0" for any other token.
approve steptransaction
Goes to the token you sell and approves Permit2 (0x000000000022d473030f116ddee9f6b43ac78ba3), never another spender.
permit stepsignature
Domain Permit2, chain 4663, verifying contract Permit2; spender is the Universal Router; token and amount match what you sell; expiration and deadline are minutes away, not months.
minReceivedquote
Matches your own floor. The router enforces it on chain: the swap reverts rather than fill below it.