npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rostatstrack

v1.0.2

Published

A Node.js package that tracks and logs ROBLOX game and group statistics over time

Readme

🎮 rostatstrack

npm version License: MIT Node.js Version npm downloads

A powerful Node.js package for tracking and logging ROBLOX game and group statistics over time.

rostatstrack automates the collection of critical ROBLOX metrics, storing them in organized CSV files with detailed logging. Perfect for game developers, analytics enthusiasts, and community managers who want to track their ROBLOX presence over time.


📊 What It Tracks

| Metric | Description | Use Case | |--------|-------------|----------| | Game Visits | Total number of visits to your game | Track overall popularity and growth | | Favorites | Number of users who favorited your game | Measure player engagement | | Active Players | Current concurrent players | Monitor real-time activity | | Group Members | Total members in your group | Track community growth | | Upvotes | Positive votes on your game | Gauge player satisfaction | | Downvotes | Negative votes on your game | Identify improvement areas |


✨ Features

🎯 Core Functionality

  • Automated Data Collection - Set it and forget it! Collects stats at configurable intervals
  • Dual Usage Modes - Use as a CLI tool or integrate into your Node.js applications
  • Smart CSV Organization - Daily CSV files with headers, easy to analyze
  • Comprehensive Logging - Detailed logs with timestamps and error tracking
  • Robust Error Handling - Automatic retries on API failures with exponential backoff

🛡️ Reliability Features

  • Rate Limit Respect - Built-in delays to comply with ROBLOX API limits
  • Graceful Shutdown - Handles SIGINT/SIGTERM for clean exits
  • Log Rotation - Automatic archiving of old log files
  • Config Validation - Validates settings before starting
  • Network Resilience - Retries on 429, 500, 502, 503, and 504 errors

📦 Developer Experience

  • TypeScript-Ready - Works seamlessly in TypeScript projects
  • Zero Configuration - Works out of the box with sensible defaults
  • Full API Access - Programmatic control over all functionality
  • Easy Integration - Simple, intuitive API design

📦 Installation

Prerequisites

  • Node.js >= 14.0.0
  • npm or yarn
  • A ROBLOX game universe ID and group ID

Quick Install

As a Global CLI Tool

Install globally to use the rostatstrack command anywhere on your system:

npm install -g rostatstrack

As a Project Dependency

Install locally in your project for programmatic use:

npm install rostatstrack

Or with Yarn:

yarn add rostatstrack

From Source (Development)

Clone the repository for development or customization:

git clone https://github.com/yourusername/roblox-stats-tracker.git
cd roblox-stats-tracker
npm install

🚀 Quick Start

1. Find Your IDs

Universe ID

  1. Go to ROBLOX Creator Dashboard
  2. Select your game
  3. The universe ID is in the URL: https://create.roblox.com/dashboard/creations/experiences/[universeId]/...

Group ID

  1. Navigate to your group page on ROBLOX
  2. The group ID is in the URL: https://www.roblox.com/groups/[groupId]/...

2. Create Configuration

Create a config.js file in your project directory:

const path = require("path");

module.exports = {
	// REQUIRED: Your ROBLOX game's universe ID
	universeId: 1931573465,
	
	// REQUIRED: Your ROBLOX group ID
	groupId: 2880815,
	
	// OPTIONAL: Interval between data collections (milliseconds)
	// Default: 300000 (5 minutes)
	// Minimum: 60000 (1 minute)
	saveInterval: 5 * 60 * 1000,
	
	// OPTIONAL: Directory to save CSV files
	// Default: './saves'
	savesDir: path.join(__dirname, "saves"),
	
	// OPTIONAL: Directory to save log files
	// Default: './logs'
	logDir: path.join(__dirname, "logs"),
	
	// OPTIONAL: Name of the current log file
	// Default: 'latest.log'
	logFileName: "latest.log"
};

3. Run the Tracker

rostatstrack

That's it! Your stats are now being tracked every 5 minutes.


💻 Usage

CLI Usage

Basic Usage

After creating your config.js file:

# If installed globally
rostatstrack

# If installed locally
npx rostatstrack

# Or using npm script
npm start

Running in Background (Linux/macOS)

For production environments, run the tracker in the background:

Using screen:

# Install screen (if not installed)
# Ubuntu/Debian:
sudo apt-get install screen
# CentOS/RHEL:
sudo yum install screen

# Start tracker in a screen session
screen -S rostatstrack
rostatstrack

# Detach from screen: Press Ctrl+A, then D

# Reattach to screen
screen -r rostatstrack

# List all screens
screen -ls

Using PM2 (Recommended):

# Install PM2 globally
npm install -g pm2

# Start tracker
pm2 start bin/cli.js --name "rostatstrack"

# View logs
pm2 logs rostatstrack

# Stop tracker
pm2 stop rostatstrack

# Restart tracker
pm2 restart rostatstrack

# Configure auto-restart on system reboot
pm2 startup
pm2 save

Running in Background (Windows)

Using Windows Service:

Install PM2 and create a Windows service:

npm install -g pm2
npm install -g pm2-windows-service

# Install as service
pm2-service-install

# Start service
pm2 start bin/cli.js --name "rostatstrack"
pm2 save

Library Usage

Basic Example

const RobloxStatsTracker = require('rostatstrack');

// Create tracker instance
const tracker = new RobloxStatsTracker({
	universeId: 1931573465,
	groupId: 2880815,
	saveInterval: 5 * 60 * 1000 // 5 minutes
});

// Start tracking
tracker.start()
	.then(() => {
		console.log('Tracker started successfully');
	})
	.catch(error => {
		console.error('Failed to start tracker:', error);
		process.exit(1);
	});

// Handle graceful shutdown
process.on('SIGINT', () => {
	console.log('Shutting down gracefully...');
	tracker.stop();
});

Advanced Example with Error Handling

const RobloxStatsTracker = require('rostatstrack');
const path = require('path');

// Configure tracker
const tracker = new RobloxStatsTracker({
	universeId: 1931573465,
	groupId: 2880815,
	saveInterval: 5 * 60 * 1000,
	savesDir: path.join(__dirname, 'data', 'stats'),
	logDir: path.join(__dirname, 'data', 'logs'),
	logFileName: 'tracker.log'
});

// Custom error handler
async function startTracker() {
	try {
		await tracker.start();
		console.log('✅ Tracker started successfully');
	} catch (error) {
		console.error('❌ Critical error:', error.message);
		
		// Send alert (email, Slack, etc.)
		await sendAlert(`Tracker failed: ${error.message}`);
		
		process.exit(1);
	}
}

// Graceful shutdown with cleanup
async function shutdown() {
	console.log('Received shutdown signal');
	tracker.stop();
	
	// Wait for current operation to complete
	await new Promise(resolve => setTimeout(resolve, 2000));
	
	console.log('Tracker stopped. Goodbye!');
	process.exit(0);
}

process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);

// Start the tracker
startTracker();

One-Time Stats Fetch

Fetch current stats without starting the continuous tracker:

const RobloxStatsTracker = require('rostatstrack');

const tracker = new RobloxStatsTracker({
	universeId: 1931573465,
	groupId: 2880815
});

async function getCurrentStats() {
	try {
		const stats = await tracker.fetchRobloxStats();
		console.log('Current Statistics:');
		console.log('─────────────────────────────────');
		console.log(`📅 Timestamp: ${stats.timestamp}`);
		console.log(`👁️  Visits: ${stats.visits?.toLocaleString() || 'N/A'}`);
		console.log(`⭐ Favorites: ${stats.favorites?.toLocaleString() || 'N/A'}`);
		console.log(`🎮 Active Players: ${stats.players?.toLocaleString() || 'N/A'}`);
		console.log(`👥 Group Members: ${stats.members?.toLocaleString() || 'N/A'}`);
		console.log(`👍 Upvotes: ${stats.upVotes?.toLocaleString() || 'N/A'}`);
		console.log(`👎 Downvotes: ${stats.downVotes?.toLocaleString() || 'N/A'}`);
		
		return stats;
	} catch (error) {
		console.error('Failed to fetch stats:', error.message);
		throw error;
	}
}

getCurrentStats();

Integration with Express.js API

const express = require('express');
const RobloxStatsTracker = require('rostatstrack');

const app = express();
const tracker = new RobloxStatsTracker({
	universeId: 1931573465,
	groupId: 2880815
});

// Endpoint to get current stats
app.get('/api/stats/current', async (req, res) => {
	try {
		const stats = await tracker.fetchRobloxStats();
		res.json({
			success: true,
			data: stats
		});
	} catch (error) {
		res.status(500).json({
			success: false,
			error: error.message
		});
	}
});

// Endpoint to start tracker
app.post('/api/tracker/start', async (req, res) => {
	try {
		if (tracker.isRunning) {
			return res.status(400).json({
				success: false,
				error: 'Tracker is already running'
			});
		}
		
		tracker.start();
		res.json({
			success: true,
			message: 'Tracker started successfully'
		});
	} catch (error) {
		res.status(500).json({
			success: false,
			error: error.message
		});
	}
});

// Endpoint to stop tracker
app.post('/api/tracker/stop', (req, res) => {
	tracker.stop();
	res.json({
		success: true,
		message: 'Tracker stopped'
	});
});

app.listen(3000, () => {
	console.log('API running on http://localhost:3000');
});

📖 API Reference

Class: RobloxStatsTracker

Main class for tracking ROBLOX statistics.

Constructor

new RobloxStatsTracker(config)

Parameters:

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | config | Object | Yes | - | Configuration object | | config.universeId | Number | Yes | - | ROBLOX game universe ID | | config.groupId | Number | Yes | - | ROBLOX group ID | | config.saveInterval | Number | No | 300000 | Interval between saves (ms), min: 60000 | | config.savesDir | String | No | './saves' | Directory for CSV files | | config.logDir | String | No | './logs' | Directory for log files | | config.logFileName | String | No | 'latest.log' | Name of current log file |

Throws:

  • Error - If configuration validation fails

Example:

const tracker = new RobloxStatsTracker({
	universeId: 1931573465,
	groupId: 2880815,
	saveInterval: 10 * 60 * 1000 // 10 minutes
});

Method: start()

Starts the continuous stats tracking loop.

tracker.start() → Promise<void>

Returns: Promise<void> - Resolves when tracker stops

Throws:

  • Error - If tracker is already running
  • Error - On critical failures (network, filesystem, etc.)

Behavior:

  1. Creates necessary directories
  2. Initializes log file (archives existing one)
  3. Fetches initial stats immediately
  4. Waits until next interval mark
  5. Continues fetching at configured intervals

Example:

try {
	await tracker.start();
} catch (error) {
	console.error('Tracker failed:', error);
}

Method: stop()

Requests the tracker to stop gracefully.

tracker.stop() → void

Returns: void

Behavior:

  • Sets stop flag
  • Allows current iteration to complete
  • Exits loop cleanly

Example:

process.on('SIGINT', () => {
	console.log('Stopping tracker...');
	tracker.stop();
});

Method: fetchRobloxStats()

Fetches current statistics from ROBLOX APIs.

tracker.fetchRobloxStats() → Promise<Stats>

Returns: Promise<Stats> - Statistics object

Stats Object:

{
	timestamp: string;     // ISO 8601 timestamp
	visits: number | null; // Total game visits
	favorites: number | null; // Total favorites
	players: number | null;   // Current active players
	members: number | null;   // Group member count
	upVotes: number | null;   // Total upvotes
	downVotes: number | null; // Total downvotes
}

Note: Values are null if the API call fails

Example:

const stats = await tracker.fetchRobloxStats();
console.log(`Current players: ${stats.players}`);

Method: saveDataToCSV(data)

Saves statistics to a CSV file.

tracker.saveDataToCSV(data) → Promise<void>

Parameters:

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | data | Stats | Yes | Statistics object to save |

Returns: Promise<void>

Behavior:

  • Creates daily CSV files (format: YYYY-MM-DD.csv)
  • Adds headers if file is new
  • Appends data as new row

Example:

const stats = await tracker.fetchRobloxStats();
await tracker.saveDataToCSV(stats);

Property: isRunning

Indicates if the tracker is currently running.

tracker.isRunning → boolean

Type: boolean

Read-only: Yes

Example:

if (tracker.isRunning) {
	console.log('Tracker is active');
}

📁 Data Format

CSV Structure

Daily CSV files are created in the saves directory with this structure:

Timestamp,Visits,Favorites,Players,Members,UpVotes,DownVotes
2024-03-20T12:00:00.000Z,1000000,50000,150,5000,75000,2500
2024-03-20T12:05:00.000Z,1000025,50001,148,5001,75005,2501
2024-03-20T12:10:00.000Z,1000051,50003,152,5001,75012,2502

File Organization

project/
├── saves/
│   ├── 2024-03-20.csv
│   ├── 2024-03-21.csv
│   └── 2024-03-22.csv
└── logs/
    ├── latest.log
    ├── 2024-03-19T14-23-45-123Z.log
    └── 2024-03-20T08-15-30-456Z.log

Log Format

2024-03-20T12:00:00.123Z - ROBLOX Stats Tracker started
2024-03-20T12:00:00.456Z - game API call successful. Status: 200
2024-03-20T12:00:00.789Z - group API call successful. Status: 200
2024-03-20T12:00:01.012Z - votes API call successful. Status: 200
2024-03-20T12:00:01.234Z - Data saved successfully to 2024-03-20.csv

🔍 Data Analysis

Using CSViewer (Recommended)

CSViewer is a fast, lightweight CSV viewer perfect for analyzing your data:

  1. Download from csviewer.com
  2. Open your daily CSV files
  3. Use filtering and sorting features
  4. Export charts and visualizations

Why CSViewer?

  • ✅ Free for commercial use
  • ✅ Handles large files efficiently
  • ✅ Built-in charting capabilities
  • ✅ No installation required (portable)

Using Excel/Google Sheets

Import CSV files to create custom charts and pivot tables:

Excel:

  1. Open Excel
  2. File → Import → CSV file
  3. Select your CSV file
  4. Create charts from the Insert tab

Google Sheets:

  1. Open Google Sheets
  2. File → Import → Upload
  3. Select your CSV file
  4. Use built-in charting tools

Using Python/Pandas

import pandas as pd
import matplotlib.pyplot as plt

# Load data
df = pd.read_csv('saves/2024-03-20.csv')
df['Timestamp'] = pd.to_datetime(df['Timestamp'])

# Plot visits over time
plt.figure(figsize=(12, 6))
plt.plot(df['Timestamp'], df['Visits'])
plt.title('Game Visits Over Time')
plt.xlabel('Time')
plt.ylabel('Total Visits')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

Using R

library(ggplot2)
library(readr)

# Load data
data <- read_csv('saves/2024-03-20.csv')
data$Timestamp <- as.POSIXct(data$Timestamp)

# Plot active players
ggplot(data, aes(x=Timestamp, y=Players)) +
  geom_line(color='blue') +
  labs(title='Active Players Over Time',
       x='Time', y='Players') +
  theme_minimal()

⚙️ Configuration Guide

Minimal Configuration

module.exports = {
	universeId: 1931573465,
	groupId: 2880815
};

Production Configuration

const path = require("path");
const os = require("os");

module.exports = {
	// ROBLOX IDs
	universeId: parseInt(process.env.ROBLOX_UNIVERSE_ID) || 1931573465,
	groupId: parseInt(process.env.ROBLOX_GROUP_ID) || 2880815,
	
	// Collection interval: 10 minutes
	saveInterval: 10 * 60 * 1000,
	
	// Use dedicated data directory
	savesDir: path.join(__dirname, 'data', 'csv'),
	logDir: path.join(__dirname, 'data', 'logs'),
	
	// Custom log file name
	logFileName: `tracker-${os.hostname()}.log`
};

Environment Variables

Create a .env file:

ROBLOX_UNIVERSE_ID=1931573465
ROBLOX_GROUP_ID=2880815
SAVE_INTERVAL=300000
DATA_DIR=/var/data/rostatstrack

Load in config.js:

require('dotenv').config();
const path = require('path');

const dataDir = process.env.DATA_DIR || __dirname;

module.exports = {
	universeId: parseInt(process.env.ROBLOX_UNIVERSE_ID),
	groupId: parseInt(process.env.ROBLOX_GROUP_ID),
	saveInterval: parseInt(process.env.SAVE_INTERVAL) || 300000,
	savesDir: path.join(dataDir, 'saves'),
	logDir: path.join(dataDir, 'logs'),
	logFileName: 'latest.log'
};

Interval Guidelines

| Interval | Use Case | Daily Data Points | |----------|----------|-------------------| | 1 minute | High-frequency monitoring | 1,440 | | 5 minutes | Standard tracking (default) | 288 | | 10 minutes | Low-frequency tracking | 144 | | 15 minutes | Minimal resource usage | 96 | | 30 minutes | Very light tracking | 48 | | 1 hour | Historical trends only | 24 |

Recommendation: 5-10 minutes balances detail with API respect


🐛 Troubleshooting

Common Issues

"Invalid configuration" Error

Problem: Config validation failed

Solution:

// Ensure universeId and groupId are positive integers
module.exports = {
	universeId: 1931573465,  // ✅ Number, not string
	groupId: 2880815,        // ✅ Number, not string
	saveInterval: 300000     // ✅ At least 60000 (1 minute)
};

"Tracker is already running" Error

Problem: Called start() twice

Solution:

if (!tracker.isRunning) {
	await tracker.start();
}

API Call Failures

Problem: ROBLOX API returning errors

Symptoms in logs:

Error in game API call: Request failed with status code 429

Solutions:

  1. Rate Limited (429): Increase saveInterval to 10+ minutes
  2. Server Errors (500-504): Tracker auto-retries, wait for recovery
  3. Invalid IDs (400): Verify your universe ID and group ID are correct

No Data Being Saved

Problem: CSV files not created or empty

Checks:

  1. Check log file for errors: cat logs/latest.log
  2. Verify write permissions: ls -la saves/
  3. Check disk space: df -h
  4. Confirm API calls succeed in logs

Process Crashes

Problem: Tracker stops unexpectedly

Solutions:

  1. Use PM2 for auto-restart:
pm2 start bin/cli.js --name rostatstrack --max-restarts 10
  1. Check system resources:
free -m  # Check memory
df -h    # Check disk space
  1. Review logs:
tail -f logs/latest.log

Debug Mode

Enable verbose logging:

const tracker = new RobloxStatsTracker({ /* config */ });

// Add custom logging
const originalLog = tracker.log.bind(tracker);
tracker.log = async function(message) {
	console.log('[DEBUG]', message);
	await originalLog(message);
};

await tracker.start();

❓ FAQ

General Questions

Q: Is this allowed by ROBLOX Terms of Service?
A: Yes! This uses public ROBLOX APIs that are documented and intended for developer use. We respect rate limits and follow best practices.

Q: How much data will this generate?
A: At 5-minute intervals, approximately:

  • ~1 KB per data point
  • ~300 KB per day
  • ~9 MB per month
  • ~110 MB per year

Q: Can I track multiple games?
A: Yes! Create separate tracker instances:

const game1 = new RobloxStatsTracker({
	universeId: 111111,
	groupId: 222222,
	savesDir: './saves/game1'
});

const game2 = new RobloxStatsTracker({
	universeId: 333333,
	groupId: 444444,
	savesDir: './saves/game2'
});

await Promise.all([
	game1.start(),
	game2.start()
]);

Q: Does this work with private/group-only games?
A: Yes! The ROBLOX APIs used are public and work for all game types.

Technical Questions

Q: Does this use webhooks or polling?
A: Polling. It fetches data at regular intervals from ROBLOX's public APIs.

Q: What happens if my server restarts?
A: The tracker will need to be restarted. Use PM2 or systemd for auto-restart.

Q: Can I change the CSV format?
A: Yes! Extend the class and override saveDataToCSV():

class CustomTracker extends RobloxStatsTracker {
	async saveDataToCSV(data) {
		// Your custom CSV logic
	}
}

Q: Is there a way to send alerts for certain thresholds?
A: Yes! Implement custom logic:

const stats = await tracker.fetchRobloxStats();

if (stats.players > 1000) {
	await sendSlackAlert('🎉 Over 1000 concurrent players!');
}

Q: Can I export to a database instead of CSV?
A: Yes! Override or extend the save method:

class DatabaseTracker extends RobloxStatsTracker {
	async saveDataToCSV(data) {
		await super.saveDataToCSV(data); // Keep CSV
		await this.saveToDatabase(data);  // Also save to DB
	}
	
	async saveToDatabase(data) {
		await db.query(
			'INSERT INTO stats (timestamp, visits, favorites, ...) VALUES (?, ?, ?, ...)',
			[data.timestamp, data.visits, data.favorites, ...]
		);
	}
}

🛠️ Development

Building from Source

git clone https://github.com/yourusername/roblox-stats-tracker.git
cd roblox-stats-tracker
npm install

Project Structure

rostatstrack/
├── bin/
│   └── cli.js           # CLI entry point
├── utils/
│   ├── apiHandler.js    # API retry logic
│   └── validation.js    # Config validation
├── index.js             # Main library export
├── main.js              # Legacy standalone script
├── config.js            # Example configuration
├── package.json         # Package metadata
└── README.md            # This file

Running Tests

npm test

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Quick Contribution Guide:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit changes: git commit -m 'Add amazing feature'
  4. Push to branch: git push origin feature/amazing-feature
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

TL;DR: You can use this package for any purpose, including commercial projects, as long as you include the license notice.


🙏 Acknowledgments

  • ROBLOX for providing public APIs
  • axios team for the excellent HTTP client
  • All contributors and users of this package

📬 Support


🔗 Links


Made with ❤️ for the ROBLOX developer community

⭐ Star us on GitHub if this project helped you!