Skip to content

Pay with USDC

Status: built, behind a switch, and off. Until the operator switches it on, a request with no key is a 401, exactly as before, and everything below is what will happen then. The day it is switched on will be written here. This page is the short guide; Paying per call (x402) has the details of every answer and what is kept.

  1. You ask for something without a key. The answer is 402 Payment Required with what to pay.
  2. You sign a USDC transfer on Base for exactly one of the amounts offered (any x402 client library does this; the remote MCP server at mcp.debyko.com takes an API key or OAuth, not payments).
  3. You send the same request again with the payment in PAYMENT-SIGNATURE (the base64 of the x402 version 2 payment).
  4. You get the answer; the payment settles after it.

A key beats a payment: if both are sent, the key is used and the payment is not read.

Terminal window
curl -si -X POST https://api.debyko.com/v2/snapshots -H "Content-Type: application/json" -d '{"where":"base in (BTC)","layers":["mark"]}'
{
"x402Version": 2,
"error": "PAYMENT-SIGNATURE header is required",
"resource": { "url": "https://api.debyko.com/v2/snapshots", "description": "DEBYKO /v2/snapshots (price class snapshots)", "mimeType": "application/json" },
"accepts": [
{ "scheme": "exact", "network": "eip155:8453", "amount": "5000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x…", "maxTimeoutSeconds": 60, "extra": { "name": "USD Coin", "version": "2" } },
{ "scheme": "exact", "network": "eip155:8453", "amount": "9000000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x…", "maxTimeoutSeconds": 60, "extra": { "name": "USD Coin", "version": "2" } },
{ "scheme": "exact", "network": "eip155:8453", "amount": "29000000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x…", "maxTimeoutSeconds": 60, "extra": { "name": "USD Coin", "version": "2" } },
{ "scheme": "exact", "network": "eip155:8453", "amount": "99000000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x…", "maxTimeoutSeconds": 60, "extra": { "name": "USD Coin", "version": "2" } },
{ "scheme": "exact", "network": "eip155:8453", "amount": "249000000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x…", "maxTimeoutSeconds": 60, "extra": { "name": "USD Coin", "version": "2" } }
],
"errors": [ { "code": "PAYMENT_REQUIRED", "message": "PAYMENT-SIGNATURE header is required", "hint": "or send an API key instead: …" } ],
"plans": [
{ "code": "mini", "name": "Mini", "price_usd": "9", "amount": "9000000", "days": 30 },
{ "code": "standard", "name": "Standard", "price_usd": "29", "amount": "29000000", "days": 30 },
{ "code": "pro", "name": "Pro", "price_usd": "99", "amount": "99000000", "days": 30 },
{ "code": "pro-plus", "name": "Pro Plus", "price_usd": "249", "amount": "249000000", "days": 30 }
],
"plans_note": "To buy a plan instead of this one request, pay the amount of that plan's entry in accepts (above): …"
}

accepts is a list of what you may pay: the first entry is this one request, the others are the plans. An amount is in atomic units (USDC has six decimals: 5000 is 0.005 USDC, 9000000 is 9). A requirement carries only an amount, so plans says which amount is which plan. The same accepts is in the PAYMENT-REQUIRED header (base64), as the protocol has it.

Pay the first amount. You are answered as Pro answers, for that one request. The price is by the kind of call (0.005 for snapshots and candles, 0.01 for a screen, 0.02 for history); the table is on Paying per call. A call that fails (a 400, a 5xx) is not settled and the same signed payment may be sent again.

Pay the amount of a plan instead — Mini $9, Standard $29, Pro $99, Pro Plus $249, the same prices as at checkout — on any payable path. The answer is not data: the payment is verified and settled first, and then you get a key of that plan:

{
"plan": "mini", "plan_name": "Mini",
"api_key": "dbk_…",
"issued_at": "2026-10-02T09:15:00Z", "expires_at": "2026-11-01T09:15:00Z", "valid_days": 30,
"payment": { "transaction": "0x…", "network": "eip155:8453", "payer": "0x…", "amount": "9000000", "price_usd": "9" },
"notes": [ "This is the only time the key is shown. …", "Valid 30 days from the payment, not renewed: …", "…" ]
}
  • The key is shown once. DEBYKO keeps its first twelve characters and a hash of the rest, so it cannot show it again. Keep it. The settlement is also in the PAYMENT-RESPONSE header.
  • It is a normal key of that plan (Authorization: Bearer dbk_…), with that plan’s limits, valid 30 days from the payment. There is no renewal: when it expires nothing is charged and nothing continues. A new payment buys a new key. From three days before it expires every response carries X-Debyko-Key-Expires (the instant it stops working).
  • No account. The key has none. Rules (Agent) live in a free account with an email: open one at studio.debyko.com/account and link the key there (“A key bought with USDC”, under the keys); from then on the account’s rules are read under it. A linked key keeps the plan it was bought as until it expires, and does not count as the account’s own key. Push (a Runner) needs a plan on the account, not a key.
  • Nothing else is sold this way. No packs of requests, no units, no balance.
  • If your payment settles and you are told the key could not be stored, do not pay again: write to [email protected] with the transaction hash and the key is issued by hand.

The tick-level history of the core and top-100 listings, one Parquet file per listing, layer and UTC day, is payable per file without a key: GET /v2/archive/{listing}/{layer}/{day} answers 402 with the price (0.10 USDC for a core listing, 0.05 USDC for a top-100 one), and the same request with the payment answers 302 to a link that reads that one file for fifteen minutes: one payment, one file. The list of files (GET /v2/archive) is not payable: it is free with any key.

GET /v2/agent/state (a key, or a payment without one) returns the state of the account’s Agent rules:

{ "evaluated_at": "2026-10-02T09:20:01.317Z", "served_at": "…", "receipt_id": "…",
"evaluations": { "charged": 0, "used_today": 812, "per_day": 15000 },
"items": [
{ "rule_id": "dfc3d6d2-acf1-41e3-b9f1-9ca0c6c6eeeb", "state": "true", "previous_state": "false",
"evaluated_at": "2026-10-02T09:20:00.002Z", "effective_interval": "1m" } ] }

state is true, false or unknown (with a reason). previous_state is what this key was told the last time it asked — one value per key and rule, so your poll sees what changed since your own previous poll, whatever other keys asked. evaluated_at is when the state was established. effective_interval is how often the rule is really checked: the largest of your plan’s interval, the rule’s own cadence and the cadence of its instruments — a rule is never checked faster than its instruments are collected.

  • On a plan with push you get the rule’s last scheduled evaluation, and that is free. ?fresh=true evaluates it now and counts against your plan’s evaluations a day.
  • On a plan without push, or past your evaluations a day, the rule is evaluated when you ask, through the same engine as /v2/screen; past the budget that costs a DQL screening call of your key.
  • Without a key (x402 on): name the rules, ?rule_id=<uuid>&rule_id=<uuid> (1 to 100), and pay 0.01 USDC per rule named; they are evaluated when you ask. previous_state is then kept per payer address.

An honest note on polling. Polling cannot be faster than the thing it polls. A rule whose instruments arrive every minute can change at most once a minute, so asking every second returns the same stored answer 59 times — free on a plan with push, but pointless — and a fresh=true poll every second pays for 59 evaluations that say nothing new. Poll at effective_interval or slower. A poll also sees the state, not every change: if a rule goes true and false again between two polls, you saw neither. If you need the moment it changes, that is what push is for (a Runner webhook fires on a change of state, once), and polling does not replace it. And per call, keyless polling is the dear way: 10 rules polled every 10 seconds is 0.10 USDC × 360 = 36 USDC an hour, where a Mini plan for 30 days is 9.

X402__Enabled=true with X402__PayTo, a CDP API key, and X402__InternalPayers for the owner’s own wallet (so its smoke-test payment is in no revenue figure). The ledger and its daily export are in Admin (/api/x402/revenue); the accounting policy is in the repository (docs/engineering/accounting/x402-revenue.md).