Public API
Every endpoint below is the same JSON API this site's own pages call — nothing held back for a private tier. All are read-only GET requests, unauthenticated, no API key. There's no SLA: this is a community-run site, not Concrete's own infrastructure, so treat it as best-effort and cache aggressively on your end.
Base URL
https://concrete-guide-six.vercel.appAuth
None. Don't send wallet private keys or signatures — every endpoint here only ever reads public on-chain / public API data.
Rate limits
Unposted routes (market/gas/vaults/etc.) have no explicit limit but are cached at the edge. Write-adjacent read routes (reviews, quiz leaderboard) are limited per-IP — expect a 429 with a short cooldown if you poll too fast.
Errors
Non-2xx responses return { status: "error", message } or, for a missing/invalid query param, { error: "..." }. Some endpoints return { status: "not_configured" } (200) when the underlying store — e.g. reviews, quiz leaderboard — isn't set up on this deployment, rather than a 4xx/5xx.
/api/vaultA wallet's live position (shares held, underlying value, share price) in one or more vaults on one chain.
| Param | Required | Description |
|---|---|---|
| wallet | yes | A checksummable 0x address. |
| chain | no | ethereum (default), arbitrum, or base. |
| vaults | yes | Comma-separated vault (ERC-4626) addresses. |
Example
GET https://concrete-guide-six.vercel.app/api/vault?wallet=0xabc...&chain=ethereum&vaults=0x0e60...,0xacce...Response shape
{ vaults: [{ vaultAddress, name, symbol, underlyingSymbol, sharesHeldFormatted, underlyingValueFormatted, totalAssetsFormatted, sharePrice, blockNumber, error? }], warnings? }Freshness: no-store — always reads the current block.
/api/vaultsEvery vault in this app's known-vault list, read live on-chain — no wallet needed. Powers the home vault dashboard.
Example
GET https://concrete-guide-six.vercel.app/api/vaultsResponse shape
{ fetchedAt, chains: [{ chain, blockNumber }], vaults: LiveVaultResult[] }Freshness: no-store.
/api/pointsA wallet's Fuul points total, rank, and (when Fuul's response includes it) a per-campaign breakdown.
| Param | Required | Description |
|---|---|---|
| wallet | yes | A checksummable 0x address. |
Example
GET https://concrete-guide-six.vercel.app/api/points?wallet=0xabc...Response shape
{ wallet, status: "ok" | "not_configured" | "error", totals }Freshness: no-store.
/api/leaderboardThe Fuul points leaderboard — top wallets by points, plus total participant count.
| Param | Required | Description |
|---|---|---|
| limit | no | 1–1000, default 1000. |
Example
GET https://concrete-guide-six.vercel.app/api/leaderboard?limit=100Response shape
{ status: "ok", rows: [{ rank, wallet, points }], totalUsers }/api/marketThe Vault Terminal's market snapshot for all 4 modeled vaults: APY, TVL, daily yield, and ETH/BTC prices, with source labelled (live / partial / rpc / simulated).
Example
GET https://concrete-guide-six.vercel.app/api/marketResponse shape
{ vaults: Record<VaultId, {apy, tvl, dailyYield}>, prices: {WETH, WBTC, USDC, FRAX, USD1, WSTETH}, source, ts }Freshness: CDN-cached 60s; source chain is app.concrete.xyz → DefiLlama → ETH RPC → simulated fallback.
/api/gasCurrent Ethereum mainnet gas price (eth_gasPrice), server-proxied so the browser never hits a public RPC directly.
Example
GET https://concrete-guide-six.vercel.app/api/gasResponse shape
{ gwei, source: "live" | "simulated", ts }Freshness: 30s.
/api/ensReverse-resolves an address to its ENS name, if any.
| Param | Required | Description |
|---|---|---|
| address | yes | A checksummable 0x address. |
Example
GET https://concrete-guide-six.vercel.app/api/ens?address=0xabc...Response shape
{ address, name: string | null }Freshness: 5 min in-memory per serverless instance.
/api/ct-price$CT token price from CoinGecko by contract address. Returns { trading: false } honestly until $CT actually lists — not an error state.
Example
GET https://concrete-guide-six.vercel.app/api/ct-priceResponse shape
{ trading: false } | { trading: true, usd, usdMarketCap, ts }Freshness: 60s.
/api/vault-reviewsCommunity upvote/downvote + short-comment reviews for one of the Vault Terminal's 4 modeled vaults.
| Param | Required | Description |
|---|---|---|
| vaultId | yes | weeth, ctwbtc, ctusd, frxusd, frontier, rwausd1, srroyusdc, or royeth. |
| limit | no | 1–100, default 30. |
| clientId | no | Your own anonymous client id, to get yourReview back. |
Example
GET https://concrete-guide-six.vercel.app/api/vault-reviews?vaultId=ctusd&limit=30Response shape
{ status: "ok" | "not_configured" | "error", upvotes, downvotes, reviews: [{name, vote, comment, updatedAt}], yourReview }Freshness: 15s.
/api/quiz-leaderboardThe Concrete-knowledge quiz's leaderboard.
| Param | Required | Description |
|---|---|---|
| limit | no | 1–200, default 50. |
Example
GET https://concrete-guide-six.vercel.app/api/quiz-leaderboard?limit=50Response shape
{ status: "ok", rows: [{ rank, name, weightedScore, totalCorrect, tierKey, streak }] }/api/alert-checkStateless: reads a vault's live share price and, if a threshold is currently crossed, POSTs a payload to your webhook URL. Add &format=discord to send a ready-to-post Discord message body instead of raw JSON.
| Param | Required | Description |
|---|---|---|
| vault | yes | Vault (ERC-4626) address. |
| chain | no | ethereum (default), arbitrum, or base. |
| direction | no | above (default) or below. |
| threshold | yes | Numeric share-price threshold. |
| webhook | no | URL to POST to when crossed (Slack/Discord/Zapier/your own). |
| format | no | raw (default) or discord. |
Example
GET https://concrete-guide-six.vercel.app/api/alert-check?vault=0x0e60...&threshold=1.05&webhook=https://hooks.slack.com/...Response shape
{ vault, chain, sharePrice, threshold, direction, crossed, webhook, checkedAt }Freshness: Always live. Re-fires every check while the condition holds — point a cron at it, and have your receiver de-duplicate, or use the Telegram bot below for built-in dedup.
Telegram alerts bot
A stateful alternative to /api/alert-check above: message a Telegram bot directly and it remembers your watches, checking on a cron and pinging you only when a threshold is newly crossed (not on every tick).
Commands (DM the bot)
/watchprice <chain> <vault_address> <above|below> <price>/watchapy <weeth|ctwbtc|ctusd|frxusd|frontier|rwausd1|srroyusdc|royeth> <above|below> <apy>/watchct <above|below> <usd_price>/myalerts·/unwatch <id>·/stop·/help
Running your own instance
- Create a bot with @BotFather and set
TELEGRAM_BOT_TOKENin your deployment's environment. - Set
KV_REST_API_URL/KV_REST_API_TOKEN(Vercel KV) orUPSTASH_REDIS_REST_URL/UPSTASH_REDIS_REST_TOKEN— subscriptions are stored there. Free tier is plenty. - Register the webhook once, from anywhere, by visiting:
https://api.telegram.org/bot<TOKEN>/setWebhook?url=https://concrete-guide-six.vercel.app/api/telegram-webhook - Optionally set
TELEGRAM_CRON_SECRETto any string, then point a free external cron (cron-job.org, EasyCron, a GitHub Actions schedule) at:https://concrete-guide-six.vercel.app/api/telegram-check?secret=<TELEGRAM_CRON_SECRET>every 2–5 minutes. Without the secret set, the endpoint works unauthenticated — fine for low-traffic personal use.
Without a Telegram bot, use /api/alert-check's webhook param with any Slack/Discord/Zapier URL instead — no bot token needed.
Nothing here is versioned yet — paths and response shapes can change as the app evolves. If you're building something that depends on one of these, open an issue (or a PR) on GitHub so a breaking change doesn't surprise you.
Looking for Concrete's own protocol data (not this guide's cached copies)? See the SDK guide and subgraph & events doc pages.