DEV Community

Paxmod
Paxmod

Posted on

Perspective API shuts down Dec 31: what breaks on January 1

Google's Perspective API stops working after December 31, 2026. Jigsaw stopped taking usage and quota-increase requests after February 2026, so whatever quota you have now is what you'll have until the end. The default is an average of 1 query per second per project.

The shutdown itself is easy to understand. The dangerous part is what your code does when the call stops working, because a lot of moderation code is written to never get in the user's way. When that code fails, it lets everything through and doesn't tell anyone.

Disclosure: we build Paxmod, a moderation API, and we mention it at the end. Everything else here applies whatever you replace Perspective with.

1. Assume it fails quietly

We don't know exactly what commentanalyzer.googleapis.com will return on January 1. It could be an HTTP error, a timeout or a DNS failure. Your code has to treat all three as "moderation is down", not "this comment is fine".

Here's what two popular integrations do today, from reading their source:

  • Discourse's official plugin turns any response without attributeScores into a score of 0.0. An HTTP error body has no scores, so every post looks clean and nothing gets flagged. Only connection failures raise an error. If you have backfill on, it also stores those 0.0 scores against old posts.
  • Coral Talk logs "could not determine comment toxicity" and lets the comment through unflagged.

Your own code probably has the same habit. Look for patterns like these:

const score = data?.attributeScores?.TOXICITY?.summaryScore?.value ?? 0;
Enter fullscreen mode Exit fullscreen mode
try:
    score = analyze(text)
except Exception:
    score = 0.0
Enter fullscreen mode Exit fullscreen mode

Both of these turn an outage into an "everything is clean" result. Make the failure explicit instead:

if (!res.ok || !data.attributeScores) {
  metrics.increment("moderation.failed");
  throw new ModerationUnavailable(res.status);
}
Enter fullscreen mode Exit fullscreen mode

Then decide on purpose, for each surface, whether a failed check lets the message through, holds it for review, or blocks it. Live game chat usually allows it and logs. A kids' platform or a comment section with legal exposure might hold it.

2. Rehearse January 1 now

You don't have to wait. In staging, make Perspective unreachable and watch what happens:

# Option A: break DNS for the host (staging box only)
echo "127.0.0.1 commentanalyzer.googleapis.com" | sudo tee -a /etc/hosts

# Option B: swap in a bad key, which gives you an error body instead of a dead host
export PERSPECTIVE_API_KEY=invalid
Enter fullscreen mode Exit fullscreen mode

Then post something nasty and check:

  • [ ] Did anything get flagged, held or logged?
  • [ ] Did an alert fire?
  • [ ] Did the service still start? If you build a Google API client at startup with googleapiclient.discovery.build("commentanalyzer", ...), that call fetches Perspective's discovery document over the network (the Python client doesn't ship a copy for this API), so it can fail before your app handles a single request.
  • [ ] Did a queue or retry loop start hammering the API?

Undo the change afterwards.

3. Find every call site

Perspective tends to show up in more places than the one service you remember.

Code and config:

rg -n -i "commentanalyzer|perspectiveapi|comments:analyze|suggestscore|PERSPECTIVE_API" .
rg -n "perspective-api-client|google-api-python-client|peRspective" \
  package.json package-lock.json yarn.lock pnpm-lock.yaml requirements*.txt pyproject.toml poetry.lock DESCRIPTION 2>/dev/null
rg -n -i "perspective" .env* *.tf *.yaml *.yml 2>/dev/null
Enter fullscreen mode Exit fullscreen mode

Across your GitHub org:

gh search code "commentanalyzer" --owner YOUR_ORG
gh search code "perspectiveapi" --owner YOUR_ORG
Enter fullscreen mode Exit fullscreen mode

In Google Cloud. The API is enabled per project, so this lists every project that can still call it:

for p in $(gcloud projects list --format="value(projectId)"); do
  gcloud services list --enabled --project "$p" --format="value(config.name)" \
    | grep -q commentanalyzer && echo "$p"
done
Enter fullscreen mode Exit fullscreen mode

Then check the traffic graph for each one in the console under APIs and Services. If a project shows traffic you can't explain, something you forgot is still calling it.

In apps you didn't write. These keep their Perspective settings in the app, not in your repo:

  • Discourse: the discourse-perspective-api plugin, with its key in site settings.
  • Coral Talk: Configure, Moderation, the Perspective toxicity filter.
  • Minecraft servers: chat plugins like ChatProtect and AutoMod can be set up to use Perspective.
  • Discord and Telegram bots, browser extensions, and research notebooks (the peRspective R package is common in academic work).

4. List what you'll lose

Write down exactly what you use, because a replacement won't match it one for one:

  • Attributes. The production ones were TOXICITY, SEVERE_TOXICITY, IDENTITY_ATTACK, INSULT, PROFANITY and THREAT, and SEXUALLY_EXPLICIT and FLIRTATION were experimental (English only). Note which ones you request and which ones you actually act on.
  • Thresholds. A 0.8 from Perspective doesn't mean 0.8 anywhere else. Every threshold has to be re-tuned.
  • Span scores. If you highlight the offending part of a comment, you're using spanScores, and most replacements don't return them.
  • Languages. Check which languages your traffic is actually in, not which ones you configured.
  • Stored scores. Dashboards, trust levels and "toxicity over time" charts built on stored scores will flatline, or worse, fill with zeros from failed calls.
  • Feedback. If you send moderator decisions back with comments:suggestscore (Coral's "send feedback" option does this), that loop ends too.

5. Shadow-run before you switch

This is the step that makes the cutover boring. It only works while Perspective is still answering, so it has to happen before December 31.

  1. Wrap your moderation call so it sends each message to Perspective and to the replacement. Keep acting on Perspective's answer.
  2. Log both results, plus how long each took and whether either one errored.
  3. Because you can't get more quota, sample traffic if you're near your limit (on the default 1 QPS, that means sampling). A few thousand real messages is enough to compare.
  4. Review the cases where they disagree. That's where you set the new thresholds, and where you find out what the replacement catches that Perspective didn't.
  5. Switch which answer you act on, keep logging both for a few days, then remove Perspective and delete the key.

Aim to finish well before the holidays. Nobody wants to debug moderation on December 31.

Where Paxmod fits

If you're choosing a replacement, Paxmod is built for real-time chat and comments. It takes one call per message with a user_id and context_id, returns a flagged or not_flagged decision with per-category scores, and catches personal information like "where do you live", which Perspective never scored. It has its own format, so you'll change the request and response code, not just the URL. Plans start at $29/month for 50,000 messages, with a 14-day trial that includes 10,000 messages (card required, no free plan).

The step-by-step migration, with the attribute mapping and before and after code, is in our guide: Perspective API alternative: migrate before Dec 31.

Top comments (0)