Campground search and live site-level availability from Recreation.gov. Which individual sites are free, on which nights — one paid HTTP call, answer in the response body. Pay in USDC on Base or Solana — your client picks the rail.
git clone https://github.com/nirholas/x402-campsites && cd x402-campsites && npm install && npm run dev| Route | Price | You get back |
|---|---|---|
GET /search | $0.002 | Campgrounds by text, state, or coordinates — location, site count, amenities, booking link |
GET /availability/:campgroundId | $0.003 | Site-level availability over a date window, plus a summary of what is bookable |
GET /health | free | Both data sources and their live status |
GET /catalog | free | Real recreation.gov campground ids — skip the paid search when you already know the site |
GET /.well-known/x402 | free | Machine-readable price sheet, both rails |
Search then check one campground is $0.005. If you already have the campground id from the free catalog, it is just the $0.003 availability lookup.
Every 402 lists both networks. A caller holding USDC on Base pays on Base; a caller holding USDC on Solana pays on Solana. Same route, same price, same artifact.
base-sepolia by default, base for mainnet. EIP-3009 transfer authorization.
0x40252CFDF8B20Ed757D61ff157719F33Ec332402
solana mainnet by default, solana-devnet for testing. SPL transfer.
WwwuGbqHrwF5RG89KhUbmRWEvjnRH9k5kVM5p7T3WwW
X-PAYMENT# 1. Run the service (works immediately — availability needs no key)
cp .env.example .env
npm run dev # http://localhost:4025
# 2. Free: real campground ids
curl -s http://localhost:4025/catalog | jq '.campgrounds[0]'
# 3. See the dual-rail 402 quote (no wallet needed)
curl -s "http://localhost:4025/search?state=CA" \
| jq '.accepts[] | {network, payTo, maxAmountRequired}'
# 4. Pay for real on Base Sepolia (free test USDC: faucet.circle.com)
export PRIVATE_KEY=0x…
npm run client # searches, then checks live site availability
| Route | Upstream | Key | Fallback |
|---|---|---|---|
/search | RIDB — the official Recreation Information Database | Free RIDB_API_KEY | Fixture catalog, labelled source: "fixture" |
/availability/:id | recreation.gov availability API | None — keyless | Only if that endpoint is unreachable, and availabilitySource says so |
Availability — the part that actually matters — is live out of the box, because the endpoint behind recreation.gov's own booking calendar is public. And the fixture catalog carries real recreation.gov facility ids, so an availability lookup on a fixture search result still returns genuine site-level data.
Read availabilitySource, not source.
"recreation.gov" is real; "fixture" means the live endpoint was
unreachable and the numbers are a deterministic simulation that must never be presented
as bookable. The two fields are independent — a response can correctly read
"source": "fixture" with "availabilitySource": "recreation.gov".
This service reports availability; it does not reserve anything. Every response
carries a bookingUrl for a human to finish on recreation.gov.