Statistics

Statistics #

jfa-go exposes a few API endpoints which you can use, along with your own code and means of storage and display, to track and visualize whatever’s important to you. As some examples:

  • Tracking the total number of users, number created through jfa-go or number created per day/week/month
  • Monitoring how many users have accounts close to expiring, or how many accounts reach expiry before intervention (i.e. renewal)

Endpoints #

The API endpoints deemed useful for these sorts of things are tagged “Statistics” on api.jfa-go.com (or on your own instances with the -swagger flag). As of 2025-11-23, these are available:

  • /activity[/count] [GET/POST]: Probably the most useful, gets activities (or the number) of them matching any filters you provide. Most relevant things done by jfa-go are tracked in the activity log, so you can get a lot of information out of this.
  • /invites[/count[/used]] [GET]: Get all stored invites, the number of them or the number of them that have been used at least once.
  • /users[/count] [GET/POST]: Gets users, or the number of them, matching your filters or search terms.

Filtering #

The two filterable types of data, activities and users, are filtered the same way as in the web UI. Open up your browser’s dev tools, go to the network tab, then create your search in the Accounts or Activity tab and press the search button to force a server-side search. A request to the relevant endpoint will then be created, from which you can observe the query format.

In this example, we’re looking for any users created today. We’ll do this by filtering for account creation activities which took place between yesterday at 23:59 and today at 23:59. Since the date parser on the web page is quite flexible, our search ends up as time:"<today 23:59" time:">yesterday 23:59" account-creation:true. Our filter request is sent off as so:

{
  "searchTerms": [],
  "queries": [
    {
      "field": "time",
      "operator": "<",
      "class": "date",
      "value": {
        "year": 2025,
        "month": 10,
        "day": 23,
        "hour": 23,
        "minute": 59,
        "offsetMinutesFromUTC": 0
      }
    },
    {
      "field": "time",
      "operator": ">",
      "class": "date",
      "value": {
        "year": 2025,
        "month": 10,
        "day": 22,
        "hour": 23,
        "minute": 59,
        "offsetMinutesFromUTC": 0
      }
    },
    {
      "field": "accountCreation",
      "operator": "=",
      "class": "bool",
      "value": true
    }
  ],
  "limit": 20,
  "page": 0,
  "sortByField": "time",
  "ascending": false
}

Filtering example

Script example (Python) #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# Example script to get the number of users created today.
import requests
from requests.auth import HTTPBasicAuth
JFA_GO_HOST="http://localhost:8056"
JFA_GO_USERNAME="stats"
JFA_GO_PASSWORD="aSecureStatsAccountPassword"
JFA_GO_TOKEN = ""
JFA_GO_REFRESH_TOKEN = ""

# Logs in with username and password, and sets JFA_GO_TOKEN, which lasts 20 minutes and JFA_GO_REFRESH_TOKEN, which lasts 1 day.
def firstLogin():
    global JFA_GO_TOKEN, JFA_GO_REFRESH_TOKEN
    basic = HTTPBasicAuth(JFA_GO_USERNAME, JFA_GO_PASSWORD)
    resp = requests.get(JFA_GO_HOST+"/token/login", auth=basic)
    JFA_GO_TOKEN = resp.json()["token"]
    JFA_GO_REFRESH_TOKEN = resp.cookies.get("refresh")
    # print(f"Logged in with user/pass, got token {JFA_GO_TOKEN}, refresh {JFA_GO_REFRESH_TOKEN}")

# Logs in with JFA_GO_REFRESH_TOKEN, and sets a new JFA_GO_TOKEN and JFA_GO_REFRESH_TOKEN.
def loginWithRefresh():
    global JFA_GO_TOKEN, JFA_GO_REFRESH_TOKEN
    resp = requests.get(JFA_GO_HOST+"/token/refresh", cookies={"refresh": JFA_GO_REFRESH_TOKEN})
    JFA_GO_TOKEN = resp.json()["token"]
    JFA_GO_REFRESH_TOKEN = resp.cookies.get("refresh")
    # print(f"Logged in with refresh cookie, got token {JFA_GO_TOKEN}, refresh {JFA_GO_REFRESH_TOKEN}")

def countAccountsMadeToday():
    data = {
      "searchTerms": [],
      "queries": [
        {
          "field": "time",
          "operator": "<",
          "class": "date",
          "value": {
            "year": 2025,
            "month": 10,
            "day": 23,
            "hour": 23,
            "minute": 59,
            "offsetMinutesFromUTC": 0
          }
        },
        {
          "field": "time",
          "operator": ">",
          "class": "date",
          "value": {
            "year": 2025,
            "month": 10,
            "day": 22,
            "hour": 23,
            "minute": 59,
            "offsetMinutesFromUTC": 0
          }
        },
        {
          "field": "accountCreation",
          "operator": "=",
          "class": "bool",
          "value": True
        }
      ],
      "limit": 20,
      "page": 0,
      "sortByField": "time",
      "ascending": False
    }

    resp = requests.post(JFA_GO_HOST+"/activity/count", json=data, headers={"Authorization": "Bearer "+JFA_GO_TOKEN})
    if resp.status_code != 200:
        # Re-log (maybe our token has expired?)
        loginWithRefresh()
        resp = requests.post(JFA_GO_HOST+"/activity/count", json=data, headers={"Authorization": "Bearer "+JFA_GO_TOKEN})
    if resp.status_code == 200:
        print("Got count matching:", resp.json()["count"])
    else:
        print("Failed:", resp)

if __name__ == "__main__":
    firstLogin()
    countAccountsMadeToday()