Prisma Reviews / Guides / Mozilla Add-ons reviews API in Python

Mozilla Add-ons reviews API in Python: AMO ratings, JWT replies

addons.mozilla.org (AMO) is one of the few review sources where reading needs nothing at all: the ratings of any Firefox add-on are a public JSON endpoint. Replying needs an AMO API key and a JWT you sign yourself, which is five lines of Python once you know the claims. This guide reads ratings with requests, builds the token with the standard library, and posts a reply, following the addons-server docs as Prisma’s Mozilla Add-ons connector does (reading checked live, replies in beta). The developer-platform picture is on B2B software reviews API.

Shell
pip install requests

Credentials: the add-on’s page (https://addons.mozilla.org/en-US/firefox/addon/<slug>/) to read; to reply, the JWT issuer (user:…:…) and JWT secret of an API key from addons.mozilla.org/developers/addon/api/key/, created by an account listed as a developer of the add-on.

1. Ratings, newest first, following next

Python
import os, requests
from datetime import datetime, timedelta, timezone

API = "https://addons.mozilla.org/api/v5"
SLUG = "ublock-origin"   # from the add-on's page: /firefox/addon/<slug>/
PAGE = f"https://addons.mozilla.org/en-US/firefox/addon/{SLUG}"

def ratings(since=None):
    url = f"{API}/ratings/rating/?addon={SLUG}&page_size=50"
    while url:
        r = requests.get(url, headers={"Accept": "application/json"}, timeout=30)
        r.raise_for_status()
        body = r.json()
        for rt in body.get("results", []):
            if since and datetime.fromisoformat(rt["created"].replace("Z", "+00:00")) < since:
                return
            yield rt
        url = body.get("next")

since = datetime.now(timezone.utc) - timedelta(days=31)
for rt in ratings(since):
    reply = (rt.get("reply") or {}).get("body")
    print(rt["id"], rt.get("score"), (rt.get("user") or {}).get("name"), rt["created"][:10], (rt.get("body") or "")[:80], f"{PAGE}/reviews/{rt['id']}/", "| replied" if reply else "")

Fields: id, score (1 to 5), body (may be null for a star-only rating), created, user.name, and reply with body and created when the developer answered. The endpoint returns each user’s latest rating only. There is no date filter, so stop paging once you pass your last sync, with a month of look-back for new replies.

2. Reply with a 5-minute JWT

Python
import base64, hashlib, hmac, json, time, uuid

def amo_jwt(issuer, secret):
    b64 = lambda o: base64.urlsafe_b64encode(json.dumps(o, separators=(",", ":")).encode()).rstrip(b"=").decode()
    now = int(time.time())
    head = f"{b64({'alg': 'HS256', 'typ': 'JWT'})}.{b64({'iss': issuer, 'jti': str(uuid.uuid4()), 'iat': now, 'exp': now + 240})}"
    sig = base64.urlsafe_b64encode(hmac.new(secret.encode(), head.encode(), hashlib.sha256).digest()).rstrip(b"=").decode()
    return f"{head}.{sig}"

token = amo_jwt(os.environ["AMO_JWT_ISSUER"], os.environ["AMO_JWT_SECRET"])
rid = rt["id"]
requests.post(f"{API}/ratings/rating/{rid}/reply/", headers={"Authorization": f"JWT {token}", "Content-Type": "application/json"},
              json={"body": "Thanks for the report. The 1.62 update fixes the toolbar icon."}, timeout=30).raise_for_status()

AMO rejects tokens that expire more than 5 minutes after iat, so build one per request. A 401 or 403 means the key doesn’t belong to a developer of that add-on. Each rating takes one developer reply; posting again replaces it.

3. The same job through Prisma

Prisma connects a Firefox add-on from its page (no login), reads ratings with the month of look-back, and, when the developer adds the AMO API key to the connection, publishes replies with a fresh JWT per request and reads the rating back to confirm. One key covers AMO plus the App Store, Google Play, Open VSX, JetBrains Marketplace and 50+ more.

Python
H = {"Authorization": f"Bearer {os.environ['PRISMA_API_KEY']}"}
r = requests.get("https://api.prismareviews.com/v1/reviews", headers=H, params={"platform": "mozilla", "has_reply": "false", "limit": 100}, timeout=30)
for rev in r.json()["data"]:
    print(rev["rating"], rev["author"], rev["text"], rev["url"])
requests.post(f"https://api.prismareviews.com/v1/reviews/{rev['id']}/reply", headers=H, json={"text": "Thanks for the report. The 1.62 update fixes the toolbar icon."}, timeout=30).raise_for_status()

Mozilla Add-ons is in beta in Prisma, read without a key and with replies once the AMO key is added. Connection fields: Platforms. A test key returns sample reviews so the code runs now.

FAQ

Do I need a key to read Firefox add-on reviews?

No. GET https://addons.mozilla.org/api/v5/ratings/rating/?addon=<slug> is public and returns each user’s latest rating, newest first, 50 per page, with a next link. A key is only needed to reply.

How do I reply to an AMO review by API?

Create an API key at addons.mozilla.org/developers/addon/api/key/ with an account listed as a developer of the add-on, build an HS256 JWT with iss = the key, a jti, iat and an exp at most 5 minutes ahead, signed with the secret, and POST /api/v5/ratings/rating/{id}/reply/ with {"body": "…"} and Authorization: JWT <token>.

Can I filter AMO ratings by date?

No. Ratings come newest first; follow next and stop at the first one older than your last sync, looking back about a month so new developer replies on recent ratings are picked up.

Firefox add-ons, VS Code extensions, mobile apps, one key.

Get a free API key

2 locations free. Test keys return sample reviews from every platform.

Next step: read the reviews as JSON with the Mozilla Add-ons reviews API, and compare plans on Prisma Reviews pricing.

Get your API key in a minute

Free Spark plan: 2 locations, no card. Test keys return sample data on all 84 platforms, so you can build before connecting a real one.