A lightweight, privacy-focused analytics tracking and reporting system built on Cloudflare Workers and D1.
Cloudflare Applytics is designed to be a privacy-aware tracking solution for app developers who need minimal metrics like app installs, purchases, and user engagement without compromising user privacy.
Privacy features:
- No personal information tracking
- No cookies or persistent identifiers
- No cross-site tracking
- No IP address storage
- Fully compliant with privacy regulations
- Data stored in your own Cloudflare account
This solution is ideal for developers who want basic app metrics without becoming entangled in complex privacy requirements or third-party analytics that might compromise user data.
- Simple API key authentication
- Track single events or batches
- Category-based event organization
- Cumulative stats with filtering options
- Timeseries data with custom periods
- Multiple metric comparison
- Event history and detailed reporting
- Dashboard with trending metrics
Track critical milestones in your app's lifecycle:
# Track app installation
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "install", "value": 1, "category": "lifecycle"}'
# Track app update
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "update", "qualifier": "1.2.0", "category": "lifecycle"}'
# Track app uninstall
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "uninstall", "category": "lifecycle"}'Measure how users interact with your app without tracking personal data:
# Track page views
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "page_view", "qualifier": "home"}'
# Track feature usage
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "feature_used", "qualifier": "dark_mode"}'
# Track button clicks
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "button_click", "qualifier": "signup"}'Monitor purchase and subscription events:
# Track one-time purchase
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "purchase", "qualifier": "premium_upgrade", "value": 999}'
# Track subscription start
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "subscription", "qualifier": "monthly_plan", "value": 499}'
# Track in-app purchase
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "iap", "qualifier": "coins_pack", "value": 299}'- Node.js (version 14 or higher)
- Cloudflare account
- Wrangler CLI
- Clone this repository
git clone https://github.com/arraypress/cloudflare-applytics.git
cd cloudflare-applytics- Install dependencies
npm install- Update your
wrangler.tomlconfiguration
name = "cloudflare-applytics"
main = "src/index.js"
compatibility_date = "2023-10-02"
[vars]
ENVIRONMENT = "development"
API_KEY = "your-development-api-key"
[[d1_databases]]
binding = "DB"
database_name = "applytics_local"
database_id = "applytics-local"- Create the D1 database (only needed once)
wrangler d1 create applytics_local
# Update the database_id in wrangler.toml with the ID returned- Initialize the database with schema
npm run setup- Check that tables were created successfully
npm run dbStart the local development server:
npm run dev# Development
npm run dev # Start local development server
npm run build # Build the project
# Deployment
npm run deploy # Deploy to development environment
npm run deploy:prod # Deploy to production environment
# Database Setup & Management
npm run setup # Setup local database with schema
npm run setup:prod # Setup production database with schema
npm run db # Show database tables
npm run db:info # Show database table information
npm run db:events # Show all events
npm run db:stats # Show all stats
npm run db:reset # Reset database (deletes all data)-
Update production configuration in
wrangler.toml -
Set API key as a secret:
wrangler secret put API_KEY --env production- Deploy to production:
npm run deploy:prodEvents are individual occurrences tracked in real-time:
- Each event is recorded with a timestamp
- Events have types, optional qualifiers, and values
- Events are stored in the events table
- Used for historical analysis and timeseries data
Stats are cumulative counters derived from events:
- Automatically updated when events are tracked
- Represent aggregate metrics (e.g., total page views)
- Stored in the stats table for efficient querying
- Used for dashboards and overall analytics
Events consist of:
event_type: The main action being tracked (e.g., "page_view", "purchase")qualifier: Optional sub-type or specific instance (e.g., "home", "premium_plan")value: Numeric value associated with the event (default: 1)category: Organizational group (e.g., "engagement", "revenue")country: Country code (automatically detected or can be provided)timestamp: When the event occurred (defaults to current time)
When an event is tracked, it creates or updates a corresponding stat with the key format event_type.qualifier.
Example 1: Tracking App Usage
Track daily active users by recording a "session" event when your app starts:
# User opens the app - track a session start
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "session", "qualifier": "start"}'
# Later, analyze daily active users
curl "https://your-worker.workers.dev/timeseries?metric=session.start&period=day&limit=30" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key"Example 2: Feature Adoption
Track how many users enable a new feature:
# User enables dark mode
curl -X POST "https://your-worker.workers.dev/track" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"event_type": "feature_enabled", "qualifier": "dark_mode"}'
# Get total count of feature enablement
curl "https://your-worker.workers.dev/stats?prefix=feature_enabled" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key"Example 3: Conversion Funnel
Track user progression through a signup flow:
# Track each step in the signup flow
curl -X POST "https://your-worker.workers.dev/track/batch" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"events": [
{"event_type": "signup_step", "qualifier": "view_form"},
{"event_type": "signup_step", "qualifier": "submit_email"},
{"event_type": "signup_step", "qualifier": "verify_email"},
{"event_type": "signup_step", "qualifier": "complete"}
]
}'
# Compare conversion rates between steps
curl "https://your-worker.workers.dev/timeseries?metrics=signup_step.view_form,signup_step.submit_email,signup_step.verify_email,signup_step.complete&period=day" \
-H "X-App-ID: your-app-id" \
-H "X-API-Key: your-api-key"All API requests require the following headers:
X-App-ID: Your application IDX-API-Key: Your API key
Track a single event.
Request Body:
{
"event_type": "page_view",
"qualifier": "home",
"value": 1,
"category": "engagement",
"timestamp": 1678912345,
"country": "US"
}Parameters:
event_type(required): Type of eventqualifier(optional): Event qualifiervalue(optional): Numeric value, defaults to 1category(optional): Event categorytimestamp(optional): Unix timestamp, defaults to current timecountry(optional): Country code, automatically detected from request if not provided
Response:
{
"success": true,
"app_id": "app1",
"metric": "page_view.home",
"category": "engagement",
"value": 42,
"timestamp": 1678912345,
"country": "US"
}Track multiple events in a single request.
Request Body:
{
"events": [
{
"event_type": "page_view",
"qualifier": "home",
"value": 1
},
{
"event_type": "button_click",
"qualifier": "signup",
"category": "interaction"
}
]
}Response:
{
"success": true,
"app_id": "app1",
"processed": 2,
"events": [
{
"event_type": "page_view",
"qualifier": "home",
"metric": "page_view.home",
"category": "engagement",
"country": "US"
},
{
"event_type": "button_click",
"qualifier": "signup",
"metric": "button_click.signup",
"category": "interaction",
"country": "US"
}
]
}Get all stats for an application.
Query Parameters:
prefix(optional): Filter metrics starting with prefixcategory(optional): Filter metrics by categoryformat(optional): Response format, either 'simple' (default) or 'detailed'view(optional): View type, supports 'default', 'category', 'top'paginate(optional): Enable pagination (true/false)page(optional): Page number for paginationpageSize(optional): Items per page for pagination
Response (simple format):
{
"page_view.home": 42,
"button_click.signup": 18
}Response (detailed format):
{
"app_id": "app1",
"filters": {
"prefix": null,
"category": "engagement",
"country": null
},
"data": [
{
"metric": "page_view.home",
"value": 42,
"category": "engagement",
"last_updated": "2025-03-09T12:34:56Z"
}
]
}Get top metrics by value.
Query Parameters:
category(optional): Filter by categorylimit(optional): Maximum number of results, defaults to 10sort(optional): Sort direction, 'asc' or 'desc' (default)
Response:
{
"app_id": "app1",
"category": "all",
"sort": "desc",
"metrics": [
{
"metric": "page_view.home",
"value": 42,
"category": "engagement",
"last_updated": "2025-03-09T12:34:56Z"
}
]
}Get stats grouped by category.
Response:
{
"app_id": "app1",
"groupBy": "category",
"data": {
"engagement": 156,
"revenue": 2850,
"lifecycle": 42
}
}Get time-based data for a specific metric or multiple metrics.
Query Parameters:
metric(required if metrics not provided): Single metric namemetrics(required if metric not provided): Comma-separated list of metrics to compareperiod(optional): Time grouping, one of 'hour', 'day', 'week', 'month', defaults to 'day'limit(optional): Maximum number of data points, defaults to 30from(optional): Start date (Unix timestamp or ISO format)to(optional): End date (Unix timestamp or ISO format)country(optional): Filter by country code
Response for single metric:
{
"app_id": "app1",
"metric": "page_view.home",
"period": "day",
"from": "2025-02-07T00:00:00Z",
"to": "2025-03-09T00:00:00Z",
"country": null,
"data": [
{
"time_period": "2025-03-08",
"total": 15
},
{
"time_period": "2025-03-09",
"total": 27
}
]
}Response for multiple metrics:
{
"app_id": "app1",
"metrics": ["page_view.home", "page_view.settings"],
"period": "day",
"from": "2025-02-07T00:00:00Z",
"to": "2025-03-09T00:00:00Z",
"country": null,
"data": [
{
"time_period": "2025-03-08",
"page_view.home": 15,
"page_view.settings": 8
},
{
"time_period": "2025-03-09",
"page_view.home": 27,
"page_view.settings": 12
}
]
}Get event history for an application.
Query Parameters:
type(optional): Filter by event typecategory(optional): Filter by event categorycountry(optional): Filter by country codelimit(optional): Maximum number of events to return, defaults to 50from(optional): Start date (Unix timestamp or ISO format), defaults to 24 hours ago
Response:
{
"app_id": "app1",
"from": "2025-03-08T12:34:56Z",
"count": 2,
"events": [
{
"id": 123,
"event_type": "page_view",
"event_category": "engagement",
"qualifier": "home",
"value": 1,
"country": "US",
"timestamp": "2025-03-09T12:34:56Z"
},
{
"id": 124,
"event_type": "button_click",
"event_category": "interaction",
"qualifier": "signup",
"value": 1,
"country": "US",
"timestamp": "2025-03-09T12:35:23Z"
}
]
}Get information about a specific app.
Query Parameters:
view(optional): View type, either 'summary' (default) or 'dashboard'
Response (summary view):
{
"app_id": "app1",
"total_events": 1542,
"first_event": "2025-01-15T08:23:45Z",
"last_event": "2025-03-09T12:34:56Z",
"metrics_count": 25,
"recent_events": [
{
"event_type": "page_view",
"qualifier": "home",
"category": "engagement",
"country": "US",
"timestamp": "2025-03-09T12:34:56Z"
}
]
}Get dashboard data for an application.
Response:
{
"app_id": "app1",
"summary": {
"total_events": 1542,
"first_event": "2025-01-15T08:23:45Z",
"last_event": "2025-03-09T12:34:56Z",
"metrics_count": 25
},
"recent_activity": {
"last_24h_events": 145,
"last_24h_value": 198
},
"categories": [
{
"category": "engagement",
"total": 856
},
{
"category": "revenue",
"total": 3540
}
],
"top_countries": [
{
"country": "US",
"event_count": 523,
"value_sum": 1245
},
{
"country": "GB",
"event_count": 218,
"value_sum": 532
}
]
}Get a list of all available apps.
Query Parameters:
stats(optional): Include stats summary for each app (true/false)
Response:
{
"apps": ["app1", "app2", "app3"]
}Response with stats:
{
"apps": [
{
"app_id": "app1",
"total_events": 1542,
"first_event": "2025-01-15T08:23:45Z",
"last_event": "2025-03-09T12:34:56Z",
"metrics_count": 25
},
{
"app_id": "app2",
"total_events": 856,
"first_event": "2025-02-03T10:15:32Z",
"last_event": "2025-03-09T11:22:33Z",
"metrics_count": 18
}
]
}- ApplyticsKit - Swift client for iOS/macOS
- More coming soon!
This project is licensed under the MIT License - see the LICENSE file for details.