Headless, step by step
Sign up free with a wallet. Get an API key free. A small free allowance to try it. Top up in USDC or USDT over the API to raise your limits and pay as you go. No human required.
Sign up free with a wallet. Get an API key free. A small free allowance to try it. Top up in USDC or USDT over the API to raise your limits and pay as you go. No human required.
Each request below, with its answer. The same flow as one file: headless.mts.
Ask for a challenge
curl 'https://hanzo.id/v1/iam/web3/nonce?chain=evm&address=0xYOUR_ADDRESS'{"status":"ok","msg":"","data":{"domain":"hanzo.id","uri":"https://hanzo.id/login","statement":"Sign in to Hanzo. This request will not trigger a blockchain transaction or cost any gas.","nonce":"f2e7f942d43d6a73662cbf57b28a1b65","issuedAt":"2026-10-26T22:48:28Z","expirationTime":"2026-10-26T22:58:28Z","version":"1"}}The challenge lives 10 minutes.
Sign it
Build this CAIP-122 message from data and sign it with EIP-191
personal_sign:
hanzo.id wants you to sign in with your Ethereum account:
0xYOUR_ADDRESS
Sign in to Hanzo. This request will not trigger a blockchain transaction or cost any gas.
URI: https://hanzo.id/login
Version: 1
Nonce: f2e7f942d43d6a73662cbf57b28a1b65
Issued At: 2026-10-26T22:48:28Z
Expiration Time: 2026-10-26T22:58:28ZVerify
Make a PKCE pair first:
verifier=$(openssl rand -hex 32)
challenge=$(printf %s "$verifier" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')curl -X POST https://hanzo.id/v1/iam/web3/verify \
-H 'Content-Type: application/json' \
-d '{"clientId":"hanzo-ai","chain":"evm","scheme":"secp256k1-eip191","address":"0xYOUR_ADDRESS","message":"<the message, newlines as \n>","signature":"0x<signature>","type":"code","redirectUri":"http://localhost:3000/callback","scope":"openid profile email","codeChallenge":"<challenge>","codeChallengeMethod":"S256"}'{"status":"ok","data":"<authorization code>"}A first-seen wallet gets an account, wallet_evm_<16 hex>, and its own
organization, wallet-evm-<16 hex>, which it administers.
Trade the code for a token
curl -X POST https://hanzo.id/v1/iam/oauth/token \
-d grant_type=authorization_code -d client_id=hanzo-ai \
-d code=<authorization code> -d code_verifier=$verifier \
-d redirect_uri=http://localhost:3000/callback{"access_token":"eyJ…","id_token":"eyJ…","refresh_token":"eyJ…","token_type":"Bearer","expires_in":604800,"scope":"openid profile email"}The token lasts seven days.
Get an API key, free
curl -X POST https://api.hanzo.ai/v1/account/keys \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"agent","type":"secret"}'{"id":"…","name":"agent","type":"secret","key":"sk-live-…","status":"active","created":"…"}key is shown once. Keep it as HANZO_API_KEY in your secret store. It
outlives the token.
Read your limits
Reading spends nothing.
curl https://api.hanzo.ai/v1/billing/plans
curl https://api.hanzo.ai/v1/billing/tier -H "Authorization: Bearer $HANZO_API_KEY"
curl https://api.hanzo.ai/v1/allowance -H "Authorization: Bearer $HANZO_API_KEY"-
GET /v1/billing/plansneeds no key: every plan withslug,name,price(cents),featuresandlimits. -
GET /v1/billing/tier: your plan and what is left to spend.{"user": "…", "plan": "…", "tier": {"name", "displayName", "dailyCreditsCents", "allowedModels", "maxAgents"}, "balance": {"prepaidAvailable", "creditsRemaining", "dailyRemaining", "effectiveAvailable", "currency"}}Compare only
effectiveAvailableto zero; the others are its parts. -
GET /v1/allowance: your free calls this period.{"plan", "limit", "used", "resets", "window", "pooled", "pool": {"state", "resets"}, "spent"}limitminususedis what is left.resetsis unix seconds.spent: truemeans the next free call is refused.
Every API answer also carries X-RateLimit-Limit, X-RateLimit-Remaining and
X-RateLimit-Reset.
A 429 means the free allowance is used
{"error":{"message":"…","type":"rate_limit_error","code":"free_plan_cap","resets_at":"…","topup_url":"https://…","actions":[{"kind":"wait","label":"Wait until …"},{"kind":"topup","label":"Buy usage credits","url":"https://…"}]}}It comes with Retry-After, X-Hanzo-Usage-Reason: free_plan_cap and
X-Hanzo-Topup-Url. Two ways on: wait for resets_at, or top up (step 8).
With a balance, calls past the allowance draw on it, per token.
- 429
usage_cap_exceeded: a spent request window. Same two ways on. - 402
paid_plan_required: nothing pays for that model yet. Top up. - 402 or 429 from a key's own
budgetorrate: the owner set it. See Bound every key.
Top up in USDC or USDT
curl https://api.hanzo.ai/v1/billing/crypto/options -H "Authorization: Bearer $HANZO_API_KEY"{"chains":["base","ethereum","solana"],"tokens":["usdc","usdt"]}USDC on Base, Ethereum and Solana. USDT on Ethereum.
curl -X POST https://api.hanzo.ai/v1/billing/crypto/deposit \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"chain":"base","token":"usdc","amountCents":2000}'{"id":"GRncZpMYqBuV","status":"pending","chain":"base","token":"usdc","depositAddress":"0x…","expiresAt":"2026-10-27T20:33:54Z"}- Send exactly to
depositAddress, withaddressTagwhen one is set. Never to an address from a page. - The address is yours, open 24 hours, and reused while open.
amountCentsis for the record. You are credited what arrives.- 400: that chain or token is not offered. 503: that rail is shut, so send nothing.
Watch it confirm:
curl https://api.hanzo.ai/v1/billing/crypto/deposit/GRncZpMYqBuV -H "Authorization: Bearer $HANZO_API_KEY"Same shape. status moves pending, confirming, succeeded. Another
payer's id answers 404.
Read the balance
curl https://api.hanzo.ai/v1/billing/balance -H "Authorization: Bearer $HANZO_API_KEY"{"balance": <cents>, "holds": <cents>, "available": <cents>, "account": "<payer>", "cash": <cents>, "credit": <cents>}Whole USD cents. available is what you can spend: cash you paid, credit
Hanzo granted. account is the wallet a top-up credits. Then pay as you go:
each call past the allowance draws on available.
Other ways to pay
- Card, Apple Pay or Google Pay at hanzo.ai/pay. Needs a browser.
- Bank wire:
GET /v1/billing/wirereturns the instructions to a signed-in caller. Staff credit it once the bank confirms, so it is not instant.
Prices per model: Pricing.