API v2.4 ← Back to Faucet
REST API v2.4

Ethora Faucet
API Docs

Programmatically request BEP-20 tokens, query wallet cooldowns, monitor faucet reserves, and receive real-time transaction events via webhooks. All responses are JSON.

Base URL: api.ethora.io/v1
BNB Smart Chain · Chain ID 97
JSON · REST

Authentication

All API requests must include your API key in the request header. Keys are scoped to a specific environment (only in v1) and can be generated from your developer dashboard.

Keep your API key secret

Never expose your API key in client-side JavaScript or public repositories. Use environment variables and server-side requests only.

API Key Header

HTTP
X-Faucet-API-Key: ef_live_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Example Authenticated Request

cURL
curl https://api.ethora.io/v1/faucet/status \
  -H "X-Faucet-API-Key: ef_live_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json"

API keys have the following prefix conventions: ef_live_sk_ for live keys and ef_test_sk_ for sandbox testing keys. Both operate against the BNB Smart Chain in v1.

Base URL & Versioning

All endpoints are versioned. The current stable version is v1. Breaking changes will be released under a new version prefix and the previous version will receive a 12-month deprecation window.

Base URL
https://api.ethora.io/v1
EnvironmentBase URLChain
testnethttps://api.ethora.io/v1BNB (97)
sandboxhttps://sandbox.api.ethora.io/v1Mocked responses
Sandbox environment

The sandbox mirrors the production API 1:1 but returns mocked blockchain responses without broadcasting real transactions. Ideal for integration testing and CI pipelines.

Rate Limits

Rate limits are applied per API key. All responses include X-RateLimit-* headers so you can track your current usage. Exceeding the limit returns a 429 Too Many Requests response with a Retry-After header.

TierRequests / minDrips / dayWebhook Events
Free3010
Basic1201003 endpoints
Pro600UnlimitedUnlimited

Rate Limit Headers

Response Headers
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1716912060
Retry-After: 34  # only present on 429 responses

POST /faucet/request

Dispatch a token drip to a specified BNB Smart Chain wallet address. This is the core endpoint — it validates the address, checks the cooldown window, verifies the Turnstile captcha token, and broadcasts a BEP-20 transfer transaction on Chain ID 97.

POST /faucet/request

Request Body

FieldTypeRequiredDescription
wallet_address string Required EVM-compatible wallet address starting with 0x, exactly 42 characters. Must be a valid checksum or lowercase hex address.
tokens string[] Required Array of BEP-20 token symbols to send. Maximum 5 per request. Example: ["USDT", "LINK", "CAKE"]. Use GET /tokens for the full list.
captcha_token string Required Cloudflare Turnstile token obtained client-side. Must be a valid, unexpired token issued for your registered domain. Omit only in sandbox mode.
note string Optional Arbitrary string up to 120 characters. Stored alongside the request record, returned in GET /wallet/history. Useful for tagging automated CI runs.
Request · JSON
{
  "wallet_address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
  "tokens": ["USDT", "LINK", "CAKE"],
  "captcha_token": "0.xxxxxxxxxxxxxxxxxxxxxxxx",
  "note": "ci-run-#482 DEX integration test"
}
200 Success
400 Bad Request
429 Cooldown
{
  "success": true,
  "request_id": "req_01HX9KZB4P6WQNJ3T2VQKF8MR",
  "tx_hash": "0x3f9d2a1c4b0e7f8a5c6d9e2b1f3a4c7e0b9d2f5a8c1e4b7d0f3a6c9e2b5f8a",
  "wallet_address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
  "tokens_sent": [
    { "symbol": "USDT", "amount": "10000", "decimals": 18 },
    { "symbol": "LINK", "amount": "500", "decimals": 18 },
    { "symbol": "CAKE", "amount": "300", "decimals": 18 }
  ],
  "fee_bnb": "0.03",
  "block_number": 39874521,
  "created_at": "2025-05-20T14:32:11.042Z",
  "next_request_at": "2025-05-21T14:32:11.042Z"
}
{
  "success": false,
  "error": {
    "code": "INVALID_ADDRESS",
    "message": "wallet_address must be a 42-character hex string starting with 0x",
    "field": "wallet_address"
  }
}
{
  "success": false,
  "error": {
    "code": "COOLDOWN_ACTIVE",
    "message": "This wallet was recently funded. Please wait before requesting again.",
    "next_request_at": "2025-05-21T14:32:11.042Z",
    "seconds_remaining": 72941
  }
}

GET /faucet/status

Returns real-time operational status of the faucet service, including current gas price on the testnet, number of pending transactions in the queue, and estimated delivery time for new requests.

GET /faucet/status

No request body. No required parameters. Authentication is optional — unauthenticated calls are allowed but are rate-limited to 10 req/min.

Response · 200
{
  "status": "operational",   // "operational" | "degraded" | "down"
  "queue_depth": 4,
  "gas_price_gwei": "3.1",
  "estimated_delivery_sec": 8,
  "last_block": 39874529,
  "requests_today": 47238,
  "uptime_30d_pct": "99.8",
  "chain_id": 97
}

GET /faucet/balance

Returns the current token reserves held by the faucet hot wallet. Useful for monitoring scripts to alert when a token is running low and needs topping up.

GET /faucet/balance
Response · 200
{
  "bnb_reserve": "18.432",
  "tokens": [
    { "symbol": "USDT", "balance": "42000000", "low_threshold": "500000", "is_low": false },
    { "symbol": "LINK", "balance": "180000",  "low_threshold": "50000",  "is_low": false },
    { "symbol": "MKR",  "balance": "820",      "low_threshold": "200",    "is_low": false }
  ],
  "updated_at": "2025-05-20T14:31:58.000Z"
}

GET /tokens

Returns the full list of available BEP-20 tokens including their mainnet contract addresses, drip amounts, category type, and current availability status. Use this to build dynamic token pickers in your own interfaces.

GET /tokens

Query Parameters

ParamTypeRequiredDescription
type string Optional Filter by token type. One of Stablecoin, DeFi, L1 Peg, Wrapped, Oracle, Utility.
available_only boolean Optional If true, excludes tokens marked wip: true. Defaults to false.
Response · 200
{
  "count": 18,
  "tokens": [
    {
      "symbol": "USDT",
      "name": "Tether USD",
      "contract_address": "0x55d398326f99059fF775485246999027B3197955",
      "decimals": 18,
      "drip_amount": "10000",
      "type": "Stablecoin",
      "available": true,
      "icon_url": "https://assets.coingecko.com/coins/images/325/small/Tether.png"
    }
    // ... more tokens
  ]
}

GET /tokens/:symbol

Returns detailed metadata for a single BEP-20 token by its ticker symbol. Symbol matching is case-insensitive.

GET /tokens/:symbol
cURL
curl https://api.ethora.io/v1/tokens/CAKE \
  -H "X-Faucet-API-Key: ef_live_sk_xxx"
Response · 200
{
  "symbol": "CAKE",
  "name": "PancakeSwap Token",
  "contract_address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82",
  "decimals": 18,
  "drip_amount": "300",
  "type": "DeFi",
  "available": true,
  "bscscan_url": "https://bscscan.com/token/0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82",
  "faucet_reserve": "2840000"
}

GET /wallet/cooldown

Check whether a wallet address is currently in its 24-hour cooldown period and how long until the next request is allowed. Does not require authentication.

GET /wallet/cooldown?address=0x...
ParamTypeRequiredDescription
address string Required The EVM wallet address to check. Must be a valid 42-character hex string.
Response · 200
{
  "address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
  "on_cooldown": true,
  "last_request_at": "2025-05-20T14:32:11.042Z",
  "next_request_at": "2025-05-21T14:32:11.042Z",
  "seconds_remaining": 72941
}

GET /wallet/history

Returns the paginated drip history for a specific wallet address — all past token requests with their transaction hashes, timestamps, and statuses. Useful for audit trails and dashboard UIs.

GET /wallet/history?address=0x...
ParamTypeRequiredDescription
addressstringRequiredThe wallet address to query history for.
limitintegerOptionalNumber of records per page. Default 20, max 100.
beforestringOptionalCursor for pagination. Pass the cursor from the previous response to get the next page.
Response · 200
{
  "address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
  "total": 14,
  "cursor": "req_01HX2KZB1P2WQNJ3T1VQKF6MR",
  "has_more": true,
  "requests": [
    {
      "request_id": "req_01HX9KZB4P6WQNJ3T2VQKF8MR",
      "tx_hash": "0x3f9d2a1c...f8a",
      "tokens": ["USDT", "LINK", "CAKE"],
      "status": "confirmed",  // "pending" | "confirmed" | "failed"
      "block_number": 39874521,
      "note": "ci-run-#482 DEX integration test",
      "created_at": "2025-05-20T14:32:11.042Z"
    }
  ]
}

POST /webhooks

Register a webhook endpoint to receive real-time event notifications. When a drip transaction confirms, fails, or the faucet reserve falls below a threshold, Ethora will send a signed HTTP POST to your URL within seconds.

POST /webhooks
FieldTypeRequiredDescription
urlstringRequiredHTTPS endpoint to receive events. Must respond with 2xx within 10 seconds.
eventsstring[]RequiredArray of event types to subscribe to. See Webhook Events.
secretstringOptionalShared secret used to compute HMAC-SHA256 signatures. If omitted, one is generated for you and returned in the response.
filter_walletstringOptionalIf provided, only events for this specific wallet address will be sent to this webhook.
Request · JSON
{
  "url": "https://yourapp.io/webhooks/faucet",
  "events": ["drip.confirmed", "drip.failed", "reserve.low"],
  "filter_wallet": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
}

Webhook Events

Each event is delivered as a JSON payload with a standard envelope plus event-specific data. The full list of subscribable events:

drip.requestedFires immediately when a new drip request is received and queued, before the transaction is broadcast.
drip.pendingTransaction has been broadcast to the BSC mainnet and is awaiting block confirmation.
drip.confirmedTransaction has at least 1 confirmation on Chain ID 97. Includes block number and gas used.
drip.failedTransaction reverted or dropped from the mempool. Includes a failure reason string.
reserve.lowA token reserve has fallen below its configured threshold. Useful for automated top-up scripts.
cooldown.expiredA wallet's 24-hour cooldown window has expired and it may request tokens again.

Payload Envelope

Webhook Payload
{
  "id": "evt_01HXB2MFQKR7WY9XNPJA3T8VLZ",
  "type": "drip.confirmed",
  "api_version": "v1",
  "created_at": "2025-05-20T14:32:19.000Z",
  "data": {
    "request_id": "req_01HX9KZB4P6WQNJ3T2VQKF8MR",
    "wallet_address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
    "tx_hash": "0x3f9d2a1c4b0e7f8a5c6d9e2b1f3a4c7e0b9d2f5a8c1e4b7d0f3a6c9e2b5f8a",
    "tokens": ["USDT", "LINK", "CAKE"],
    "block_number": 39874521,
    "gas_used": 184320
  }
}

Webhook Signature Verification

Every webhook delivery includes an X-Ethora-Signature header. This is an HMAC-SHA256 signature of the raw request body using your webhook secret. Always verify this signature before processing the payload.

Node.js
const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  const sig = signature.replace('sha256=', '');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(sig)
  );
}
Always use timingSafeEqual

Never compare signatures with === — this is vulnerable to timing attacks. Use crypto.timingSafeEqual (Node.js) or an equivalent constant-time comparison in your language.

Error Codes

All error responses follow a consistent structure. The error.code field contains a machine-readable string; error.message is human-readable.

INVALID_ADDRESS
Invalid wallet address
Address is not a valid 42-char EVM hex string.
COOLDOWN_ACTIVE
Wallet on cooldown
This wallet made a request within the last 24 hours.
INVALID_CAPTCHA
Captcha failed
Turnstile token is invalid or expired.
TOKEN_UNAVAILABLE
Token not available
One or more requested tokens are marked WIP or paused.
TOO_MANY_TOKENS
Token limit exceeded
Requested more than 5 tokens in a single call.
RESERVE_EMPTY
Faucet reserve empty
Token reserve is depleted; try again later.
UNAUTHORIZED
Missing API key
API key header is absent or revoked.
RATE_LIMITED
Rate limit exceeded
Too many requests. See Retry-After header.

SDKs & Libraries

Official SDK packages are available for the most common environments. All SDKs wrap the REST API and provide typed request/response models.

npm
npm install @ethora/faucet-sdk
JavaScript / TypeScript
import { EthoraFaucet } from '@ethora/faucet-sdk';

const faucet = new EthoraFaucet({ apiKey: process.env.ETHORA_API_KEY });

// Request tokens
const result = await faucet.request({
  walletAddress: '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B',
  tokens: ['USDT', 'LINK'],
  captchaToken: turnstileToken
});

console.log(result.txHash); // 0x3f9d2a...

// Check cooldown
const cd = await faucet.cooldown('0xAb5801...');
if (!cd.onCooldown) {
  console.log('Ready to request!');
}

Available Packages

LanguagePackageStatus
JavaScript / TypeScript@ethora/faucet-sdkStable
Pythonethora-faucetStable
Gogithub.com/ethora/faucet-goBeta
Rustethora-faucet (crates.io)Beta

Changelog

v1.2.0 — 2025-05-15

  • Added GET /wallet/history endpoint with cursor-based pagination.
  • Webhook events now include gas_used field on drip.confirmed.
  • Added filter_wallet parameter to webhook registration.
  • TWT, SXP, ALPACA tokens added to the available list.

v1.1.0 — 2025-04-01

  • Introduced webhook system with HMAC-SHA256 signature verification.
  • Added sandbox environment at sandbox.api.ethora.io.
  • GET /faucet/balance now returns per-token low_threshold and is_low flags.

v1.0.0 — 2025-02-14

  • Initial public API release.
  • Core endpoints: /faucet/request, /faucet/status, /tokens, /wallet/cooldown.
  • Cloudflare Turnstile captcha integration.