Skip to main content

Verified against Package registries and forge APIs (npm, PyPI, crates.io, RubyGems, Packagist, Go proxy, GitHub, Codeberg) on 2026-10-15; @fedify/* 2.4.4 on 2026-10-15.

Python Libraries

Python libraries for implementing ActivityPub.

Status and versions below were taken from PyPI, Codeberg and GitHub on 2026-10-15. Active = a release or commit in the last 12 months; Dormant = 12–36 months; Archived = the repository is archived.

LibraryLatest releaseStatus
federation0.27.0 (2026-03-28)Active
PyLD3.3.0 (2026-09-02)Active
http-message-signatures2.0.1 (2026-01-19)Active
bovine0.5.19 (2025-09-24)Dormant
apkit0.3.8 (2026-02-17)Archived (Sep 2026)
little-boxes0.1.0 (2018-06-12)Archived
httpsig1.3.0 (2018-11-28)Archived

federation​

Python library that abstracts several social-web protocols — ActivityPub and Diaspora are stable; Matrix support is early — behind one entity API. Used by Socialhome, with optional Django integration.

PropertyValue
Repositorycodeberg.org/socialhome/federation (mirror: github.com/jaywink/federation)
Documentationfederation.readthedocs.io
pipfederation
LicenseBSD-3-Clause

Sending goes through federation.outbound.handle_send(entity, author_user, recipients, ...), where the author must carry private_key and fid, and each recipient is a dict with endpoint, fid, public and protocol. See the usage documentation for full examples.

PyLD​

The reference JSON-LD processor for Python.

PropertyValue
Repositorygithub.com/digitalbazaar/pyld
pipPyLD
from pyld import jsonld

expanded = jsonld.expand(document)
compacted = jsonld.compact(expanded, "https://www.w3.org/ns/activitystreams")

http-message-signatures​

An implementation of RFC 9421 HTTP Message Signatures. Note that it does not check the body digest for you, and that it follows the "see what is signed" rule: only trust data returned by verify(). Its sister project requests-http-signature adds body-digest handling for requests.

PropertyValue
Repositorygithub.com/pyauth/http-message-signatures
piphttp-message-signatures

Most of the fediverse still sends draft-cavage signatures, so a server receiving from Mastodon and others needs to handle both formats. See the README for signer and verifier examples.

bovine​

Async ActivityPub client and server building blocks (actor objects, signed requests, WebFinger), by Helge.

PropertyValue
Repositorycodeberg.org/bovine/bovine
Documentationbovine.readthedocs.io
pipbovine
LicenseMIT
from bovine import BovineActor
from bovine.activitystreams import Actor

# Build an actor document
actor_doc = Actor(
id="https://example.com/users/alice",
preferred_username="alice",
name="Alice",
inbox="https://example.com/users/alice/inbox",
outbox="https://example.com/users/alice/outbox",
).build()

# Send a signed POST to a remote inbox
async with BovineActor(
actor_id="https://example.com/users/alice",
public_key_url="https://example.com/users/alice#main-key",
secret=private_key_pem,
) as actor:
await actor.post("https://remote.example/inbox", activity)

BovineActor can also send an RFC 9530 Content-Digest instead of the legacy Digest header (digest_method parameter).

Archived Projects​

These still appear in older tutorials. Don't start new work on them.

ProjectNotes
apkitActivityPub toolkit; the whole fedi-libs organisation was archived in September 2026
little-boxesArchived; last release 2018
httpsigdraft-cavage only; archived, last release 2018 — use http-message-signatures or bovine's signing instead

Choosing​

  • Multi-protocol or Django project: federation.
  • Async, building your own server: bovine is the most complete toolkit, but check its activity before depending on it.
  • Only need signatures: http-message-signatures (RFC 9421) — and handle draft-cavage too for compatibility.

Example: Serving an Actor with Flask​

import json
from flask import Flask, Response

app = Flask(__name__)
AP = "application/activity+json"

@app.get("/users/<username>")
def actor(username):
base = f"https://example.com/users/{username}"
doc = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
],
"type": "Person",
"id": base,
"preferredUsername": username,
"inbox": f"{base}/inbox",
"outbox": f"{base}/outbox",
"publicKey": {
"id": f"{base}#main-key",
"owner": base,
"publicKeyPem": PUBLIC_KEY_PEM,
},
}
# Flask's jsonify() sends application/json, which Mastodon ignores.
return Response(json.dumps(doc), content_type=AP)

An inbox endpoint must verify the request signature, the body digest and that the signing key belongs to the activity's actor before acting on anything — see issue #37.

See Also​