Professional View Counter
Advanced serverless analytics with enterprise-grade
admin management
⚡ High Performance
🔒 Secure Admin
📊 Advanced Analytics
💰 Cost Optimized
✨ Key Features
⚡
Ultra Fast
Optimized for minimal latency with intelligent
caching and efficient database queries.
🔒
Secure Admin
Password-protected admin panel with
environment-based configuration and role
management.
📊
Rich Analytics
Detailed view tracking, visitor analytics, and
comprehensive reporting dashboard.
💰
Cost Effective
Designed to stay within Cloudflare's free tier
with optional premium features.
🚀 Live Demo
Try out the view counter with real-time updates
🎨 Badge Generator
Badge Preview:

📚 Complete Documentation
Everything you need to integrate CFlairCounter into your projects
🚀 Quick Start
📖 API Reference
💡 Examples
⚙️ Configuration
🚀 Quick Start
Get your view counter running in under 2 minutes:
1. Basic Setup
fetch('https://your-domain.com/api/views/my-project', {
method: 'POST'
})
.then(response => response.json())
.then(data => console.log('✅ View tracked!', data.totalViews));
fetch('https://your-domain.com/api/views/my-project')
.then(response => response.json())
.then(data => {
document.getElementById('view-count').textContent = data.totalViews;
});
2. Add to Your HTML
<div>
<span>Views: </span>
<span id="view-count">Loading...</span>
</div>
<img src="https://your-domain.com/api/views/my-project/badge"
alt="View Count" />
3. GitHub README Integration

[](https://your-domain.com)
🔗 Complete API Reference
📈 POST /api/views/{project-name}
Purpose: Increment view count for a project
| Parameter |
Type |
Description |
Required |
| project-name |
string |
Unique project identifier (max 100 chars) |
✅ Yes |
POST /api/views/my-awesome-project
{
"success": true,
"projectName": "my-awesome-project",
"totalViews": 1337,
"uniqueViews": 892,
"timestamp": "2025-08-28T13:45:30Z"
}
📊 GET /api/views/{project-name}
Purpose: Get current statistics for a project
GET /api/views/my-awesome-project
{
"success": true,
"projectName": "my-awesome-project",
"totalViews": 1337,
"uniqueViews": 892,
"description": null,
"createdAt": "2025-08-01T10:30:00Z"
}
{
"success": true,
"projectName": "new-project",
"totalViews": 0,
"uniqueViews": 0,
"description": null,
"createdAt": null
}
🎨 GET /api/views/{project-name}/badge
Purpose: Generate SVG badge with view count
| Parameter |
Type |
Default |
Options |
| color |
string |
blue |
blue, green, red, orange, purple, brightgreen |
| label |
string |
views |
Any custom text (URL encoded) |
GET /api/views/my-project/badge
GET /api/views/my-project/badge?color=brightgreen&label=downloads
💊 GET /health
Purpose: Health check endpoint
{
"success": true,
"status": "ok",
"timestamp": "2025-08-28T13:45:30Z",
"worker": "cflaircounter-api",
"version": "2.0.0"
}
⚡ Integration Examples
🌐 JavaScript (Browser)
async function trackView(projectName) {
try {
const response = await fetch(`/api/views/${projectName}`, {
method: 'POST'
});
const data = await response.json();
if (data.success) {
console.log(`📈 Views: ${data.totalViews}`);
updateViewCounter(data.totalViews);
}
} catch (error) {
console.error('❌ Failed to track view:', error);
}
}
async function displayViewCount(projectName) {
try {
const response = await fetch(`/api/views/${projectName}`);
const data = await response.json();
document.getElementById('view-count').textContent =
data.totalViews.toLocaleString();
document.getElementById('unique-count').textContent =
data.uniqueViews.toLocaleString();
} catch (error) {
console.error('❌ Failed to load stats:', error);
}
}
document.addEventListener('DOMContentLoaded', () => {
trackView('my-website');
displayViewCount('my-website');
});
🚀 Node.js (Server-side)
async function trackServerView(projectName) {
try {
const response = await fetch(`https://your-domain.com/api/views/${projectName}`, {
method: 'POST',
headers: {
'User-Agent': 'YourApp/1.0.0'
}
});
const data = await response.json();
console.log(`✅ Tracked view for ${projectName}: ${data.totalViews} total`);
return data;
} catch (error) {
console.error('❌ Tracking failed:', error);
throw error;
}
}
const axios = require('axios');
async function getProjectStats(projectName) {
try {
const { data } = await axios.get(`https://your-domain.com/api/views/${projectName}`);
return {
views: data.totalViews,
unique: data.uniqueViews,
created: data.createdAt
};
} catch (error) {
if (error.response?.status === 404) {
return { views: 0, unique: 0, created: null };
}
throw error;
}
}
const trackViews = (projectName) => async (req, res, next) => {
try {
await trackServerView(projectName);
} catch (error) {
console.warn('View tracking failed:', error.message);
}
next();
};
app.use('/my-project', trackViews('my-project'), myProjectRoutes);
🐍 Python
import requests
from typing import Dict, Optional
class ViewCounter:
def __init__(self, base_url: str):
self.base_url = base_url.rstrip('/')
def track_view(self, project_name: str) -> Dict:
"""Track a view for the given project."""
url = f"{self.base_url}/api/views/{project_name}"
try:
response = requests.post(url, timeout=5)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
print(f"❌ Failed to track view: {e}")
return {"success": False, "error": str(e)}
def get_stats(self, project_name: str) -> Dict:
"""Get current statistics for a project."""
url = f"{self.base_url}/api/views/{project_name}"
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
print(f"❌ Failed to get stats: {e}")
return {"success": False, "error": str(e)}
counter = ViewCounter("https://your-domain.com")
result = counter.track_view("my-python-project")
if result.get("success"):
print(f"📈 Total views: {result['totalViews']}")
stats = counter.get_stats("my-python-project")
if stats.get("success"):
print(f"📊 Views: {stats['totalViews']}, Unique: {stats['uniqueViews']}")
🦀 Rust
use reqwest;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
struct ViewResponse {
success: bool,
#[serde(rename = "totalViews")]
total_views: u32,
#[serde(rename = "uniqueViews")]
unique_views: u32,
#[serde(rename = "projectName")]
project_name: String,
}
async fn track_view(project_name: &str) -> Result<ViewResponse, reqwest::Error> {
let url = format!("https://your-domain.com/api/views/{}", project_name);
let client = reqwest::Client::new();
let response = client
.post(&url)
.send()
.await?
.json::<ViewResponse>()
.await?;
println!("✅ Tracked view for {}: {} total", project_name, response.total_views);
Ok(response)
}
async fn get_stats(project_name: &str) -> Result<ViewResponse, reqwest::Error> {
let url = format!("https://your-domain.com/api/views/{}", project_name);
let response = reqwest::get(&url)
.await?
.json::<ViewResponse>()
.await?;
Ok(response)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let stats = track_view("my-rust-project").await?;
println!("📊 Project stats: {:?}", stats);
Ok(())
}
🎯 Advanced Usage
⚙️ Environment Configuration
Configure your CFlairCounter instance with these environment variables:
| Variable |
Default |
Description |
ADMIN_PASSWORD |
admin123 |
Password for admin panel access |
ENABLE_ADMIN |
true |
Enable/disable admin functionality |
ENABLE_ANALYTICS |
false |
Enable detailed visitor tracking (uses more DB queries) |
MAX_PROJECTS |
100 |
Maximum number of projects to prevent abuse |
🏗️ Self-Hosting
git clone https://github.com/Life-Experimentalists/CFlair-Counter.git
cd CFlair-Counter
npm install
cp .env.example .env
# Edit .env with your settings
npm run db:create
npm run db:init
npm run deploy
📊 Rate Limiting & Performance
class ViewTracker {
constructor(cooldown = 60000) { // 1 minute cooldown
this.cooldown = cooldown;
this.lastTrack = new Map();
}
async track(projectName) {
const now = Date.now();
const last = this.lastTrack.get(projectName) || 0;
if (now - last < this.cooldown) {
console.log('⏱️ Cooldown active, skipping track');
return this.getStats(projectName);
}
this.lastTrack.set(projectName, now);
return this.trackView(projectName);
}
async trackView(projectName) {
// Implementation here
}
}
const tracker = new ViewTracker(30000); // 30 second cooldown
tracker.track('my-project');
🔄 Batch Operations
async function trackMultiple(projects) {
const promises = projects.map(project =>
fetch(`/api/views/${project}`, { method: 'POST' })
.then(r => r.json())
.catch(err => ({ success: false, project, error: err.message }))
);
const results = await Promise.all(promises);
return results;
}
async function getMultipleStats(projects) {
const promises = projects.map(async project => {
try {
const response = await fetch(`/api/views/${project}`);
const data = await response.json();
return { ...data, project };
} catch (error) {
return { success: false, project, error: error.message };
}
});
return Promise.all(promises);
}
const projects = ['project-1', 'project-2', 'project-3'];
const stats = await getMultipleStats(projects);
console.table(stats);
🔧 Troubleshooting
❌ Common Issues
| Issue |
Cause |
Solution |
| CORS Error |
Cross-origin request blocked |
Use server-side tracking or proxy requests |
| 404 Not Found |
Project name contains invalid characters |
Use alphanumeric characters, hyphens, underscores only |
| Admin Access Denied |
Wrong password or admin disabled |
Check ADMIN_PASSWORD environment variable |
| High Response Times |
Database not optimized |
Ensure D1 database is properly initialized |
🐛 Debugging
const DEBUG = true;
async function debugTrack(projectName) {
if (DEBUG) console.log(`🔍 Tracking view for: ${projectName}`);
try {
const start = performance.now();
const response = await fetch(`/api/views/${projectName}`, {
method: 'POST'
});
const duration = performance.now() - start;
if (DEBUG) console.log(`⏱️ Request took: ${duration.toFixed(2)}ms`);
const data = await response.json();
if (DEBUG) console.log('📊 Response:', data);
return data;
} catch (error) {
if (DEBUG) console.error('❌ Error:', error);
throw error;
}
}
📈 Monitoring
async function healthCheck() {
try {
const response = await fetch('/health');
const data = await response.json();
console.log('✅ System Status:', data.status);
console.log('📅 Last Check:', new Date(data.timestamp));
console.log('🔢 Version:', data.version);
return data.status === 'ok';
} catch (error) {
console.error('❌ Health check failed:', error);
return false;
}
}
setInterval(async () => {
const isHealthy = await healthCheck();
if (!isHealthy) {
console.warn('⚠️ System health check failed!');
// Implement your alerting logic here
}
}, 300000); // Check every 5 minutes
⚡ Performance & Limits
📊 System Specifications
< 100ms
Average Response Time
💰 Cost Optimization
CFlairCounter is designed to stay within Cloudflare's free tier limits:
- Database: Optimized queries (1-3 per request)
- Functions: Minimal compute time
- Caching: 1-hour badge cache reduces load
- Analytics: Optional to save resources
🔒 Security Features
Access-Control-Allow-Origin: *
Content-Security-Policy: default-src 'self'
X-Content-Type-Options: nosniff
Max requests per IP: 1000/hour
Project name validation: Alphanumeric + hyphens/underscores
Max project name length: 100 characters
IP address hashing: SHA-256 with salt
No personal data stored
GDPR compliant by design