base-gas-x402

Gas data for agents on Base. Five endpoints, no API key, paid per call in USDC.

Including the one answer nobody else gives you: sometimes there is nothing to optimise.

● Sampling Base gas every 5 minutes, 168 hours of retention. Check /health

What's new

  • Costs in USD. /gas and /gas/compare now return usd next to gwei and ETH, priced from the Chainlink ETH/USD feed on Base.
  • Hours to avoid. /gas/cheapest-window now names avoidHoursUtc, plus the honest everyday saving versus the average hour.

What this is

A pay-per-call gas oracle for Base mainnet that AI agents can use without an API key, a signup, or a subscription. Payment settles in USDC on Base (eip155:8453) through the Coinbase CDP production facilitator.

Who it is for

Developers building AI agents that transact on Base. And, secondarily, developers evaluating x402 as a way to sell their own APIs: all of it is open source and readable as a reference implementation.

Why not just call an RPC?

An RPC tells you the current gas price but not whether it is high or low, because it has no memory. This service samples Base every five minutes, so it can answer:

  • Is gas cheap, normal, expensive, or flat right now?
  • How does Base compare to OP Mainnet, Arbitrum One, and Ethereum?
  • Would waiting for a cheaper hour actually help?

The 402 part

HTTP 402 Payment Required has been reserved and unused since 1997. x402 finally uses it. Your agent requests data, the server answers 402 with a price, the agent pays in USDC, and the data comes back. One round trip, no account.

See it for yourself. This costs nothing, you never pay the challenge itself:

the 402 challenge
curl -i https://base-gas-x402-production.up.railway.app/gas

Setup

Three ways in. Pick one.

1. Agent skill (recommended)

install
npx skills add memosr/base-gas-skill --all --yes

Then ask the agent: What is gas on Base right now?

The paid routes need a funded AgentCash wallet:

wallet
npx agentcash@latest onboard
npx agentcash@latest balance

2. MCP server

install
claude mcp add base-gas \
  --env BUYER_PRIVATE_KEY=0xYOUR_FUNDED_BASE_KEY \
  -- npx -y base-gas-mcp

Five tools, one of them free. The wallet needs USDC on Base, no ETH. Payments above $0.05 per call are refused unless you raise MAX_USD_PER_CALL. Claude Desktop and other clients: see base-gas-mcp on npm.

3. Direct HTTP

free, no wallet needed
curl -s https://base-gas-x402-production.up.railway.app/health
paid
npx agentcash@latest fetch "https://base-gas-x402-production.up.railway.app/gas?gasLimit=180000"

Full docs live with the code: API repo, skill repo, OpenAPI.

Five routes

Prices are transcribed from /openapi.json, which is the source of truth.
RoutePriceReturns
GET /gas$0.002Base fee, priority tiers, cost for any gasLimit, ready-to-sign maxFeePerGas
GET /gas/compare$0.01Base, OP Mainnet, Arbitrum One, Ethereum, cheapest first
GET /gas/history$0.01Time series, min/max/avg/median, verdict, spreadPercent
GET /gas/cheapest-window$0.02hasDailyCycle, recommendation, hourly averages
GET /healthfreeSamples, hours covered, interval, retention

/gas and /gas/compare take gasLimit (21000 to 30000000). /gas/history and /gas/cheapest-window take hours (1 to 168).

Pass the gas limit of the operation and get its actual cost.
OperationgasLimit
ETH transfer21000
ERC-20 transfer65000
NFT mint85000
Uniswap swap180000
Contract deploy1500000

Real response: /gas

GET /gas?gasLimit=21000
{
  "baseFeePerGas": "0.005",
  "priorityFeePerGas": { "low": "0.000580202", "medium": "0.001019902", "high": "0.011204355" },
  "gasPrice": "0.006",
  "estimatedTransferCost": {
    "gasLimit": 21000,
    "basis": "baseFee + medium priority fee",
    "gwei": "126.417942",
    "eth": "0.000000126417942",
    "usd": "0.00034"
  },
  "suggestedFees": {
    "unit": "wei",
    "low":    { "maxFeePerGas": "10580202", "maxPriorityFeePerGas": "580202" },
    "medium": { "maxFeePerGas": "11019902", "maxPriorityFeePerGas": "1019902" },
    "high":   { "maxFeePerGas": "21204355", "maxPriorityFeePerGas": "11204355" }
  },
  "ethUsd": { "price": "2689.87", "source": "chainlink-eth-usd-base", "updatedAt": "2026-09-30T16:44:29.000Z" }
}

A live call, default gasLimit (a plain ETH transfer). estimatedTransferCost.usd comes from the Chainlink ETH/USD feed on Base (ethUsd). If the feed is unreadable, both fields are null and the rest of the response is unchanged. suggestedFees goes straight into a wallet or viem sendTransaction: maxFeePerGas is 2 × base fee + priority, a ceiling, and the unused part is refunded.

Real response: /gas/compare

Base comes second here, not first. That is left in on purpose: anyone can call the endpoint and check.

GET /gas/compare
{
  "gasLimit": 21000,
  "chains": [
    { "chain": "optimism", "label": "OP Mainnet", "gasPrice": "0.001000371",
      "estimatedCost": { "eth": "0.000000021007791" } },
    { "chain": "base", "label": "Base", "gasPrice": "0.006",
      "estimatedCost": { "eth": "0.000000126" } },
    { "chain": "arbitrum", "label": "Arbitrum One", "gasPrice": "0.02",
      "estimatedCost": { "eth": "0.00000042" } },
    { "chain": "ethereum", "label": "Ethereum", "gasPrice": "0.046909277",
      "estimatedCost": { "eth": "0.000000985094817" } }
  ],
  "cheapest": "optimism",
  "baseRank": 2,
  "baseVsEthereum": "Base is 7.8x cheaper than Ethereum",
  "unavailable": []
}

Real response: /gas/cheapest-window

GET /gas/cheapest-window
{
  "hasDailyCycle": true,
  "recommendation": "Avoid 08:00 and 14:00 UTC. The cheapest hour is 22:00 UTC: about 1.1% cheaper than the average hour and 15.3% cheaper than the priciest.",
  "savingsPercent": 15.3,
  "savingsVsAveragePercent": 1.1,
  "avoidHoursUtc": [8, 14],
  "cheapestHourUtc": 22
}

Seven days of observation. avoidHoursUtc names the hours running hot, worst first; 22:00 UTC is the cheapest hour. Read the two percentages separately: savingsPercent (15.3%) compares the cheapest hour against the single priciest one and can be dominated by one spike, while savingsVsAveragePercent (1.1%) compares it against the average hour, which is the honest everyday saving.

Over shorter windows that miss a spike, this endpoint returns hasDailyCycle: false and says plainly that there is nothing to time, rather than naming a cheapest hour the data does not support.

Advantages and differences

Several Base gas endpoints exist, including multi-chain toolkits covering seven chains to this one's one. Here is where this one differs.

Focused, not a dumping ground
Five routes, one question each. Competitors ship 67, 307, and 353 endpoints; the AgentCash validator warns above 40, because a large surface eats an agent's token budget.
Honest about its own data
/health is free and reports how much history exists, so you can decide whether a paid history call is worth it. Every paid response carries a coverage object. I have not found another Base gas endpoint that does this.
It tells you when there is nothing to do
Base spends most hours pinned to its fee floor. When the window you asked about shows no real variation, /gas/history returns flat and /gas/cheapest-window returns hasDailyCycle: false instead of inventing a cheapest hour. An endpoint that always names one is selling a decision that cannot be made.
Priced per operation
Pass gasLimit, get the cost of the thing you are about to do, not a gwei figure to multiply yourself.
Fails partially, not totally
/gas/compare reads four chains independently. One dead RPC lands in unavailable instead of killing the response you paid for.
Open source, end to end
API, MCP server, and skill are public and MIT licensed. The skill ships rules that steer agents away from overspending.
Low-cost current gas, priced in dollars
/gas at $0.002, below most Base gas endpoints ($0.005 to $0.10), and it returns the cost in USD. It is not the cheapest: at least one competitor sells plain gas data for $0.001.

What it is not: not the only or first Base gas API, and not the cheapest overall. It will not save you money by timing transactions on Base, because there is nothing there to time. Single instance on Railway, no uptime guarantee and no SLA. No usage numbers to quote, it is new.

Findings

Two x402 failure modes found while building this. Both are reproducible and both cost hours. If you run an x402 origin, they may save you an afternoon.

1. A missing trust proxy silently blocks discovery

Railway and most PaaS edges terminate TLS and forward plain HTTP. Express reports req.protocol as http, x402 advertises an http:// resource, and Bazaar registration fails hourly with:

hourly, invisibly
resource must start with 'https://' when protocol type is http

Nothing surfaces it: the discovery CLI reports zero warnings and the endpoints stay live and payable, while the index never updates. One line fixes it:

the fix
app.set("trust proxy", true);

2. The CDP facilitator size-limits the payment payload

The bazaar metadata from the 402 challenge is echoed back inside the payment payload. Past a threshold the facilitator reports it as a schema error:

not actually a schema problem
'paymentPayload' is invalid: must match one of [x402V2Pay...

Measured on Base, same wallet and client: 4188 bytes accepted, 4260 bytes rejected. One endpoint stopped accepting payment purely because its description was longer than the others. Writing better documentation made it unpayable.