Over the last 48 hours, swarms of remarkably similar and remarkably boring spam accounts have been following various Bluesky users at a rapid pace. The users followed by this group of spam accounts have relatively little in common, and it is at present unclear whether any of them requested or desired the infusions of inauthentic followers they received. This spam network shares certain traits with a previous spam network discussed on this blog, and like its potential predecessor, was flagged by an ongoing and evolving experiment in using the Bluesky firehose to detect various forms of inauthentic activity.
This spam network consists of at least 629 Bluesky accounts, all of which have handles ending in numbers between 1 and 1000. (It is possible that the network was initially larger, as some of the spam accounts were suspended while this article was being written.) Many of these accounts lacked created_at or indexed_at timestamps at the time the data was gathered, making it difficult to determine exactly when they came into being. Based on the timestamps that were present, the accounts were likely created around April 15th, 2025, or later. Each account has a default profile image, and each follows most but not all of the same set of ten accounts. Most of the accounts followed by the network post in English or Turkish, with the exception of @happyhentai.bsky.social, a Portuguese-language adult content account.
from atproto import Client
import json
import pandas as pd
import time
client = Client ()
client.login ("***************", "***************")
fakes = list (pd.read_csv ("20250417_spam_followers.csv")["did"])
targets = list (pd.read_csv ("20250417_followed.csv")["did"])
def retry (method, params):
retries = 5
delay = 1
while retries > 0:
try:
r = method (params)
return r
except:
print (" error, sleeping " + str (delay) + "s")
time.sleep (delay)
delay = delay * 2
retries = retries - 1
return None
def get_relationships (actor, others, client):
r = retry (client.app.bsky.graph.get_relationships,
{"actor" : actor, "others" : others})
return r
def to_params (uri):
u = uri.replace ("at://", "").split ("/")
return {"repo" : u[0], "collection" : u[1], "rkey" : u[2]}
rows = []
for fake in fakes:
r = get_relationships (fake, targets, client)
if r is not None:
r = r["relationships"]
for result, target in zip (r, targets):
row = {"fake_did" : fake, "target_did" : target}
uri = result["following"]
row["follow_record"] = uri
try:
record = client.com.atproto.repo.get_record (
to_params (uri)).value
row["follow_timestamp"] = record.created_at
rows.append (row)
except:
print ("error, skipping: ")
print (result)
df = pd.DataFrame (rows)
df.to_csv ("20250417_follow_times.csv")
Although the creation times are unavailable for many of the spam accounts, it is possible to determine the times at which the spam accounts followed their targets. This is done by calling the app.bsky.graph.getRelationships endpoint for each pairing of spam account and target account to obtain the URI of each follow record, and then in turn retrieving the record and extracting the timestamp. The above code shows how this can be done in Python using the atproto module; implementation in other popular languages is similar. (Python code to download follower lists can be found in this previous article.)
All of the spam accounts followed their targets between April 15th and April 17th, 2025. Most of the follows took place in batches of hundreds of accounts that followed the same account in the span of a few minutes to an hour; there are a few individual follows that took place earlier, and may be the remains of prior batches of accounts that were mostly removed by Bluesky.
In addition to the bulk follow behavior, this spam network also reposted a trio of posts en masse. The three posts reposted by the network, which include a post advertising a news website, a movie review, and a photograph of a sunset, have no apparent relationship to one another beyond having inauthentic amplification bestowed upon them by the same spam network. For each of the first two posts, almost all of the engagement they received was from the spam accounts.
Some of the spam accounts also left brief replies to the posts reposted by the spam network, as well as to various posts from the accounts followed by the spam network. In a few cases, the same spam account replied to the same post more than once. Each reply consists of the word "Adorei", "Amei", or "Gostei" followed by emoji, a behavior this network shares with a now-suspended spam network discussed in this article from February. The accounts in the banned network also used a similar naming convention to those in the current network, suggesting that both networks are iterations of the same project, or were created using the same tools.