The same live data, over REST.
The public API serves exactly what the app renders: every pour, tap, receipt and risk score, straight from the SPOUT ledger and the chain. It is read only, needs no key and returns JSON.
https://usespout.com/api/v1Conventions
Fields ending in _hype are decimal strings in whole HYPE so no precision is lost. Settlement objects carry raw wei strings. Rates are percent. Times are unix seconds of chain time.
List endpoints take limit (1 to 100) and return next_cursor. Pass it back as cursor for the next page; null means you have everything.
120 requests per minute per IP (30 for the risk endpoint). Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset.
Live endpoints are cached for 3 seconds at the edge. Poll no faster than that; the keeper ticks every few seconds anyway.
/poursEvery pour, newest first by default.
| Parameter | In | Description |
|---|---|---|
| status | query | funding, filling, flowing, settling, spring, ended, shutoff, cancelled, or live (the first four) |
| token | query | Token address |
| project | query | Project wallet |
| sort | query | newest, oldest, water_line, flow_rate, ending |
| limit | query | 1 to 100, default 20 |
| cursor | query | next_cursor from the previous page |
curl -s "https://usespout.com/api/v1/pours?status=filling&sort=flow_rate&limit=10"{ data: Pour[]; next_cursor: string | null }/pours/{id}One pour with its latest receipts and tick depth readings.
| Parameter | In | Description |
|---|---|---|
| id | path | Pour id, 10 hex characters |
curl -s "https://usespout.com/api/v1/pours/$POUR_ID"{
data: Pour
events: { at: number; kind: string; message: string; tx_hash: string | null }[] // newest first, up to 50
depth: { at: number; price_hype: number; twap_hype: number; spout_hype: number; organic_hype: number }[] // one per minute, up to 288
}/pours/{id}/tapsEvery tap in one pour, oldest first.
| Parameter | In | Description |
|---|---|---|
| id | path | Pour id |
curl -s "https://usespout.com/api/v1/pours/$POUR_ID/taps"{ data: Tap[] }/taps?owner={address}Every tap a wallet opened, across pours, newest first.
| Parameter | In | Description |
|---|---|---|
| owner | query | Wallet address, required |
| limit | query | 1 to 100, default 50 |
| cursor | query | Pagination cursor |
curl -s "https://usespout.com/api/v1/taps?owner=$WALLET"{ data: (Tap & { pour: { id: string; status: string; token_symbol: string; token: string; ends_at: number | null } })[]; next_cursor: string | null }/statsProtocol totals, the same numbers the board shows.
curl -s "https://usespout.com/api/v1/stats"{ data: {
pours: { live: number; flowing: number; filling: number; springs: number; shutoffs: number; ended: number; total: number }
hype_flowing: string; reserve_tanks_hype: string; paid_to_taps_hype: string
unique_lps: number; rent_per_second_hype: number; at: number
} }/tokens/{address}/riskEligibility and the live risk score for any token. If the token has a pour, its own terms are used. Rate limited to 30 requests a minute because it reads the chain.
| Parameter | In | Description |
|---|---|---|
| address | path | Token address |
| reserve_pct | query | Cover to score against when there is no pour, default 15 |
| water_line | query | HYPE water line to score against, default 500 |
curl -s "https://usespout.com/api/v1/tokens/$TOKEN/risk?reserve_pct=20&water_line=300"{ data: {
token: { address: string; name?: string; symbol?: string; decimals?: number; total_supply?: string }
eligible: boolean
reasons: string[]
launchpad: { name: string; factory: string; creation_tx: string; deployer: string } | null
risk: Risk | null
pour_id: string | null
} }
type Risk = {
score: number // 0 safest, 100 riskiest
grade: "Low" | "Medium" | "High"
factors: { key: "cover" | "holders" | "launchpad" | "dev" | "history"; label: string; value: string; points: number; note: string }[]
computedAt: number
}Types
type Pour = {
id: string // 10 hex characters
status: "funding" | "filling" | "flowing" | "settling" | "spring" | "ended" | "shutoff" | "cancelled"
status_label: string
end_reason: string | null // spring, mains, term, floor, fault_dump, fault_supply, fault_token_pulled, cancelled
token: { address: string; symbol: string; name: string; decimals: number; launchpad: string }
project: string // project wallet
venue: "uniswap-v3" | "hybra-cl"
pool: string
pool_key: number // fee tier or tick spacing
vault: string // the pour's vault wallet
water_line_hype: string // decimal string, 18 decimals of precision
filled_hype: string
fill_ratio: number // filled / water line
taps: number
band_pct: number
term_days: number
spring_target_hype: string
flow_rate_apr: number // percent, current (rising while filling, locked after)
start_apr: number
max_apr: number
clearing_apr: number | null
meter: { hype_per_second: number; rent_streamed_hype: string; rent_streamed_net_hype: string }
rent_prepaid_hype: string
reserve_tank_hype: string
cover_ratio: number
token_side: string
listing_fee_hype: string
gas_deposit_hype: string
protocol_rent_cut: number
floor_pct: number
open_price_hype: number | null // HYPE per token when flow started
floor_price_hype: number | null
spring_since: number | null // unix seconds organic depth first held the target
spring_hold_secs: number
risk: Risk | null
times: { created_at: number; funded_at: number | null; fill_ends_at: number | null;
flow_started_at: number | null; ends_at: number | null; ended_at: number | null }
settlement: Settlement | null
keeper: { last_tick_at: number | null; last_error: string | null }
}type Tap = {
id: string
pour_id: string
owner: string
status: "queued" | "flowing" | "closed" | "refunded"
principal_hype: string
deposit_tx: string
position: { token_id: string; tick_lower: number; tick_upper: number; liquidity: string } | null
rent_earned_hype: string // net of the protocol share
rent_paid_hype: string
rent_per_second_hype: number
flow_rate_apr: number | null
settlement: { // wei strings, set once the pour closes
principal: string; hypeBack: string; tokenBack: string; feesHype: string; feesToken: string
valueHype: string; loss: string; rentNet: string; bonus: string; cover: string
payHype: string; payToken: string
} | null
created_at: number
}Errors
Every error has the same envelope.
{ "error": { "code": "not_found", "message": "No pour with id 0a1b2c3d4e" } }| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_parameter | A query or path value is missing or malformed |
| 401 | unauthorized | The request needs a signed in wallet |
| 404 | not_found | No pour, tap or token with that id |
| 422 | deposit_rejected, not_eligible | The chain says no: wrong recipient, reverted, or the token fails eligibility |
| 429 | rate_limited | Too many requests; wait for X-RateLimit-Reset |
| 503 | chain_unavailable | The deployment cannot reach Elysium right now |