Authentication

Every request to the Bootnode API must be authenticated. This guide covers how to get your API key, use it in requests, handle rate limits, and manage errors.

1

Get Your API Key

Sign in to your Bootnode dashboard at dashboard.bootnode.dev and navigate to Settings → API Keys.

Key types

PrefixEnvironmentUse for
bn_live_ProductionLive mainnet requests
bn_test_TestnetDevelopment and testing (testnets only)

Security

Never expose your API key in client-side code, public repositories, or browser network requests. Always use server-side code or environment variables.

# .env.local (Next.js, Vite, etc.)
BOOTNODE_API_KEY=bn_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

# Never commit this file to version control.
# Add .env.local to your .gitignore.
2

Use the X-API-Key Header

The recommended authentication method. Pass your API key in the X-API-Key header.

# curl
curl https://api.bootnode.dev/v1/gas/ethereum/prices \
  -H "X-API-Key: bn_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"

# TypeScript / fetch
const res = await fetch("https://api.bootnode.dev/v1/gas/ethereum/prices", {
  headers: {
    "X-API-Key": process.env.BOOTNODE_API_KEY!,
  },
});

# Python / requests
import requests
res = requests.get(
    "https://api.bootnode.dev/v1/gas/ethereum/prices",
    headers={"X-API-Key": "bn_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"},
)

# Go / net/http
req, _ := http.NewRequest("GET", "https://api.bootnode.dev/v1/gas/ethereum/prices", nil)
req.Header.Set("X-API-Key", "bn_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6")
3

Use the Authorization Bearer Header

Alternatively, use the standard Bearer token format. Both methods are equivalent.

# curl
curl https://api.bootnode.dev/v1/gas/ethereum/prices \
  -H "Authorization: Bearer bn_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"

# TypeScript / fetch
const res = await fetch("https://api.bootnode.dev/v1/gas/ethereum/prices", {
  headers: {
    Authorization: `Bearer ${process.env.BOOTNODE_API_KEY}`,
  },
});

# Python / requests
import requests
res = requests.get(
    "https://api.bootnode.dev/v1/gas/ethereum/prices",
    headers={"Authorization": "Bearer bn_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"},
)
4

Rate Limiting

All API responses include rate limit headers. When you exceed your limit, the API returns HTTP 429.

PlanRequests / secRequests / dayCompute Units / sec
Free10100,000200
Growth1005,000,0002,000
EnterpriseCustomCustomCustom

Response headers:

X-RateLimit-Limit: 100          # Max requests per second
X-RateLimit-Remaining: 97       # Remaining in current window
X-RateLimit-Reset: 1706400000   # Unix timestamp when window resets
Retry-After: 1                  # Seconds to wait (only on 429)

Different methods consume different compute units (CU). Simple reads like eth_blockNumber cost 1 CU, while archive calls like eth_getStorageAt with a historical block cost 20 CU.

5

Error Handling

Bootnode returns structured error responses. Always check the HTTP status code and handle errors gracefully with retries for transient failures.

// Robust API client with retry logic
async function bootnodeRequest(
  path: string,
  options: RequestInit = {}
): Promise<any> {
  const maxRetries = 3;
  const baseDelay = 1000; // 1 second

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(`https://api.bootnode.dev/v1${path}`, {
      ...options,
      headers: {
        "X-API-Key": process.env.BOOTNODE_API_KEY!,
        "Content-Type": "application/json",
        ...options.headers,
      },
    });

    // Success
    if (res.ok) {
      return res.json();
    }

    // Parse error
    const error = await res.json().catch(() => ({
      error: { code: "unknown", message: res.statusText, status: res.status },
    }));

    // Don't retry client errors (except rate limits)
    if (res.status !== 429 && res.status < 500) {
      throw new Error(
        `Bootnode API error [${error.error.code}]: ${error.error.message}`
      );
    }

    // Retry with exponential backoff
    if (attempt < maxRetries) {
      const retryAfter = res.headers.get("Retry-After");
      const delay = retryAfter
        ? parseInt(retryAfter, 10) * 1000
        : baseDelay * Math.pow(2, attempt);
      console.warn(
        `Bootnode request failed (attempt ${attempt + 1}/${maxRetries + 1}), ` +
        `retrying in ${delay}ms...`
      );
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }

  throw new Error("Bootnode API: max retries exceeded");
}

// Usage:
// const prices = await bootnodeRequest("/gas/ethereum/prices");
// const wallet = await bootnodeRequest("/wallets/create", {
//   method: "POST",
//   body: JSON.stringify({ owner: "0x...", chain: "base" }),
// });

Error Response Format

// 401 Unauthorized
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key. Check your X-API-Key header.",
    "status": 401
  }
}

// 429 Rate Limited
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry after 1 second.",
    "status": 429
  }
}

// 400 Bad Request
{
  "error": {
    "code": "invalid_request",
    "message": "Missing required parameter: chain",
    "status": 400
  }
}

API Key Scopes

When creating an API key, you can restrict it to specific scopes for least-privilege access:

ScopeAccess
rpc:readJSON-RPC read methods (eth_call, eth_getBalance, etc.)
rpc:writeJSON-RPC write methods (eth_sendRawTransaction)
data:readToken, NFT, and transfer APIs
wallets:manageCreate and manage smart wallets
webhooks:manageCreate, update, and delete webhooks
gas:manageGas policies and sponsorship
bundler:sendSubmit UserOperations to the bundler

Next Steps