Integrate API keys using Stacks.js
Configure Stacks.js to use API keys for enhanced rate limits and monitoring
import { createApiKeyMiddleware, createFetchFn } from "@stacks/common";import { StacksMainnet, StacksTestnet } from "@stacks/network";// Create middleware with your API keyconst apiMiddleware = createApiKeyMiddleware({apiKey: process.env.HIRO_API_KEY});// Create custom fetch functionconst customFetch = createFetchFn(apiMiddleware);// Configure network with API keyconst network = new StacksMainnet({fetchFn: customFetch});
Use cases
- Increased API rate limits for production applications
- API usage monitoring and analytics
- Priority access during high traffic periods
- Custom enterprise support features
Key concepts
API key benefits:
- Higher rate limits: 500 requests/minute vs 50 for anonymous
- Usage tracking: Monitor your API consumption
- Priority queue: Better performance during peak times
- Support: Access to dedicated support channels
Complete configuration example
import {makeSTXTokenTransfer,broadcastTransaction,AnchorMode} from "@stacks/transactions";import { StacksMainnet } from "@stacks/network";import { createApiKeyMiddleware, createFetchFn } from "@stacks/common";// Setup API key middlewareconst middleware = createApiKeyMiddleware({apiKey: process.env.HIRO_API_KEY,// Optional: Add custom headersheaders: {"X-Custom-Header": "my-app-v1"}});// Create network with custom fetchconst network = new StacksMainnet({fetchFn: createFetchFn(middleware),// Optional: Use custom API URLcoreApiUrl: "https://api.mainnet.hiro.so"});// Use network for transactionsasync function transferSTX() {const txOptions = {recipient: "SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159",amount: "1000000", // 1 STX in microSTXsenderKey: process.env.SENDER_KEY,network,anchorMode: AnchorMode.Any,};const transaction = await makeSTXTokenTransfer(txOptions);const broadcastResponse = await broadcastTransaction(transaction, network);return broadcastResponse;}
Environment-based configuration
import { StacksNetwork, StacksMainnet, StacksTestnet } from "@stacks/network";function createNetworkWithApiKey(env: "mainnet" | "testnet"): StacksNetwork {const apiKey = process.env[`HIRO_${env.toUpperCase()}_API_KEY`];if (!apiKey) {console.warn(`No API key found for ${env}, using default rate limits`);}const middleware = apiKey? createApiKeyMiddleware({ apiKey }): undefined;const fetchFn = middleware? createFetchFn(middleware): undefined;return env === "mainnet"? new StacksMainnet({ fetchFn }): new StacksTestnet({ fetchFn });}// Usageconst network = createNetworkWithApiKey(process.env.NODE_ENV === "production" ? "mainnet" : "testnet");
Get your API key
Sign up for a free Hiro API key at platform.hiro.so to get higher rate limits and access to additional features.
Multiple API keys for different services
// Different keys for different purposesconst readApiKey = process.env.HIRO_READ_API_KEY;const writeApiKey = process.env.HIRO_WRITE_API_KEY;// Read-only operations (higher rate limit)const readNetwork = new StacksMainnet({fetchFn: createFetchFn(createApiKeyMiddleware({ apiKey: readApiKey }))});// Write operations (transaction broadcasts)const writeNetwork = new StacksMainnet({fetchFn: createFetchFn(createApiKeyMiddleware({ apiKey: writeApiKey }))});// Use appropriate network for each operationasync function getAccountInfo(address: string) {return fetch(`${readNetwork.coreApiUrl}/extended/v1/address/${address}/balances`,{ fetchFn: readNetwork.fetchFn });}async function broadcastTx(signedTx: string) {return broadcastTransaction(signedTx, writeNetwork);}
Error handling and fallbacks
class ResilientStacksClient {private networks: StacksNetwork[];constructor(apiKeys: string[]) {// Create multiple networks with different API keysthis.networks = apiKeys.map(apiKey =>new StacksMainnet({fetchFn: createFetchFn(createApiKeyMiddleware({ apiKey }))}));// Add fallback network without API keythis.networks.push(new StacksMainnet());}async executeWithFallback<T>(operation: (network: StacksNetwork) => Promise<T>): Promise<T> {for (const network of this.networks) {try {return await operation(network);} catch (error: any) {if (error.status === 429) {console.log("Rate limited, trying next network...");continue;}throw error;}}throw new Error("All networks failed");}}// Usageconst client = new ResilientStacksClient([process.env.PRIMARY_API_KEY!,process.env.BACKUP_API_KEY!]);const balance = await client.executeWithFallback(async (network) => {const response = await fetch(`${network.coreApiUrl}/extended/v1/address/${address}/stx`,{ fetchFn: network.fetchFn });return response.json();});
Monitoring API usage
// Track API callslet apiCallCount = 0;let apiCallLog: Array<{ timestamp: Date; endpoint: string }> = [];const monitoringMiddleware = createApiKeyMiddleware({apiKey: process.env.HIRO_API_KEY!,// Intercept requests for monitoringonRequest: (url: string) => {apiCallCount++;apiCallLog.push({timestamp: new Date(),endpoint: new URL(url).pathname});}});// Get usage statsfunction getApiUsageStats() {const now = new Date();const oneMinuteAgo = new Date(now.getTime() - 60 * 1000);const recentCalls = apiCallLog.filter(call => call.timestamp > oneMinuteAgo);return {totalCalls: apiCallCount,callsLastMinute: recentCalls.length,endpointBreakdown: recentCalls.reduce((acc, call) => {acc[call.endpoint] = (acc[call.endpoint] || 0) + 1;return acc;}, {} as Record<string, number>)};}