API Documentation

Programmatic access to bluepages.fyi

Overview

The Bluepages API allows you to query crypto address ↔ Twitter mappings programmatically. All endpoints use the x402 payment protocol for micropayments on Base mainnet.

💡 Payment Protocol: This API uses x402 for automatic USDC payments. You'll need:

Endpoints

GET /check $0.001

Check if an address or Twitter handle exists in the database.

Query params: ?address=0x... or ?twitter=@handle

Response format:

{
  "timestamp": "2025-11-05T12:00:00.000Z",
  "query": {
    "type": "twitter",
    "value": "@vitalikbuterin"
  },
  "exists": true,
  "twitter": true,
  "farcaster": false,
  "message": "✓ Found in database. Use /data endpoint ($0.05) to get full details."
}

Note: exists is true if either twitter OR farcaster is true.

GET /data $0.05

Get full data for an address or Twitter handle.

Query params: ?address=0x... or ?twitter=@handle

Response format (address with both Twitter & Farcaster):

{
  "timestamp": "2025-11-05T12:00:00.000Z",
  "query": {
    "type": "address",
    "value": "0x1db3439a222c519ab44bb1144fc28167b4fa6ee6"
  },
  "found": true,
  "address": "0x1db3439a222c519ab44bb1144fc28167b4fa6ee6",
  "twitter": "@vitalikbuterin",
  "twitterMetadata": {
    "displayName": "vitalik.eth",
    "ens": "vitalik.eth",
    "source": "manual"
  },
  "twitterPriority": 2,
  "farcaster": "@vitalik",
  "farcasterMetadata": {
    "displayName": "Vitalik Buterin",
    "source": "farcaster"
  },
  "farcasterPriority": 1
}

Note: If Twitter or Farcaster is not available, those fields will be omitted.

POST /batch/check $0.04 (bulk)

Check up to 50 addresses/Twitter handles at once (20% discount).

Request body:

{
  "addresses": ["0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6", ...],
  "twitters": ["@vitalikbuterin", "@jessepollak", ...]
}

Response format:

{
  "results": [
    {
      "query": "0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6",
      "exists": true,
      "type": "address"
    },
    {
      "query": "@vitalikbuterin",
      "exists": true,
      "type": "twitter"
    },
    {
      "query": "@nonexistent",
      "exists": false,
      "type": "twitter"
    }
  ]
}
POST /batch/data $2.00 (bulk)

Get full data for up to 50 addresses/Twitter handles at once (20% discount).

Request body:

{
  "addresses": ["0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6", ...],
  "twitters": ["@vitalikbuterin", "@jessepollak", ...]
}

Response format:

{
  "results": [
    {
      "query": "0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6",
      "found": true,
      "type": "address",
      "data": {
        "address": "0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6",
        "twitter": "@vitalikbuterin",
        "ens": "vitalik.eth",
        "source": "manual"
      }
    },
    {
      "query": "@jessepollak",
      "found": true,
      "type": "twitter",
      "data": {
        "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
        "twitter": "@jessepollak",
        "ens": "jessepollak.eth",
        "source": "farcaster"
      }
    },
    {
      "query": "@nonexistent",
      "found": false,
      "type": "twitter",
      "data": null
    }
  ]
}

Note: When found: false, the data field will be null.

POST /my-data FREE

Look up your own data for free by signing a message from your wallet (rate limited to once per 15 minutes).

Request body:

{
  "address": "0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6",
  "signature": "0x..." // EIP-712 signature from the address
}

Response format:

{
  "success": true,
  "address": "0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6",
  "found": true,
  "twitter": "@vitalikbuterin",
  "farcaster": "@vitalik",
  "metadata": {
    "source": "manual",
    "ens": "vitalik.eth"
  }
}

// If not found:
{
  "success": true,
  "found": false,
  "message": "No data found for this address"
}

Note: The signature must be an EIP-712 signature of the message "View my data on bluepages.fyi" from the specified address.

📦 Working Examples: Full, tested code examples are available:

JavaScript Example

Single Query

// Install: npm install ethers x402-fetch
import { Wallet } from 'ethers';
import { fetchWithPayment } from 'x402-fetch';

const PRIVATE_KEY = 'your_private_key_here';
const API_URL = 'https://bluepages.fyi'; // or http://localhost:4021

const wallet = new Wallet(PRIVATE_KEY);

async function checkAddress(address) {
    const url = `${API_URL}/check?address=${address}`;
    
    const response = await fetchWithPayment(url, {
        method: 'GET',
        wallet: wallet,
        network: 'base'
    });
    
    const data = await response.json();
    console.log(data);
    return data;
}

async function getTwitter(address) {
    const url = `${API_URL}/data?address=${address}`;
    
    const response = await fetchWithPayment(url, {
        method: 'GET',
        wallet: wallet,
        network: 'base'
    });
    
    const data = await response.json();
    console.log('Twitter:', data.twitter);
    return data;
}

// Usage
checkAddress('0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6');
getTwitter('0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6');

Batch Query

async function batchCheck(queries) {
    const url = `${API_URL}/batch/check`;
    
    const response = await fetchWithPayment(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ queries }),
        wallet: wallet,
        network: 'base'
    });
    
    const data = await response.json();
    console.log(data);
    return data;
}

// Check up to 50 at once
batchCheck([
    '0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6',
    '@vitalikbuterin',
    '0x...'
]);

Python Example

Single Query

# Install: pip install eth-account requests
import os
import requests
import time
from eth_account import Account
from eth_account.messages import encode_typed_data

PRIVATE_KEY = os.getenv('PRIVATE_KEY')
API_URL = 'https://bluepages.fyi'  # or http://localhost:4021

account = Account.from_key(PRIVATE_KEY)

def create_payment_header(facilitator_data):
    """Create x402 payment authorization header"""
    typed_data = {
        "types": {
            "EIP712Domain": [
                {"name": "name", "type": "string"},
                {"name": "version", "type": "string"},
                {"name": "chainId", "type": "uint256"},
                {"name": "verifyingContract", "type": "address"}
            ],
            "TransferWithAuthorization": [
                {"name": "from", "type": "address"},
                {"name": "to", "type": "address"},
                {"name": "value", "type": "uint256"},
                {"name": "validAfter", "type": "uint256"},
                {"name": "validBefore", "type": "uint256"},
                {"name": "nonce", "type": "bytes32"}
            ]
        },
        "primaryType": "TransferWithAuthorization",
        "domain": facilitator_data["domain"],
        "message": facilitator_data["message"]
    }
    
    encoded_msg = encode_typed_data(full_message=typed_data)
    signed = account.sign_message(encoded_msg)
    
    # Create payment object
    payment = {
        "paymentData": facilitator_data,
        "userAddress": account.address,
        "signature": signed.signature.hex()
    }
    
    import json, base64
    return base64.b64encode(json.dumps(payment).encode()).decode()

def query_api(endpoint, params=None):
    """Query the API with x402 payment handling"""
    url = f"{API_URL}{endpoint}"
    
    # First request - expect 402
    response = requests.get(url, params=params)
    
    if response.status_code == 402:
        # Get payment info from WWW-Authenticate header
        auth_header = response.headers.get('WWW-Authenticate', '')
        
        # Parse facilitator URL and request ID
        import re
        facilitator_match = re.search(r'facilitator="([^"]+)"', auth_header)
        request_id_match = re.search(r'requestId="([^"]+)"', auth_header)
        
        if facilitator_match and request_id_match:
            facilitator_url = facilitator_match.group(1)
            request_id = request_id_match.group(1)
            
            # Get payment data from facilitator
            pay_response = requests.get(
                f"{facilitator_url}/requests/{request_id}/generate-payment-data",
                params={"userAddress": account.address}
            )
            
            if pay_response.status_code == 200:
                payment_data = pay_response.json()
                
                # Create payment header
                payment_header = create_payment_header(payment_data)
                
                # Retry request with payment
                response = requests.get(
                    url,
                    params=params,
                    headers={"X-PAYMENT": payment_header}
                )
    
    return response.json()

# Usage
def check_address(address):
    return query_api('/check', {'address': address})

def get_twitter(address):
    return query_api('/data', {'address': address})

# Example
result = check_address('0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6')
print(result)

data = get_twitter('0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6')
print('Twitter:', data.get('twitter'))

Batch Query (Python)

def batch_check(queries):
    """Check multiple addresses/Twitter handles at once"""
    url = f"{API_URL}/batch/check"
    
    # First request - expect 402
    response = requests.post(
        url,
        json={"queries": queries},
        headers={"Content-Type": "application/json"}
    )
    
    if response.status_code == 402:
        # Same payment flow as above...
        auth_header = response.headers.get('WWW-Authenticate', '')
        # ... (payment handling code) ...
        
        # Retry with payment
        response = requests.post(
            url,
            json={"queries": queries},
            headers={
                "Content-Type": "application/json",
                "X-PAYMENT": payment_header
            }
        )
    
    return response.json()

# Check up to 50 at once
results = batch_check([
    '0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6',
    '@vitalikbuterin',
    '0x...'
])
print(results)
⚠️ Security Warning: Never hardcode private keys in your code. At a minimum, use environment variables. Use something better if significant funds are at stake.
# .env file
PRIVATE_KEY=your_private_key_here

# Load in code
import os
PRIVATE_KEY = os.getenv('PRIVATE_KEY')

Pricing

Endpoint Single Price Batch Price (per item) Savings
/check $0.001 USDC $0.0008 USDC 20%
/data $0.05 USDC $0.04 USDC 20%

Rate Limits

Complete Examples

Full working examples are available:

💡 Tip: For high-volume queries, use the batch endpoints to save 20% on costs and reduce API calls.