Isocast API
Isocast watches the observed daily-high temperature for 37 cities. The instant a city crosses into a new Polymarket temperature bucket, Isocast emits an immutable, sequentially numbered signal and pushes it to entitled subscribers. Buy signals in prepaid bundles over x402; pull the API to backfill.
Free endpoints
No auth, no payment. Discovery, pricing, and a sample payload are always free. Base URL https://api.isocast.dev. Every JSON response carries a legal disclaimer field.
| Endpoint | Returns |
|---|---|
| GET /health | Service health + configured payment mode |
| GET /v1/cities | All 37 tracked cities + per-city latestSeq (count) |
| GET /v1/cities/:slug | One city + today’s target date + Polymarket market URL |
| GET /v1/sample?city=SLUG | A real seq-1 signal payload (shape demo, never the latest) |
| GET /v1/signals/meta?city=SLUG | Bundle pricing, min spend, network, payTo, latestSeq |
The paid flow — HTTP 402
Live signal delivery is paid. Your agent requests a bundle, receives a 402 Payment Required with terms, signs a USDC-on-Base payment, and retries with the signature — the round-trip x402 uses everywhere.
# 1. Request a bundle. With no payment you get a 402 with terms.
$ curl -i "https://api.isocast.dev/v1/signals?city=nyc&count=100"
HTTP/1.1 402 Payment Required
PAYMENT-REQUIRED: { "x402Version": 1, "accepts": [ {
"scheme": "exact",
"network": "eip155:8453",
"asset": "USDC",
"maxAmountRequired": "0.450",
"payTo": "<ISOCAST_X402_WALLET_ADDRESS>",
"extra": { "binding": "keccak256(payer, citySlug, count, salt)" }
} ] }
# 2. Your wallet signs the payment, binding (payer, city, count),
# and you retry with the signature in the X-PAYMENT header.
$ curl "https://api.isocast.dev/v1/signals?city=nyc&count=100" \
-H "X-PAYMENT: <x402 signature>"
HTTP/1.1 200 OK
{ "city": "nyc", "count": 100, "paidThroughSeq": 141,
"delivery": "telegram", "disclaimer": "Informational data only; …" }Payment binding (anti-replay)
A bare EIP-3009 transfer signs only from, to, value, …, nonce — not the app parameters. So Isocast binds (payer, citySlug, count) into the payment signature and recomputes it server-side, rejecting any mismatch. On USDC / EIP-3009 the nonce is:
nonce = keccak256(payer, citySlug, count, salt)On Permit2 the same triple is carried in the witness. This makes a settled payment impossible to replay against a different city or count.
Bundle pricing
One settlement buys a bundle and credits paidThroughSeq += count; each pushed signal draws it down. Minimum spend 0.01 USDC. Discounts are per-settlement. Unused signals never expire.
| Tier | Signals | Price / signal | Total (USDC) |
|---|---|---|---|
| Min | 2 | 0.005 | 0.010 |
| Starter | 20 | 0.005 | 0.100 |
| Standard | 100 | 0.0045 | 0.450 |
| Pro | 500 | 0.0040 | 2.000 |
| Whale | 2000 | 0.0035 | 7.000 |
Telegram setup
DELIVERY ENDPOINT SHIPPING NOW- 01Start a chat with the Isocast bot (or add it as admin to a private channel your agent reads) and note the
chat_id. - 02Register the target with a wallet-signed EIP-712 intent. Entitlement is keyed to your wallet, never the
chat_id— so changing the target is a pure config write, with no re-payment. - 03Isocast sends a test message; the target is persisted only on success. Then every crossing for a city you're entitled to is pushed as machine-readable JSON in the message body.
// EIP-712 typed intent — sets the delivery target for a wallet.
// Entitlement is keyed to the wallet, NOT the chat_id, so changing
// the target is a pure config write — no re-payment.
{
"types": {
"IsocastDeliveryIntent": [
{ "name": "wallet", "type": "address" },
{ "name": "chatId", "type": "string" },
{ "name": "nonce", "type": "uint256" }
]
},
"primaryType": "IsocastDeliveryIntent",
"message": { "wallet": "0x…", "chatId": "123456789", "nonce": 1 }
}Delivery is per-user chat_idfan-out — one message per entitled subscriber, not a shared channel. At the entitlement boundary Isocast sends a “top up to continue” message instead of the signal.
Backfill & polling
Signals are immutable and sequentially numbered, so a crashed consumer can always replay. Pull GET /v1/signals?city=X&since=<seq> for entitled signals; on exhaustion the endpoint returns a 402 to buy the next bundle. Poll GET /v1/cities to watch each city's latestSeq advance.