Automate Mastodon interactions with Python

Follow along as I play with the Mastodon API to create a new application.
1 reader likes this.
Women in computing and open source v5

kris krüg

The federated Mastodon social network has gotten very popular lately. It's fun to post on social media, but it's also fun to automate your interactions. There is some documentation of the client-facing API, but it's a bit light on examples. This article aims to help with that.

You should be fairly confident with Python before trying to follow along with this article. If you're not comfortable in Python yet, check out Seth Kenlon's Getting started with Python article and my Program a simple game article.

Create an application

The first step is to go to Preferences in Mastodon and select the Development category. In the Development panel, click on the New Applications button.

After creating an application, copy the access token. Be careful with it. This is your authorization to post to your Mastodon account.

There are a few Python modules that can help.

  • The httpx module is useful, given that it is a web API.
  • The getpass module allows you to paste the token into the session safely.
  • Mastodon uses HTML as its post content, so a nice way to display HTML inline is useful.
  • Communication is all about timing, and the dateutil and zoneinfo modules will help deal with that.

Here's what my typical import list looks like:

import httpx
import getpass
from IPython.core import display
from dateutil import parser
import zoneinfo

Paste in the token into the getpass input:

token=getpass.getpass()

Create the httpx.Client:

client = httpx.Client(headers=dict(Authorization=f"Bearer {token}"))

The verify_credentials method exists to verify that the token works. It's a good test, and it gives you useful metadata about your account:

res = client.get("https://mastodon.social/api/v1/accounts/verify_credentials")

You can query your Mastodon identity:

res.raise_for_status()
result = res.json()
result["id"], result["username"]

>>> ('27639', 'moshez')

Your mileage will vary, but you get your internal ID and username in response. The ID can be useful later.

For now, abstract away the raise_for_status and parse the JSON output:

def parse(result):
    result.raise_for_status()
    return result.json()

Here's how this can be useful. Now you can check your account data by ID. This is a nice way to cross-check consistency:

result = parse(client.get("https://mastodon.social/api/v1/accounts/27639"))
result["username"]

>>> 'moshez'

But the interesting thing, of course, is to get your timeline. Luckily, there's an API for that:

statuses = parse(client.get("https://mastodon.social/api/v1/timelines/home"))
len(statuses)

>>> 20

It's just a count of posts, but that's enough for now. There's no need to deal with paging just yet. The question is, what can you do with a list of your posts? Well, you can query it for all kinds of interesting data. For instance, who posted the fourth status?

some_status = statuses[3]
some_status["account"]["username"]

>>> 'donwatkins'

Wonderful, a tweet from fellow Opensource.com correspondent Don Watkins! Always great content. I'll check it out:

display.HTML(some_status["content"])

<p>Just finished installed <span class="h-card"><a href="https://fosstodon.org/@fedora" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@<span>fedora</span></a></span> <a href="https://fosstodon.org/tags/Silverblue" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank">#<span>Silverblue</span></a> 37 on <span class="h-card"><a href="https://fosstodon.org/@system76" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@<span>system76</span></a></span> <a href="https://fosstodon.org/tags/DarterPro" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank">#<span>DarterPro</span></a></p>

"Just" finished? Wait, when was this tweet posted? I live in California, so I want the time in my local zone:

california = zoneinfo.ZoneInfo("US/Pacific")
when = parser.isoparse(some_status["created_at"])
print(when.astimezone(california))

>>> 2022-12-29 13:56:56-08:00

Today (at the time of this writing), a little before 2 PM. Talk about timeliness.

Do you want to see the post for yourself? Here's the URL:

some_status["url"]

>>> 'https://fosstodon.org/@donwatkins/109599196234639478'

Enjoy tooting, now with 20% more API!

Moshe sitting down, head slightly to the side. His t-shirt has Guardians of the Galaxy silhoutes against a background of sound visualization bars.
Moshe has been involved in the Linux community since 1998, helping in Linux "installation parties". He has been programming Python since 1999, and has contributed to the core Python interpreter. Moshe has been a DevOps/SRE since before those terms existed, caring deeply about software reliability, build reproducibility and other such things.

2 Comments

Thank you for the mention Moshez. That was very kind of you. I really like this article and you've inspired me to try this use of Python.

Very nice. Another reason for me to get started with Mastodon.

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.