API Documentation

Integrate Timekeeper into your applications with our tiered REST API. Choose the tier that fits your needs.

⚠️ API Access Required

API access requires generating an API key. Contact support for tier upgrades beyond Supporter.

Overview

The Timekeeper API provides programmatic access to time tracking data, analytics, and server management features. All endpoints use RESTful conventions and return JSON responses.

Base URL: https://api.timekeeper.404connernotfound.dev/api/v1

API Tiers

Tier Rate Limit Permissions Price
Supporter 20 req/min Read-only basic data $1/month or $10/year
Premium 60 req/min Full read + admin write $2.50/month or $25/year
Enterprise 120 req/min Full access + sub-keys $5/month or $50/year
Admin Unlimited Unrestricted Internal only

Authentication

Generating an API Key

API keys are generated server-side. Contact an administrator or use the following endpoint with master key:

POST /api/v1/auth/generate
Content-Type: application/json

{
  "guild_id": 123456789,
  "admin_user_id": 987654321,
  "master_key": "YOUR_MASTER_KEY",
  "tier": "PREMIUM"
}

Using Your API Key

Include your API key in the X-API-Key header with every request:

GET /api/v1/guild/123456789/status
X-API-Key: tk_your_api_key_here

Rate Limiting

Rate limits are enforced per minute. Response headers indicate your usage:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 28
X-API-Tier: PREMIUM

Core Endpoints

Get Guild Status

GET /api/v1/guild/{guild_id}/status

Tier Required: Supporter+

GET /api/v1/guild/123456789/status
X-API-Key: tk_your_key

Response:
{
  "guild_id": 123456789,
  "total_time_seconds": 3600000,
  "total_time_hours": 1000.0,
  "total_users": 42,
  "active_sessions": 5,
  "categories": {
    "Development": 1800000,
    "Meetings": 900000
  }
}

Clock In User

POST /api/v1/guild/{guild_id}/clockin

Tier Required: Premium+ (admin users only), Enterprise+ (all users)

POST /api/v1/guild/123456789/clockin
X-API-Key: tk_your_key
Content-Type: application/json

{
  "user_id": 987654321,
  "category": "Development",
  "description": "Working on API integration"
}

Response:
{
  "success": true,
  "session_id": "api_abc123def456",
  "category": "Development",
  "start_time": "2025-10-10T14:30:00Z",
  "user_id": 987654321
}

List Categories

GET /api/v1/guild/{guild_id}/categories

Tier Required: Supporter+

Get Leaderboard

GET /api/v1/guild/{guild_id}/leaderboard

Tier Required: Supporter+

Code Examples

Choose your preferred programming language and see how to integrate Timekeeper:

timekeeper_api.py
import requests

class TimekeeperAPI:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.timekeeper.404connernotfound.dev"
        self.session = requests.Session()
        self.session.headers.update({"X-API-Key": api_key})
    
    def get_guild_status(self, guild_id: int):
        """Get guild status and statistics"""
        response = self.session.get(
            f"{self.base_url}/api/v1/guild/{guild_id}/status"
        )
        response.raise_for_status()
        return response.json()
    
    def clock_in(self, guild_id: int, user_id: int, 
                 category: str, description: str = None):
        """Clock in a user"""
        data = {"user_id": user_id, "category": category}
        if description:
            data["description"] = description
        
        response = self.session.post(
            f"{self.base_url}/api/v1/guild/{guild_id}/clockin",
            json=data
        )
        response.raise_for_status()
        return response.json()
    
    def get_leaderboard(self, guild_id: int, category: str = None, 
                        timeframe: str = "all"):
        """Get guild leaderboard"""
        params = {"timeframe": timeframe}
        if category:
            params["category"] = category
        
        response = self.session.get(
            f"{self.base_url}/api/v1/guild/{guild_id}/leaderboard",
            params=params
        )
        response.raise_for_status()
        return response.json()

# Usage
api = TimekeeperAPI("tk_your_api_key_here")

# Get guild status
status = api.get_guild_status(123456789)
print(f"Total hours: {status['total_time_hours']}")

# Clock in a user
result = api.clock_in(
    guild_id=123456789,
    user_id=987654321,
    category="Development",
    description="API integration"
)
print(f"Session started: {result['session_id']}")

# Get leaderboard
leaderboard = api.get_leaderboard(123456789, timeframe="week")
for entry in leaderboard['leaderboard'][:5]:
    print(f"{entry['rank']}. {entry['username']}: {entry['hours']}h")
timekeeperAPI.js
const axios = require('axios');

class TimekeeperAPI {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseURL = 'https://api.timekeeper.404connernotfound.dev';
    this.client = axios.create({
      baseURL: this.baseURL,
      headers: {
        'X-API-Key': apiKey,
        'Content-Type': 'application/json'
      }
    });
  }

  async getGuildStatus(guildId) {
    const response = await this.client.get(
      `/api/v1/guild/${guildId}/status`
    );
    return response.data;
  }

  async clockIn(guildId, userId, category, description = null) {
    const data = { user_id: userId, category };
    if (description) data.description = description;
    
    const response = await this.client.post(
      `/api/v1/guild/${guildId}/clockin`,
      data
    );
    return response.data;
  }

  async getLeaderboard(guildId, category = null, timeframe = 'all') {
    const params = { timeframe };
    if (category) params.category = category;
    
    const response = await this.client.get(
      `/api/v1/guild/${guildId}/leaderboard`,
      { params }
    );
    return response.data;
  }
}

// Usage
(async () => {
  const api = new TimekeeperAPI('tk_your_api_key_here');

  // Get guild status
  const status = await api.getGuildStatus(123456789);
  console.log(`Total hours: ${status.total_time_hours}`);

  // Clock in a user
  const result = await api.clockIn(
    123456789,
    987654321,
    'Development',
    'API integration'
  );
  console.log(`Session started: ${result.session_id}`);

  // Get leaderboard
  const leaderboard = await api.getLeaderboard(123456789, null, 'week');
  leaderboard.leaderboard.slice(0, 5).forEach(entry => {
    console.log(`${entry.rank}. ${entry.username}: ${entry.hours}h`);
  });
})();
timekeeperAPI.ts
import axios, { AxiosInstance } from 'axios';

interface GuildStatus {
  guild_id: number;
  total_time_seconds: number;
  total_time_hours: number;
  total_users: number;
  active_sessions: number;
  categories: Record;
}

interface ClockInResponse {
  success: boolean;
  session_id: string;
  category: string;
  start_time: string;
  user_id: number;
}

class TimekeeperAPI {
  private client: AxiosInstance;

  constructor(private apiKey: string) {
    this.client = axios.create({
      baseURL: 'https://api.timekeeper.404connernotfound.dev',
      headers: {
        'X-API-Key': apiKey,
        'Content-Type': 'application/json'
      }
    });
  }

  async getGuildStatus(guildId: number): Promise {
    const response = await this.client.get(
      `/api/v1/guild/${guildId}/status`
    );
    return response.data;
  }

  async clockIn(
    guildId: number,
    userId: number,
    category: string,
    description?: string
  ): Promise {
    const data: any = { user_id: userId, category };
    if (description) data.description = description;
    
    const response = await this.client.post(
      `/api/v1/guild/${guildId}/clockin`,
      data
    );
    return response.data;
  }
}

// Usage
(async () => {
  const api = new TimekeeperAPI('tk_your_api_key_here');

  const status = await api.getGuildStatus(123456789);
  console.log(`Total hours: ${status.total_time_hours}`);

  const result = await api.clockIn(
    123456789,
    987654321,
    'Development',
    'API integration'
  );
  console.log(`Session started: ${result.session_id}`);
})();
main.rs
use reqwest;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Deserialize)]
struct GuildStatus {
    guild_id: u64,
    total_time_seconds: u64,
    total_time_hours: f64,
    total_users: u32,
    active_sessions: u32,
    categories: HashMap,
}

#[derive(Debug, Serialize)]
struct ClockInRequest {
    user_id: u64,
    category: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    description: Option,
}

struct TimekeeperAPI {
    api_key: String,
    client: reqwest::Client,
}

impl TimekeeperAPI {
    fn new(api_key: String) -> Self {
        Self {
            api_key,
            client: reqwest::Client::new(),
        }
    }

    async fn get_guild_status(
        &self,
        guild_id: u64,
    ) -> Result {
        let url = format!(
            "https://api.timekeeper.404connernotfound.dev/api/v1/guild/{}/status",
            guild_id
        );
        
        self.client
            .get(&url)
            .header("X-API-Key", &self.api_key)
            .send()
            .await?
            .json::()
            .await
    }

    async fn clock_in(
        &self,
        guild_id: u64,
        user_id: u64,
        category: String,
        description: Option,
    ) -> Result<(), reqwest::Error> {
        let url = format!(
            "https://api.timekeeper.404connernotfound.dev/api/v1/guild/{}/clockin",
            guild_id
        );
        let data = ClockInRequest {
            user_id,
            category,
            description,
        };
        
        self.client
            .post(&url)
            .header("X-API-Key", &self.api_key)
            .json(&data)
            .send()
            .await?;
        
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<(), Box> {
    let api = TimekeeperAPI::new("tk_your_api_key_here".to_string());

    let status = api.get_guild_status(123456789).await?;
    println!("Total hours: {}", status.total_time_hours);

    api.clock_in(
        123456789,
        987654321,
        "Development".to_string(),
        Some("API integration".to_string()),
    ).await?;

    Ok(())
}

Error Handling

All API errors follow a consistent format:

{
  "error": "Rate limit exceeded",
  "message": "Limit: 60 requests/minute",
  "remaining": 0,
  "reset_in": 42,
  "tier": "PREMIUM",
  "code": "RATE_001"
}