Analytics
Event tracking, search, and reporting
Track user events, search content, and access platform analytics.
Analytics & Search
Comprehensive event tracking, full-text search across all content, and advanced reporting for data-driven insights.
Overview
The Analytics Service provides:
- Event tracking
- Search functionality
- User activity timelines
- Dashboard analytics
- Leaderboards — peak concurrency, gifting, and top viewers
- Funnel analysis
Base Paths:
- Analytics:
/v1/analytics - Search:
/v1/search
Event Tracking
Track Single Event
POST /v1/analytics/trackTry it out:
{
"eventName": "page.view",
"eventType": "navigation",
"metadata": {
"page": "/dashboard",
"referrer": "/login",
"device": "mobile"
}
}Track Bulk Events
POST /v1/analytics/track/bulk{
"events": [
{
"eventName": "button.click",
"eventType": "interaction",
"metadata": { "button": "buy_now" }
},
{
"eventName": "form.submit",
"eventType": "conversion",
"metadata": { "form": "signup" }
}
]
}Get User Events
GET /v1/analytics/events| Parameter | Type | Description |
|---|---|---|
| page | number | Page number |
| limit | number | Results per page |
| eventType | string | Filter by type |
| startDate | datetime | Start date |
| endDate | datetime | End date |
{
"success": true,
"status": 200,
"data": [
{
"id": "event_123",
"userId": "user_456",
"eventName": "trade.created",
"eventType": "trading",
"metadata": {
"tradeId": "trade_789",
"symbol": "EUR/USD"
},
"timestamp": "2025-10-21T12:00:00.000Z"
}
],
"pagination": {...}
}Search
Search All Content
GET /v1/search| Parameter | Type | Required | Description |
|---|---|---|---|
| q | string | Yes | Search query |
| type | string | No | Filter by type (post, course, trade, user) |
| page | number | No | Page number |
| limit | number | No | Results per page |
Example:
# Development
curl "https://api-gateway.dev.1houseglobalservices.com/v1/search?q=EUR/USD&type=post" \
-H "X-API-Key: your-development-api-key" \
-H "Authorization: Bearer your-jwt-token"
# Production
curl "https://api-gateway.prod.1houseglobalservices.com/v1/search?q=EUR/USD&type=post" \
-H "X-API-Key: your-production-api-key" \
-H "Authorization: Bearer your-jwt-token"{
"success": true,
"status": 200,
"data": {
"results": [
{
"type": "post",
"id": "post_123",
"title": "EUR/USD Analysis",
"excerpt": "...EUR/USD breaking resistance at 1.0950...",
"url": "/posts/post_123",
"score": 0.95,
"createdAt": "2025-10-21T10:00:00.000Z"
},
{
"type": "course",
"id": "course_456",
"title": "EUR/USD Trading Masterclass",
"excerpt": "Learn to trade EUR/USD like a pro",
"url": "/courses/course_456",
"score": 0.87,
"createdAt": "2025-01-15T00:00:00.000Z"
}
],
"totalResults": 45
},
"pagination": {
"page": 1,
"limit": 10,
"total": 45,
"pages": 5
}
}Get Search Suggestions
GET /v1/search/suggestions?q=eur{
"success": true,
"status": 200,
"data": [
"EUR/USD",
"EUR/USD trading strategies",
"EUR/USD analysis",
"EUR/GBP",
"European Central Bank"
]
}Get Search History
GET /v1/search/history{
"success": true,
"status": 200,
"data": [
{
"query": "EUR/USD analysis",
"resultCount": 45,
"searchedAt": "2025-10-21T12:00:00.000Z"
}
]
}Dashboard Analytics (Admin)
Get Dashboard Data
GET /v1/analytics/dashboard| Parameter | Type | Description |
|---|---|---|
| period | string | 7d, 30d, 90d, 1y |
{
"success": true,
"status": 200,
"data": {
"overview": {
"totalUsers": 12500,
"activeUsers": 3456,
"newUsers": 234,
"totalRevenue": 45678.00
},
"traffic": {
"pageViews": 156789,
"uniqueVisitors": 8901,
"averageSessionDuration": 420
},
"engagement": {
"postsCreated": 456,
"commentsPosted": 1234,
"reactionsGiven": 5678
},
"trading": {
"tradesCreated": 789,
"tradesWon": 512,
"winRate": 64.89
},
"education": {
"coursesCompleted": 234,
"lessonsWatched": 3456,
"totalWatchTime": 123456
}
}
}Leaderboards
Four rankings behind one call: the educators with the most people watching at the same moment, who gave and received the most Keys, and who watched the most.
GET /v1/analytics/leaderboardsOne payload, two audiences
1hstream's Leaderboard tab reads this endpoint with the viewer's own JWT. The management dashboard reads GET /v1/admin/analytics/leaderboards, which takes the same parameters and returns the same shape. Both are assembled by the same code, so the two surfaces can never disagree about who is winning.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
| period | string | 24h, 7d (default), 30d, 90d |
| startDate | string | ISO 8601. Overrides period |
| endDate | string | ISO 8601 |
| platform | string | ios, android, web. Omit for every surface |
| contentType | string | livestream, meeting, lesson, video, story. Narrows the watch-derived boards |
| limit | number | Rows per board (default 10, max 50) |
What peak concurrency actually counts
peakConcurrent is the most watch sessions open at the same moment, not the total number of people who passed through. It is counted per session rather than per user, because two tabs are two streams being served — which is what the number is for when sizing a broadcast. A session that started before the window opened still counts toward the window it overlaps.
Try it out:
Example Response:
{
"success": true,
"status": 200,
"data": {
"period": {
"key": "30d",
"startDate": "2026-02-01T00:00:00.000Z",
"endDate": "2026-03-03T00:00:00.000Z"
},
"filters": { "platform": null, "contentType": null, "limit": 10 },
"partial": false,
"unavailable": [],
"totals": {
"platformPeakConcurrent": 1284,
"platformPeakAt": "2026-02-18T21:04:00.000Z",
"giftedKeys": 48210,
"gifts": 3129,
"gifters": 812,
"giftRecipients": 46
},
"topConcurrentEducators": [
{
"rank": 1,
"educatorId": "68f2c1a4e9b21c0012aa77d1",
"peakConcurrent": 412,
"peakAt": "2026-02-18T21:04:00.000Z",
"watchSecs": 1840233,
"sessions": 5120,
"uniqueViewers": 3104,
"name": "Leo Diaz",
"username": "leodiaz",
"slug": "leo-diaz",
"avatar": "https://cdn.1house.tv/educators/leo.jpg"
}
],
"topGifters": [
{
"rank": 1,
"userId": "68f2c1a4e9b21c0012aa8814",
"keys": 2400,
"gifts": 118,
"counterparties": 12,
"lastGiftAt": "2026-03-02T18:22:11.000Z",
"name": "Jane Ruiz",
"username": "janer",
"slug": null,
"avatar": null
}
],
"topGifted": [
{
"rank": 1,
"userId": "68f2c1a4e9b21c0012aa77d1",
"keys": 9120,
"gifts": 640,
"counterparties": 388,
"lastGiftAt": "2026-03-02T20:11:04.000Z",
"name": "Leo Diaz",
"username": "leodiaz",
"slug": "leo-diaz",
"avatar": "https://cdn.1house.tv/educators/leo.jpg"
}
],
"topViewers": [
{
"rank": 1,
"userId": "68f2c1a4e9b21c0012aa9002",
"watchSecs": 184320,
"sessions": 212,
"contentCount": 31,
"contentTypes": ["livestream", "meeting"],
"lastWatchedAt": "2026-03-02T22:40:00.000Z",
"name": "Marcus Hall",
"username": "mhall",
"slug": null,
"avatar": null
}
],
"lastUpdated": "2026-03-03T09:12:44.000Z"
}
}Board Fields:
| Board | Ranked by | Key fields |
|---|---|---|
| topConcurrentEducators | peakConcurrent | peakAt, watchSecs, sessions, uniqueViewers |
| topGifters | keys given | gifts, counterparties (distinct recipients) |
| topGifted | keys received | gifts, counterparties (distinct senders) |
| topViewers | watchSecs | sessions, contentCount, contentTypes |
Check `partial` before trusting an empty board
The gifting boards are built from the Keys ledger in the wallet service. If that service cannot be reached, those two boards come back empty with partial: true and unavailable: ["gifting"] — which is not the same as nobody having gifted anything. Render the difference rather than showing an empty list.
Gifts count every surface
A gift is a completed Keys transfer stamped as one, whichever surface produced it: livestream tips, educator gifts and SafeHouse meeting tips all land on the same board. Admin transfers and wallet funding are excluded — they are not somebody choosing to give their Keys away.
Caching: responses are cached for 120 seconds. lastUpdated says when the payload was built.
User Activity
Get Activity Timeline
GET /v1/analytics/user/activity| Parameter | Type | Description |
|---|---|---|
| days | number | Number of days (default: 30) |
{
"success": true,
"status": 200,
"data": [
{
"date": "2025-10-21",
"events": 45,
"breakdown": {
"page.view": 20,
"trade.created": 3,
"post.created": 2,
"course.viewed": 5
}
}
]
}Get User Statistics
GET /v1/analytics/user/stats{
"success": true,
"status": 200,
"data": {
"totalEvents": 1234,
"eventsThisWeek": 89,
"eventsThisMonth": 345,
"mostCommonEvents": [
{ "eventName": "page.view", "count": 567 },
{ "eventName": "trade.created", "count": 89 }
],
"engagement": {
"posts": 12,
"comments": 45,
"reactions": 123
},
"trading": {
"trades": 34,
"winRate": 67.65
},
"education": {
"coursesEnrolled": 5,
"lessonsCompleted": 89
}
}
}Funnel Analysis (Admin)
Analyze Conversion Funnel
POST /v1/analytics/funnel{
"eventSequence": [
"page.view.pricing",
"button.click.signup",
"form.submit.signup",
"payment.completed"
],
"startDate": "2025-10-01",
"endDate": "2025-10-21"
}{
"success": true,
"status": 200,
"data": {
"funnel": [
{
"step": "page.view.pricing",
"count": 1000,
"percentage": 100,
"dropoff": 0
},
{
"step": "button.click.signup",
"count": 450,
"percentage": 45,
"dropoff": 55
},
{
"step": "form.submit.signup",
"count": 380,
"percentage": 38,
"dropoff": 7
},
{
"step": "payment.completed",
"count": 234,
"percentage": 23.4,
"dropoff": 14.6
}
],
"conversionRate": 23.4,
"totalUsers": 1000
}
}Aggregated Analytics (Admin)
Get Event Aggregates
GET /v1/analytics/aggregate| Parameter | Type | Description |
|---|---|---|
| groupBy | string | day, week, month |
| eventType | string | Filter by event type |
| eventName | string | Filter by event name |
| startDate | datetime | Start date |
| endDate | datetime | End date |
Get Event Counts by Type
GET /v1/analytics/events/types{
"success": true,
"status": 200,
"data": [
{ "eventType": "trading", "count": 456 },
{ "eventType": "education", "count": 789 },
{ "eventType": "navigation", "count": 1234 },
{ "eventType": "engagement", "count": 567 }
]
}Get Popular Events
GET /v1/analytics/events/popular{
"success": true,
"status": 200,
"data": [
{ "eventName": "page.view", "count": 12345 },
{ "eventName": "trade.created", "count": 456 },
{ "eventName": "course.viewed", "count": 789 }
]
}Common Events to Track
Navigation
page.view- Page viewsection.scroll- Scrolled to sectionexit.intent- User about to leave
Engagement
button.click- Button clickedform.submit- Form submittedvideo.play- Video startedvideo.complete- Video finished
Trading
trade.created- New tradetrade.viewed- Trade idea viewedmarket.searched- Symbol searched
Education
course.viewed- Course page viewlesson.started- Lesson playback startedlesson.completed- Lesson finishedcourse.enrolled- User enrolled
Conversion
signup.started- Signup form openedsignup.completed- Account createdpayment.initiated- Checkout startedpayment.completed- Purchase made
Client-Side Tracking
// Tracking utility
class AnalyticsTracker {
constructor(apiKey, token) {
this.apiKey = apiKey;
this.token = token;
this.queue = [];
this.flushInterval = 10000; // 10 seconds
setInterval(() => this.flush(), this.flushInterval);
}
track(eventName, eventType, metadata = {}) {
this.queue.push({
eventName,
eventType,
metadata,
timestamp: new Date().toISOString()
});
// Flush if queue gets large
if (this.queue.length >= 10) {
this.flush();
}
}
async flush() {
if (this.queue.length === 0) return;
const events = [...this.queue];
this.queue = [];
await fetch('/v1/analytics/track/bulk', {
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ events })
});
}
}
// Usage
const tracker = new AnalyticsTracker(apiKey, token);
// Track page view
tracker.track('page.view', 'navigation', { page: '/dashboard' });
// Track button click
document.getElementById('tradeBtn').addEventListener('click', () => {
tracker.track('button.click', 'interaction', { button: 'create_trade' });
});
// Track form submit
form.addEventListener('submit', () => {
tracker.track('form.submit', 'conversion', { form: 'signup' });
});Related Documentation
- Indicators - Indicator access, granted and revoked from the backoffice
- Wallet - Keys and the ledger the gifting boards are built from
- Search API - Advanced search features
- Reporting - Generate reports
- Events - Event schema reference