Who’s the most followed user on Codeberg?
Hope it isn’t some gambling site advertiser...
Published: Feb 25, 2025

Yeah, I just randomly wondered about this earlier today. Codeberg doesn’t have an option to sort users by anything, but it’s simple to do it manually anyway, so I decided to find the answer to this question right away.

You can search for users by using the Forgejo’s API, the endpoint for the “users search” is /api/user, which fortunately doesn’t require authentication to be used, and the data also contains the followers count, which simplifies the process of collecting data a lot. There seems to be no rate limits either.

The only problem is that Codeberg has a relatively large amount of users (152091, at the time of writing), in which most of them could be (unfortunately) SEO bots or spam users (IDK but there are a lot of Vietnamese sports betting/gambling sites advertisements on Codeberg). I can’t think of a good way to filter them out, so there’s no other way but to include all of them, and accept that it’ll take a decent amount of time to fully iterate through all users.

The code

It’s just very simple. We iterate through all pages, parse the data of each user into a CSV file, and then do stuff with that file.

The following Go script parses all available users into a CSV file. I wrote this in a rush so it’s not very efficient, but it does its job.

package main

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

type UserSearch struct {
	Data []struct {
		ID                int       `json:"id"`
		Login             string    `json:"login"`
		LoginName         string    `json:"login_name"`
		SourceID          int       `json:"source_id"`
		FullName          string    `json:"full_name"`
		Email             string    `json:"email"`
		AvatarURL         string    `json:"avatar_url"`
		HTMLURL           string    `json:"html_url"`
		Language          string    `json:"language"`
		IsAdmin           bool      `json:"is_admin"`
		LastLogin         time.Time `json:"last_login"`
		Created           time.Time `json:"created"`
		Restricted        bool      `json:"restricted"`
		Active            bool      `json:"active"`
		ProhibitLogin     bool      `json:"prohibit_login"`
		Location          string    `json:"location"`
		Pronouns          string    `json:"pronouns"`
		Website           string    `json:"website"`
		Description       string    `json:"description"`
		Visibility        string    `json:"visibility"`
		FollowersCount    int       `json:"followers_count"`
		FollowingCount    int       `json:"following_count"`
		StarredReposCount int       `json:"starred_repos_count"`
		Username          string    `json:"username"`
	} `json:"data"`
	Ok bool `json:"ok"`
}

func main() {
	user_search_template := "https://codeberg.org/api/v1/users/search?limit=10&page=%d"

	data_file, err := os.Create("data.csv")
	if err != nil {
		panic(err)
	}
	defer data_file.Close()

	data_file_writer := csv.NewWriter(data_file)
	defer data_file_writer.Flush()

	for p := 1 ;; p++ {
		user_search_uri := fmt.Sprintf(user_search_template, p)
		req, err := http.NewRequest(http.MethodGet, user_search_uri, nil)

		if err != nil {
			log.Fatalln("Something failed.")
		}
		req.Header.Add("accept", "application/json")

		res, err := http.DefaultClient.Do(req)

		if err != nil {
			log.Fatalln("Something failed.")
		}

		res_body, err := io.ReadAll(res.Body)
		if err != nil {
			log.Fatalln("Something failed.")
		}

		var users_data UserSearch
		err = json.Unmarshal(res_body, &users_data)
		if err != nil {
			log.Fatalln("Something failed.")
		}

		if !users_data.Ok {
			log.Fatalln("Not OK.")
		}

        if len(users_data.Data) < 1 {
            break
        }

		for _, user := range users_data.Data {
			log.Printf("Parsing user %s", user.Username)

			source_template := "%d|%s|%s|%d|%d|%d|%d"
			source := fmt.Sprintf(source_template, user.ID,
                                  user.Login, user.LoginName, user.SourceID,
                                  user.FollowersCount, user.FollowingCount, user.StarredReposCount)

			source2 := strings.Split(source, "|") // Maybe this is more reliable...
			err = data_file_writer.Write(source2)
			if err != nil {
				log.Fatalln("Something failed.")
			}
		}
	}
}

The script took over 6 hours to finish for me. After finishing, we should have a data.csv in your working directory with the data of all users on Codeberg.

33341,_,,0,0,0,6
71406,___henil,,0,0,0,0
92767,__BlackJack,,0,0,0,0
84906,__d,,0,0,0,0
57448,__rod,,0,0,0,0
57129,_-Caleb-_,,0,1,0,1
185,_1,,0,0,0,0
29308,_456789,,0,0,0,0
39689,_a,,0,0,0,0
90652,_arabica,,0,0,0,0
...

The CSV format for each user is id,login,login_name,source_id,followers_count,following_count,starred_repos_count

The result

So we have the data now. The last thing to do is to now sort the file by their followers count. We can easily do that by using the sort command. There is a Stack Overflow answer for this.

And now we should have our results. The head -n10 most followed user on Codeberg, at the time of writing, is:

  1. blau_araujo (252 followers)
  2. dnkl (217 followers) (creator of foot, fuzzel,…)
  3. smart-airdrop (114 followers) (spam!!)
  4. fnetX (101 followers) (executive director of Codeberg)
  5. aral (75 followers) (head of Small Technology Foundation)
  6. Galkurta (63 followers) (spam!!)
  7. izzy (62 followers) (head of IzzyOnDroid)
  8. circlebuilder (57 followers) (member of Fediverse Futures)
  9. andrewrk (56 followers) (benevolent dictator of Zig)
  10. daviwil (55 followers) (creator of System Crafters)

And before you ask, I’m at the 54th place with 25 followers, sharing the position with aryak (creator of Mozhi and Gothub). I could have been on the head -n50 if there were no bots :(

If you are a Codeberg user, check out your position on the list! The sorted CSV file can be viewed here.