···
1
1
+
# User Statistics API Documentation
2
2
+
3
3
+
## Overview
4
4
+
5
5
+
The User Statistics API provides comprehensive analytics about user growth, engagement, activity, and content distribution. All statistics support flexible time intervals and are optimized for dashboard visualizations.
6
6
+
7
7
+
**Base URL**: `/api/stats`
8
8
+
9
9
+
**Authentication**: All endpoints require API key authentication via Bearer token.
10
10
+
11
11
+
---
12
12
+
13
13
+
## Authentication
14
14
+
15
15
+
Include your API key in the `Authorization` header:
16
16
+
17
17
+
```bash
18
18
+
Authorization: Bearer YOUR_STATS_API_KEY
19
19
+
```
20
20
+
21
21
+
Set the API key via the `STATS_API_KEY` environment variable.
22
22
+
23
23
+
---
24
24
+
25
25
+
## Base Endpoint
26
26
+
27
27
+
```
28
28
+
GET /api/stats
29
29
+
```
30
30
+
31
31
+
All statistics are accessed through this single endpoint using the `type` query parameter to specify the stat type.
32
32
+
33
33
+
---
34
34
+
35
35
+
## Query Parameters
36
36
+
37
37
+
| Parameter | Type | Required | Description | Valid Values |
38
38
+
| ------------------- | ------- | -------- | ------------------------------------------ | ----------------------------------------------- |
39
39
+
| `type` | string | Yes | Type of statistics to retrieve | `growth`, `engagement`, `activity`, `breakdown` |
40
40
+
| `interval` | string | No | Time interval for aggregation | `day`, `week`, `month` (default: `day`) |
41
41
+
| `limit` | number | No | Number of intervals to return | 1-365 (default: 30) |
42
42
+
| `includeTimeSeries` | boolean | No | Include time series data (engagement only) | `true`, `false` (default: `false`) |
43
43
+
44
44
+
---
45
45
+
46
46
+
## Stat Types
47
47
+
48
48
+
### 1. Growth Statistics
49
49
+
50
50
+
**Description**: Track user sign-ups over time with cumulative totals and period-over-period growth.
51
51
+
52
52
+
**Query Parameters**:
53
53
+
54
54
+
- `type=growth` (required)
55
55
+
- `interval` (optional, default: `day`)
56
56
+
- `limit` (optional, default: 30)
57
57
+
58
58
+
**Request Example**:
59
59
+
60
60
+
```bash
61
61
+
curl -X GET "http://localhost:3000/api/stats?type=growth&interval=day&limit=30" \
62
62
+
-H "Authorization: Bearer YOUR_STATS_API_KEY"
63
63
+
```
64
64
+
65
65
+
**Response Schema**:
66
66
+
67
67
+
```typescript
68
68
+
{
69
69
+
dataPoints: Array<{
70
70
+
date: string; // ISO date string
71
71
+
totalUsers: number; // Cumulative total users up to this date
72
72
+
newUsers: number; // New users added in this period
73
73
+
}>;
74
74
+
currentTotal: number; // Total users as of now
75
75
+
periodStart: string; // ISO date string of first data point
76
76
+
periodEnd: string; // ISO date string of last data point
77
77
+
}
78
78
+
```
79
79
+
80
80
+
**Example Response**:
81
81
+
82
82
+
```json
83
83
+
{
84
84
+
"dataPoints": [
85
85
+
{
86
86
+
"date": "2026-03-11T00:00:00.000Z",
87
87
+
"totalUsers": 100,
88
88
+
"newUsers": 15
89
89
+
},
90
90
+
{
91
91
+
"date": "2026-03-12T00:00:00.000Z",
92
92
+
"totalUsers": 112,
93
93
+
"newUsers": 12
94
94
+
},
95
95
+
{
96
96
+
"date": "2026-03-13T00:00:00.000Z",
97
97
+
"totalUsers": 125,
98
98
+
"newUsers": 13
99
99
+
}
100
100
+
],
101
101
+
"currentTotal": 125,
102
102
+
"periodStart": "2026-03-11T00:00:00.000Z",
103
103
+
"periodEnd": "2026-03-13T00:00:00.000Z"
104
104
+
}
105
105
+
```
106
106
+
107
107
+
---
108
108
+
109
109
+
### 2. Engagement Statistics
110
110
+
111
111
+
**Description**: Track active vs inactive users with detailed content breakdown. Supports optional time series data.
112
112
+
113
113
+
**Note**: Only counts URL cards (excludes NOTE and HIGHLIGHT cards).
114
114
+
115
115
+
**Query Parameters**:
116
116
+
117
117
+
- `type=engagement` (required)
118
118
+
- `interval` (optional, default: `day`, used if `includeTimeSeries=true`)
119
119
+
- `limit` (optional, default: 30, used if `includeTimeSeries=true`)
120
120
+
- `includeTimeSeries` (optional, default: `false`)
121
121
+
122
122
+
**Request Example (Snapshot)**:
123
123
+
124
124
+
```bash
125
125
+
curl -X GET "http://localhost:3000/api/stats?type=engagement" \
126
126
+
-H "Authorization: Bearer YOUR_STATS_API_KEY"
127
127
+
```
128
128
+
129
129
+
**Request Example (With Time Series)**:
130
130
+
131
131
+
```bash
132
132
+
curl -X GET "http://localhost:3000/api/stats?type=engagement&includeTimeSeries=true&interval=week&limit=12" \
133
133
+
-H "Authorization: Bearer YOUR_STATS_API_KEY"
134
134
+
```
135
135
+
136
136
+
**Response Schema**:
137
137
+
138
138
+
```typescript
139
139
+
{
140
140
+
// Snapshot data (always included)
141
141
+
totalUsers: number; // Total registered users
142
142
+
activeUsers: number; // Users who created any content
143
143
+
inactiveUsers: number; // Users who signed in but created nothing
144
144
+
145
145
+
// Activity breakdown
146
146
+
usersWithCards: number; // Users who created URL cards
147
147
+
usersWithCollections: number; // Users who created collections
148
148
+
usersWithConnections: number; // Users who created connections
149
149
+
usersWithFollows: number; // Users who followed targets
150
150
+
usersWithContributions: number; // Users who added cards to others' collections
151
151
+
152
152
+
// Engagement metrics
153
153
+
activationRate: number; // activeUsers / totalUsers
154
154
+
avgActionsPerActiveUser: number; // Average actions per active user
155
155
+
156
156
+
// Optional time series data (only if includeTimeSeries=true)
157
157
+
dataPoints?: Array<{
158
158
+
date: string; // ISO date string
159
159
+
activeUsers: number; // Users activated in this period
160
160
+
newlyActivatedUsers: number; // Previously inactive users who became active
161
161
+
cumulativeActiveUsers: number; // Total active users up to this date
162
162
+
}>;
163
163
+
}
164
164
+
```
165
165
+
166
166
+
**Example Response (Snapshot)**:
167
167
+
168
168
+
```json
169
169
+
{
170
170
+
"totalUsers": 500,
171
171
+
"activeUsers": 320,
172
172
+
"inactiveUsers": 180,
173
173
+
"usersWithCards": 280,
174
174
+
"usersWithCollections": 150,
175
175
+
"usersWithConnections": 90,
176
176
+
"usersWithFollows": 200,
177
177
+
"usersWithContributions": 45,
178
178
+
"activationRate": 0.64,
179
179
+
"avgActionsPerActiveUser": 12.5
180
180
+
}
181
181
+
```
182
182
+
183
183
+
**Example Response (With Time Series)**:
184
184
+
185
185
+
```json
186
186
+
{
187
187
+
"totalUsers": 500,
188
188
+
"activeUsers": 320,
189
189
+
"inactiveUsers": 180,
190
190
+
"usersWithCards": 280,
191
191
+
"usersWithCollections": 150,
192
192
+
"usersWithConnections": 90,
193
193
+
"usersWithFollows": 200,
194
194
+
"usersWithContributions": 45,
195
195
+
"activationRate": 0.64,
196
196
+
"avgActionsPerActiveUser": 12.5,
197
197
+
"dataPoints": [
198
198
+
{
199
199
+
"date": "2026-03-01T00:00:00.000Z",
200
200
+
"activeUsers": 25,
201
201
+
"newlyActivatedUsers": 25,
202
202
+
"cumulativeActiveUsers": 275
203
203
+
},
204
204
+
{
205
205
+
"date": "2026-03-08T00:00:00.000Z",
206
206
+
"activeUsers": 30,
207
207
+
"newlyActivatedUsers": 30,
208
208
+
"cumulativeActiveUsers": 305
209
209
+
},
210
210
+
{
211
211
+
"date": "2026-03-15T00:00:00.000Z",
212
212
+
"activeUsers": 15,
213
213
+
"newlyActivatedUsers": 15,
214
214
+
"cumulativeActiveUsers": 320
215
215
+
}
216
216
+
]
217
217
+
}
218
218
+
```
219
219
+
220
220
+
---
221
221
+
222
222
+
### 3. Activity Statistics
223
223
+
224
224
+
**Description**: Track daily content creation volume across all content types.
225
225
+
226
226
+
**Note**: Only counts URL cards (excludes NOTE and HIGHLIGHT cards).
227
227
+
228
228
+
**Query Parameters**:
229
229
+
230
230
+
- `type=activity` (required)
231
231
+
- `interval` (optional, default: `day`)
232
232
+
- `limit` (optional, default: 30)
233
233
+
234
234
+
**Request Example**:
235
235
+
236
236
+
```bash
237
237
+
curl -X GET "http://localhost:3000/api/stats?type=activity&interval=day&limit=7" \
238
238
+
-H "Authorization: Bearer YOUR_STATS_API_KEY"
239
239
+
```
240
240
+
241
241
+
**Response Schema**:
242
242
+
243
243
+
```typescript
244
244
+
{
245
245
+
dataPoints: Array<{
246
246
+
date: string; // ISO date string
247
247
+
cardsCreated: number; // URL cards created in this period
248
248
+
collectionsCreated: number; // Collections created in this period
249
249
+
connectionsCreated: number; // Connections created in this period
250
250
+
followsCreated: number; // Follows created in this period
251
251
+
totalActions: number; // Sum of all above
252
252
+
}>;
253
253
+
totals: {
254
254
+
cardsCreated: number; // Total across all periods
255
255
+
collectionsCreated: number; // Total across all periods
256
256
+
connectionsCreated: number; // Total across all periods
257
257
+
followsCreated: number; // Total across all periods
258
258
+
totalActions: number; // Sum of all totals
259
259
+
}
260
260
+
periodStart: string; // ISO date string of first data point
261
261
+
periodEnd: string; // ISO date string of last data point
262
262
+
}
263
263
+
```
264
264
+
265
265
+
**Example Response**:
266
266
+
267
267
+
```json
268
268
+
{
269
269
+
"dataPoints": [
270
270
+
{
271
271
+
"date": "2026-04-08T00:00:00.000Z",
272
272
+
"cardsCreated": 45,
273
273
+
"collectionsCreated": 12,
274
274
+
"connectionsCreated": 8,
275
275
+
"followsCreated": 23,
276
276
+
"totalActions": 88
277
277
+
},
278
278
+
{
279
279
+
"date": "2026-04-09T00:00:00.000Z",
280
280
+
"cardsCreated": 52,
281
281
+
"collectionsCreated": 15,
282
282
+
"connectionsCreated": 10,
283
283
+
"followsCreated": 28,
284
284
+
"totalActions": 105
285
285
+
},
286
286
+
{
287
287
+
"date": "2026-04-10T00:00:00.000Z",
288
288
+
"cardsCreated": 38,
289
289
+
"collectionsCreated": 9,
290
290
+
"connectionsCreated": 6,
291
291
+
"followsCreated": 19,
292
292
+
"totalActions": 72
293
293
+
}
294
294
+
],
295
295
+
"totals": {
296
296
+
"cardsCreated": 135,
297
297
+
"collectionsCreated": 36,
298
298
+
"connectionsCreated": 24,
299
299
+
"followsCreated": 70,
300
300
+
"totalActions": 265
301
301
+
},
302
302
+
"periodStart": "2026-04-08T00:00:00.000Z",
303
303
+
"periodEnd": "2026-04-10T00:00:00.000Z"
304
304
+
}
305
305
+
```
306
306
+
307
307
+
---
308
308
+
309
309
+
### 4. Breakdown Statistics
310
310
+
311
311
+
**Description**: Track content distribution by subtypes over time. Shows cumulative growth of different content categories.
312
312
+
313
313
+
**Breakdowns**:
314
314
+
315
315
+
- URL cards by `urlType` (article, video, tool, etc.)
316
316
+
- Collections by `accessType` (OPEN, CLOSED)
317
317
+
- Connections by `connectionType` (SUPPORTS, OPPOSES, unspecified)
318
318
+
319
319
+
**Note**: Only counts URL cards (excludes NOTE and HIGHLIGHT cards).
320
320
+
321
321
+
**Query Parameters**:
322
322
+
323
323
+
- `type=breakdown` (required)
324
324
+
- `interval` (optional, default: `day`)
325
325
+
- `limit` (optional, default: 30)
326
326
+
327
327
+
**Request Example**:
328
328
+
329
329
+
```bash
330
330
+
curl -X GET "http://localhost:3000/api/stats?type=breakdown&interval=week&limit=4" \
331
331
+
-H "Authorization: Bearer YOUR_STATS_API_KEY"
332
332
+
```
333
333
+
334
334
+
**Response Schema**:
335
335
+
336
336
+
```typescript
337
337
+
{
338
338
+
dataPoints: Array<{
339
339
+
date: string; // ISO date string
340
340
+
urlCards: {
341
341
+
total: number; // Total URL cards at this date
342
342
+
byType: Record<string, number>; // Breakdown by urlType
343
343
+
};
344
344
+
collections: {
345
345
+
total: number; // Total collections at this date
346
346
+
byAccessType: Record<string, number>; // Breakdown by accessType
347
347
+
};
348
348
+
connections: {
349
349
+
total: number; // Total connections at this date
350
350
+
byType: Record<string, number>; // Breakdown by connectionType
351
351
+
};
352
352
+
}>;
353
353
+
currentTotals: {
354
354
+
urlCards: {
355
355
+
total: number; // Current total URL cards
356
356
+
byType: Record<string, number>; // Current breakdown by urlType
357
357
+
}
358
358
+
collections: {
359
359
+
total: number; // Current total collections
360
360
+
byAccessType: Record<string, number>; // Current breakdown by accessType
361
361
+
}
362
362
+
connections: {
363
363
+
total: number; // Current total connections
364
364
+
byType: Record<string, number>; // Current breakdown by connectionType
365
365
+
}
366
366
+
}
367
367
+
periodStart: string; // ISO date string of first data point
368
368
+
periodEnd: string; // ISO date string of last data point
369
369
+
}
370
370
+
```
371
371
+
372
372
+
**Example Response**:
373
373
+
374
374
+
```json
375
375
+
{
376
376
+
"dataPoints": [
377
377
+
{
378
378
+
"date": "2026-03-17T00:00:00.000Z",
379
379
+
"urlCards": {
380
380
+
"total": 450,
381
381
+
"byType": {
382
382
+
"article": 180,
383
383
+
"video": 120,
384
384
+
"tool": 90,
385
385
+
"unspecified": 60
386
386
+
}
387
387
+
},
388
388
+
"collections": {
389
389
+
"total": 85,
390
390
+
"byAccessType": {
391
391
+
"OPEN": 60,
392
392
+
"CLOSED": 25
393
393
+
}
394
394
+
},
395
395
+
"connections": {
396
396
+
"total": 120,
397
397
+
"byType": {
398
398
+
"SUPPORTS": 70,
399
399
+
"OPPOSES": 30,
400
400
+
"unspecified": 20
401
401
+
}
402
402
+
}
403
403
+
},
404
404
+
{
405
405
+
"date": "2026-03-24T00:00:00.000Z",
406
406
+
"urlCards": {
407
407
+
"total": 520,
408
408
+
"byType": {
409
409
+
"article": 210,
410
410
+
"video": 140,
411
411
+
"tool": 105,
412
412
+
"unspecified": 65
413
413
+
}
414
414
+
},
415
415
+
"collections": {
416
416
+
"total": 98,
417
417
+
"byAccessType": {
418
418
+
"OPEN": 72,
419
419
+
"CLOSED": 26
420
420
+
}
421
421
+
},
422
422
+
"connections": {
423
423
+
"total": 145,
424
424
+
"byType": {
425
425
+
"SUPPORTS": 85,
426
426
+
"OPPOSES": 38,
427
427
+
"unspecified": 22
428
428
+
}
429
429
+
}
430
430
+
}
431
431
+
],
432
432
+
"currentTotals": {
433
433
+
"urlCards": {
434
434
+
"total": 520,
435
435
+
"byType": {
436
436
+
"article": 210,
437
437
+
"video": 140,
438
438
+
"tool": 105,
439
439
+
"unspecified": 65
440
440
+
}
441
441
+
},
442
442
+
"collections": {
443
443
+
"total": 98,
444
444
+
"byAccessType": {
445
445
+
"OPEN": 72,
446
446
+
"CLOSED": 26
447
447
+
}
448
448
+
},
449
449
+
"connections": {
450
450
+
"total": 145,
451
451
+
"byType": {
452
452
+
"SUPPORTS": 85,
453
453
+
"OPPOSES": 38,
454
454
+
"unspecified": 22
455
455
+
}
456
456
+
}
457
457
+
},
458
458
+
"periodStart": "2026-03-17T00:00:00.000Z",
459
459
+
"periodEnd": "2026-03-24T00:00:00.000Z"
460
460
+
}
461
461
+
```
462
462
+
463
463
+
---
464
464
+
465
465
+
## Error Responses
466
466
+
467
467
+
All endpoints return standard error responses:
468
468
+
469
469
+
**Validation Error** (400):
470
470
+
471
471
+
```json
472
472
+
{
473
473
+
"error": "Stat type is required"
474
474
+
}
475
475
+
```
476
476
+
477
477
+
**Authentication Error** (401):
478
478
+
479
479
+
```json
480
480
+
{
481
481
+
"error": "Unauthorized"
482
482
+
}
483
483
+
```
484
484
+
485
485
+
**Server Error** (500):
486
486
+
487
487
+
```json
488
488
+
{
489
489
+
"error": "Failed to retrieve user stats: <error message>"
490
490
+
}
491
491
+
```
492
492
+
493
493
+
---
494
494
+
495
495
+
## Implementation Notes
496
496
+
497
497
+
### Card Counting
498
498
+
499
499
+
- **Important**: All card counts refer exclusively to URL cards
500
500
+
- NOTE cards and HIGHLIGHT cards are excluded from all statistics
501
501
+
- This applies to: engagement stats, activity stats, and breakdown stats
502
502
+
503
503
+
### Time Intervals
504
504
+
505
505
+
- `day`: Data aggregated by calendar day
506
506
+
- `week`: Data aggregated by calendar week (starts Monday)
507
507
+
- `month`: Data aggregated by calendar month
508
508
+
509
509
+
### Data Points Ordering
510
510
+
511
511
+
- All time series data is returned in **chronological order** (oldest to newest)
512
512
+
- This makes it easy to render charts and track trends over time
513
513
+
514
514
+
### Performance
515
515
+
516
516
+
- Queries are optimized with indexes on relevant columns
517
517
+
- Large date ranges (limit > 100) may take longer to compute
518
518
+
- Breakdown stats execute three queries in parallel for better performance
519
519
+
520
520
+
### Null Handling
521
521
+
522
522
+
- Null values in `urlType` and `connectionType` are mapped to `"unspecified"`
523
523
+
- This ensures consistent data structure in breakdown responses
524
524
+
525
525
+
---
526
526
+
527
527
+
## Common Use Cases
528
528
+
529
529
+
### Dashboard Overview
530
530
+
531
531
+
Get current engagement snapshot:
532
532
+
533
533
+
```bash
534
534
+
GET /api/stats?type=engagement
535
535
+
```
536
536
+
537
537
+
### Growth Chart
538
538
+
539
539
+
Get 90 days of user growth by week:
540
540
+
541
541
+
```bash
542
542
+
GET /api/stats?type=growth&interval=week&limit=13
543
543
+
```
544
544
+
545
545
+
### Activity Heatmap
546
546
+
547
547
+
Get daily activity for the past month:
548
548
+
549
549
+
```bash
550
550
+
GET /api/stats?type=activity&interval=day&limit=30
551
551
+
```
552
552
+
553
553
+
### Content Distribution Trends
554
554
+
555
555
+
Track content type growth over 6 months:
556
556
+
557
557
+
```bash
558
558
+
GET /api/stats?type=breakdown&interval=month&limit=6
559
559
+
```
560
560
+
561
561
+
### Activation Funnel
562
562
+
563
563
+
Get engagement with time series to track activation trends:
564
564
+
565
565
+
```bash
566
566
+
GET /api/stats?type=engagement&includeTimeSeries=true&interval=week&limit=12
567
567
+
```