# CastDex Voting API Documentation

Welcome to the CastDex Voting API! This API provides read-only access to voting data from the CastDex platform, allowing you to retrieve token statistics, trending information, and voting analytics.

## Quick Start

### Base URL

```
https://castdex.vercel.app/api/external/votes
```

### Authentication

All API requests require authentication using an API key in the request header:

```http
X-API-Key: your_64_character_api_key_here
```

### Example Request

```bash
curl -H "X-API-Key: your_64_character_api_key_here" \
  "https://castdex.vercel.app/api/external/votes/stats?token_id=solana_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
```

***

## Authentication

The CastDex Voting API uses API key authentication. Your API key is a unique 64-character alphanumeric string that identifies your application and tracks usage.

### Header Formats

The API accepts API keys in multiple header formats:

| Header          | Format       | Example                                 |
| --------------- | ------------ | --------------------------------------- |
| `X-API-Key`     | Direct key   | `X-API-Key: abc123...xyz789`            |
| `Authorization` | Bearer token | `Authorization: Bearer abc123...xyz789` |
| `API-Key`       | Direct key   | `API-Key: abc123...xyz789`              |

### API Key Requirements

* **Length**: Exactly 64 characters
* **Format**: Alphanumeric only (a-z, A-Z, 0-9)
* **Example**: `abc123def456ghi789jkl012mno345pqr678stu901vwx234yzab567cde890fgh`

### Getting an API Key

Contact the CastDex team to request an API key for your application. Please provide:

* Application name and description
* Expected usage volume
* Integration timeline
* Technical contact information

***

## Rate Limits

To ensure fair usage and system stability, the API implements rate limiting per API key:

| Window     | Default Limit    | Header                  |
| ---------- | ---------------- | ----------------------- |
| Per Minute | 1,000 requests   | `X-RateLimit-Remaining` |
| Per Hour   | 10,000 requests  | `X-RateLimit-Reset`     |
| Per Day    | 100,000 requests | `X-RateLimit-Window`    |

### Rate Limit Headers

Every API response includes rate limit information:

```http
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 2025-07-17T23:00:00.000Z
X-RateLimit-Window: minute
```

### Handling Rate Limits

When you exceed the rate limit, you'll receive a `429 Too Many Requests` response:

```json
{
  "success": false,
  "error": "Rate limit exceeded for minute. Try again after 2025-07-17T23:00:00.000Z"
}
```

**Best Practices:**

* Monitor the `X-RateLimit-Remaining` header
* Implement exponential backoff for retries
* Cache responses when possible
* Contact support if you need higher limits

***

## API Endpoints

### 1. Token Statistics

Get detailed voting statistics for one or more tokens.

**Endpoint:** `GET /stats`

#### Parameters

| Parameter         | Type    | Required | Description                                                   |
| ----------------- | ------- | -------- | ------------------------------------------------------------- |
| `token_id`        | string  | Yes\*    | Single token identifier                                       |
| `token_ids`       | string  | Yes\*    | Comma-separated list of token IDs (max 100)                   |
| `period`          | string  | No       | Time period: `1h`, `24h`, `7d`, `30d`, `all` (default: `24h`) |
| `group_by`        | string  | No       | Group results: `hour`, `day`, `user`                          |
| `include_details` | boolean | No       | Include detailed vote information (default: `false`)          |

\*Either `token_id` or `token_ids` is required

#### Examples

**Single Token:**

```bash
curl -H "X-API-Key: your_api_key" \
  "https://castdex.vercel.app/api/external/votes/stats?token_id=solana_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&period=24h"
```

**Multiple Tokens:**

```bash
curl -H "X-API-Key: your_api_key" \
  "https://castdex.vercel.app/api/external/votes/stats?token_ids=token1,token2,token3&period=7d"
```

**Grouped by Hour:**

```bash
curl -H "X-API-Key: your_api_key" \
  "https://castdex.vercel.app/api/external/votes/stats?token_id=example_token&group_by=hour&period=24h"
```

#### Response Format

**Basic Response:**

```json
{
  "success": true,
  "data": {
    "solana_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": {
      "token_id": "solana_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "likes": 45,
      "rockets": 23,
      "total": 68,
      "unique_voters": 32,
      "unique_wallets": 30
    }
  },
  "meta": {
    "period": "24h",
    "tokens_requested": 1,
    "total_votes": 68,
    "query_time": "2025-07-17T22:15:00.000Z"
  }
}
```

**Grouped by Hour Response:**

```json
{
  "success": true,
  "data": [
    {
      "timestamp": "2025-07-17T22:00:00Z",
      "total_votes": 15,
      "likes": 8,
      "rockets": 7,
      "tokens": {
        "token_id_1": {"likes": 5, "rockets": 3, "total": 8},
        "token_id_2": {"likes": 3, "rockets": 4, "total": 7}
      }
    }
  ],
  "meta": {
    "period": "24h",
    "group_by": "hour",
    "query_time": "2025-07-17T22:15:00.000Z"
  }
}
```

### 2. Trending Tokens

Get currently trending tokens based on voting activity.

**Endpoint:** `GET /trending`

#### Parameters

| Parameter | Type    | Required | Description                                             |
| --------- | ------- | -------- | ------------------------------------------------------- |
| `period`  | string  | No       | Time period: `1h`, `24h`, `7d` (default: `24h`)         |
| `type`    | string  | No       | Sort by: `total`, `likes`, `rockets` (default: `total`) |
| `limit`   | integer | No       | Number of tokens to return (max 100, default: 10)       |

#### Examples

**Default Trending:**

```bash
curl -H "X-API-Key: your_api_key" \
  "https://castdex.vercel.app/api/external/votes/trending"
```

**Trending by Rockets (7-day):**

```bash
curl -H "X-API-Key: your_api_key" \
  "https://castdex.vercel.app/api/external/votes/trending?period=7d&type=rockets&limit=20"
```

#### Response Format

```json
{
  "success": true,
  "data": [
    {
      "token_id": "solana_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "likes": 120,
      "rockets": 45,
      "total": 165,
      "lastVote": "2025-07-17T22:15:00.000Z"
    },
    {
      "token_id": "base_0x1234567890abcdef1234567890abcdef12345678",
      "likes": 89,
      "rockets": 67,
      "total": 156,
      "lastVote": "2025-07-17T22:10:00.000Z"
    }
  ],
  "meta": {
    "period": "24h",
    "type": "total",
    "limit": 10,
    "total_tokens": 43,
    "query_time": "2025-07-17T22:15:00.000Z"
  }
}
```

***

## Response Format

All API responses follow a consistent JSON structure:

### Success Response

```json
{
  "success": true,
  "data": { /* endpoint-specific data */ },
  "meta": {
    "query_time": "2025-07-17T22:15:00.000Z",
    /* additional metadata */
  }
}
```

### Error Response

```json
{
  "success": false,
  "error": "Error description"
}
```

### Common HTTP Status Codes

| Code  | Meaning               | Description                                   |
| ----- | --------------------- | --------------------------------------------- |
| `200` | OK                    | Request successful                            |
| `400` | Bad Request           | Invalid parameters or missing required fields |
| `401` | Unauthorized          | Invalid or missing API key                    |
| `429` | Too Many Requests     | Rate limit exceeded                           |
| `500` | Internal Server Error | Server error (contact support)                |

***

## Code Examples

### JavaScript/Node.js

```javascript
const API_KEY = 'your_64_character_api_key_here';
const BASE_URL = 'https://castdex.vercel.app/api/external/votes';

async function getTokenStats(tokenId, period = '24h') {
  try {
    const response = await fetch(
      `${BASE_URL}/stats?token_id=${tokenId}&period=${period}`,
      {
        headers: {
          'X-API-Key': API_KEY
        }
      }
    );
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    const data = await response.json();
    
    if (!data.success) {
      throw new Error(data.error);
    }
    
    return data.data;
  } catch (error) {
    console.error('Error fetching token stats:', error);
    throw error;
  }
}

async function getTrendingTokens(limit = 10) {
  try {
    const response = await fetch(
      `${BASE_URL}/trending?limit=${limit}`,
      {
        headers: {
          'X-API-Key': API_KEY
        }
      }
    );
    
    const data = await response.json();
    return data.success ? data.data : null;
  } catch (error) {
    console.error('Error fetching trending tokens:', error);
    return null;
  }
}

// Usage examples
getTokenStats('solana_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')
  .then(stats => console.log('Token stats:', stats))
  .catch(error => console.error(error));

getTrendingTokens(5)
  .then(trending => console.log('Top 5 trending:', trending))
  .catch(error => console.error(error));
```

### Python

```python
import requests
import json

API_KEY = 'your_64_character_api_key_here'
BASE_URL = 'https://castdex.vercel.app/api/external/votes'

class CastDexAPI:
    def __init__(self, api_key):
        self.api_key = api_key
        self.headers = {'X-API-Key': api_key}
    
    def get_token_stats(self, token_id=None, token_ids=None, period='24h', group_by=None):
        """Get token statistics"""
        url = f'{BASE_URL}/stats'
        params = {'period': period}
        
        if token_id:
            params['token_id'] = token_id
        elif token_ids:
            params['token_ids'] = ','.join(token_ids)
        else:
            raise ValueError("Either token_id or token_ids must be provided")
        
        if group_by:
            params['group_by'] = group_by
        
        response = requests.get(url, headers=self.headers, params=params)
        response.raise_for_status()
        
        data = response.json()
        if not data['success']:
            raise Exception(data['error'])
        
        return data['data']
    
    def get_trending_tokens(self, period='24h', vote_type='total', limit=10):
        """Get trending tokens"""
        url = f'{BASE_URL}/trending'
        params = {
            'period': period,
            'type': vote_type,
            'limit': limit
        }
        
        response = requests.get(url, headers=self.headers, params=params)
        response.raise_for_status()
        
        data = response.json()
        if not data['success']:
            raise Exception(data['error'])
        
        return data['data']

# Usage examples
api = CastDexAPI(API_KEY)

try:
    # Get stats for a single token
    stats = api.get_token_stats(
        token_id='solana_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
        period='7d'
    )
    print(f"Token stats: {json.dumps(stats, indent=2)}")
    
    # Get trending tokens
    trending = api.get_trending_tokens(period='24h', limit=5)
    print(f"Top 5 trending: {json.dumps(trending, indent=2)}")
    
    # Get multiple token stats
    multi_stats = api.get_token_stats(
        token_ids=['token1', 'token2', 'token3'],
        period='24h'
    )
    print(f"Multiple token stats: {json.dumps(multi_stats, indent=2)}")
    
except Exception as e:
    print(f"API Error: {e}")
```

### PHP

```php
<?php

class CastDexAPI {
    private $apiKey;
    private $baseUrl = 'https://castdex.vercel.app/api/external/votes';
    
    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
    }
    
    private function makeRequest($endpoint, $params = []) {
        $url = $this->baseUrl . $endpoint;
        if (!empty($params)) {
            $url .= '?' . http_build_query($params);
        }
        
        $headers = [
            'X-API-Key: ' . $this->apiKey,
            'Content-Type: application/json'
        ];
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($httpCode !== 200) {
            throw new Exception("HTTP Error: $httpCode");
        }
        
        $data = json_decode($response, true);
        if (!$data['success']) {
            throw new Exception($data['error']);
        }
        
        return $data['data'];
    }
    
    public function getTokenStats($tokenId = null, $tokenIds = null, $period = '24h', $groupBy = null) {
        $params = ['period' => $period];
        
        if ($tokenId) {
            $params['token_id'] = $tokenId;
        } elseif ($tokenIds) {
            $params['token_ids'] = implode(',', $tokenIds);
        } else {
            throw new Exception("Either tokenId or tokenIds must be provided");
        }
        
        if ($groupBy) {
            $params['group_by'] = $groupBy;
        }
        
        return $this->makeRequest('/stats', $params);
    }
    
    public function getTrendingTokens($period = '24h', $type = 'total', $limit = 10) {
        $params = [
            'period' => $period,
            'type' => $type,
            'limit' => $limit
        ];
        
        return $this->makeRequest('/trending', $params);
    }
}

// Usage example
$api = new CastDexAPI('your_64_character_api_key_here');

try {
    // Get token statistics
    $stats = $api->getTokenStats(
        'solana_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
        null,
        '24h'
    );
    echo "Token stats: " . json_encode($stats, JSON_PRETTY_PRINT) . "\n";
    
    // Get trending tokens
    $trending = $api->getTrendingTokens('24h', 'total', 5);
    echo "Trending: " . json_encode($trending, JSON_PRETTY_PRINT) . "\n";
    
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

?>
```

### cURL

```bash
#!/bin/bash

API_KEY="your_64_character_api_key_here"
BASE_URL="https://castdex.vercel.app/api/external/votes"

# Function to make API calls
call_api() {
    local endpoint="$1"
    local params="$2"
    
    curl -s \
        -H "X-API-Key: $API_KEY" \
        -H "Content-Type: application/json" \
        "$BASE_URL$endpoint?$params"
}

# Get token statistics
echo "=== Token Statistics ==="
call_api "/stats" "token_id=solana_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&period=24h" | jq '.'

echo -e "\n=== Multiple Token Statistics ==="
call_api "/stats" "token_ids=token1,token2,token3&period=7d" | jq '.'

echo -e "\n=== Trending Tokens ==="
call_api "/trending" "period=24h&type=total&limit=10" | jq '.'

echo -e "\n=== Trending by Rockets ==="
call_api "/trending" "period=7d&type=rockets&limit=5" | jq '.'

echo -e "\n=== Hourly Breakdown ==="
call_api "/stats" "token_id=example_token&group_by=hour&period=24h" | jq '.'
```

***

## Best Practices

### 1. Error Handling

Always check the `success` field in API responses:

```javascript
const response = await fetch(url, { headers: { 'X-API-Key': apiKey } });
const data = await response.json();

if (!data.success) {
  console.error('API Error:', data.error);
  // Handle error appropriately
  return;
}

// Process successful response
console.log('Data:', data.data);
```

### 2. Rate Limit Management

Monitor rate limit headers and implement backoff:

```javascript
function checkRateLimit(response) {
  const remaining = response.headers.get('X-RateLimit-Remaining');
  const reset = response.headers.get('X-RateLimit-Reset');
  
  if (remaining && parseInt(remaining) < 10) {
    console.warn(`Low rate limit: ${remaining} requests remaining`);
    console.warn(`Resets at: ${reset}`);
  }
}
```

### 3. Caching

Cache responses to reduce API calls:

```javascript
const cache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function getCachedStats(tokenId, period) {
  const cacheKey = `${tokenId}-${period}`;
  const cached = cache.get(cacheKey);
  
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }
  
  const data = await getTokenStats(tokenId, period);
  cache.set(cacheKey, { data, timestamp: Date.now() });
  
  return data;
}
```

### 4. Retry Logic

Implement exponential backoff for transient errors:

```javascript
async function apiCallWithRetry(apiCall, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await apiCall();
    } catch (error) {
      if (attempt === maxRetries) throw error;
      
      const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
      console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
```

### 5. Batch Requests

Use the `token_ids` parameter to request multiple tokens efficiently:

```javascript
// Instead of multiple requests
const stats1 = await getTokenStats('token1');
const stats2 = await getTokenStats('token2');
const stats3 = await getTokenStats('token3');

// Use a single batch request
const allStats = await getTokenStats(null, ['token1', 'token2', 'token3']);
```

***

## Common Use Cases

### 1. Token Dashboard

Display comprehensive token information:

```javascript
async function createTokenDashboard(tokenId) {
  try {
    const [stats24h, stats7d, trending] = await Promise.all([
      getTokenStats(tokenId, '24h'),
      getTokenStats(tokenId, '7d'), 
      getTrendingTokens(100) // Get rank in top 100
    ]);
    
    const tokenRank = trending.findIndex(t => t.token_id === tokenId) + 1;
    
    return {
      token_id: tokenId,
      daily_stats: stats24h[tokenId],
      weekly_stats: stats7d[tokenId],
      trending_rank: tokenRank || null,
      last_updated: new Date().toISOString()
    };
  } catch (error) {
    console.error('Dashboard error:', error);
    throw error;
  }
}
```

### 2. Trending Analysis

Track trending patterns over time:

```javascript
async function analyzeTrending() {
  const periods = ['1h', '24h', '7d'];
  const types = ['total', 'likes', 'rockets'];
  
  const analysis = {};
  
  for (const period of periods) {
    analysis[period] = {};
    for (const type of types) {
      analysis[period][type] = await getTrendingTokens(period, type, 20);
    }
  }
  
  return analysis;
}
```

### 3. Portfolio Tracking

Monitor multiple tokens for a portfolio:

```javascript
async function trackPortfolio(tokenIds) {
  const portfolioStats = await getTokenStats(null, tokenIds, '24h');
  
  return Object.values(portfolioStats).map(token => ({
    token_id: token.token_id,
    total_votes: token.total,
    engagement_rate: (token.total / token.unique_voters).toFixed(2),
    like_ratio: (token.likes / token.total * 100).toFixed(1) + '%',
    rocket_ratio: (token.rockets / token.total * 100).toFixed(1) + '%'
  }));
}
```

***

## Troubleshooting

### Common Issues

#### Authentication Errors (401)

```json
{
  "success": false,
  "error": "Invalid or missing API key"
}
```

**Solutions:**

* Verify your API key is exactly 64 alphanumeric characters
* Ensure the API key is included in the request header
* Check that your API key is active and not expired
* Contact support if the issue persists

#### Rate Limit Exceeded (429)

```json
{
  "success": false,
  "error": "Rate limit exceeded for minute. Try again after 2025-07-17T23:00:00.000Z"
}
```

**Solutions:**

* Implement request throttling in your application
* Add retry logic with exponential backoff
* Cache responses to reduce API calls
* Contact support to discuss higher rate limits

#### Bad Request (400)

```json
{
  "success": false,
  "error": "Either token_id or token_ids parameter is required"
}
```

**Solutions:**

* Check that all required parameters are included
* Validate parameter formats (e.g., period values)
* Ensure token\_ids list doesn't exceed 100 tokens
* Review the API documentation for correct parameter usage

### Debug Checklist

1. **API Key Format**: Verify 64 alphanumeric characters
2. **Headers**: Ensure API key is in the correct header
3. **URL**: Check the base URL and endpoint paths
4. **Parameters**: Validate all parameter names and values
5. **Rate Limits**: Monitor usage and implement proper throttling
6. **Response Handling**: Always check the `success` field

***

## Support

### Getting Help

* **Documentation**: This guide covers all API functionality
* **Status Page**: Check \[status.castdex.com] for system status
* **Support Email**: \[<api-support@castdex.com>] for technical assistance

### Reporting Issues

When reporting issues, please include:

* Your API key (first 10 characters only)
* Request URL and parameters
* Response received
* Timestamp of the issue
* Programming language/framework used

### Feature Requests

We're always looking to improve the API. Submit feature requests to \[<feedback@castdex.com>] with:

* Detailed description of the requested feature
* Use case explanation
* Expected response format
* Priority level for your integration

***

## Changelog

### Version 1.0 (July 2025)

* Initial API release
* Token statistics endpoint
* Trending tokens endpoint
* API key authentication
* Rate limiting implementation
* Comprehensive documentation

***

*Last Updated: July 17, 2025*\
\&#xNAN;*API Version: 1.0*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.castdex.app/castdex-voting-api-documentation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
