<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tomer N</title>
    <description>The latest articles on DEV Community by Tomer N (@tomer_n).</description>
    <link>https://dev.to/tomer_n</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4206398%2F0d23c795-8bac-490f-a10c-98721968a44f.png</url>
      <title>DEV Community: Tomer N</title>
      <link>https://dev.to/tomer_n</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomer_n"/>
    <language>en</language>
    <item>
      <title>7 Profanity Filters for Developers in 2026 (Perspective API Is Shutting Down)</title>
      <dc:creator>Tomer N</dc:creator>
      <pubDate>Sun, 25 Oct 2026 11:34:15 +0000</pubDate>
      <link>https://dev.to/tomer_n/7-profanity-filters-for-developers-in-2026-perspective-api-is-shutting-down-1klj</link>
      <guid>https://dev.to/tomer_n/7-profanity-filters-for-developers-in-2026-perspective-api-is-shutting-down-1klj</guid>
      <description>&lt;p&gt;If you have ever shipped a signup form, you know the moment. Somebody registers a username you really don't want on your leaderboard, and it's sitting there in the top ten by Friday.&lt;/p&gt;

&lt;p&gt;So you add a word list. A week later they're back as sh1t, f.u.c.k or with a Greek letter swapped in, and when you tighten the list, somebody from Scunthorpe can't sign up anymore. &lt;em&gt;(Written with AI help; every number here is checked against the source I link.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And if you handed this problem to Google's Perspective API, there's a date in your calendar you may not know about: Jigsaw says the service runs until December 31, 2026, and its FAQ says access can't be extended past that.&lt;/p&gt;

&lt;p&gt;So I went through the options developers actually reach for. Five are APIs, one is an npm package, one is on its way out. A few are free, and I'll say which. I'll also say where each one falls short, because nothing annoys me more than a "best tools" list where everything is perfect.&lt;/p&gt;

&lt;p&gt;Let's start the list:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Unprofane
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi0t0puk5tc2eskrw5fxk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi0t0puk5tc2eskrw5fxk.webp" alt="The Unprofane playground reading fuck typed with a Greek upsilon: verdict review, strong, tagged homoglyph and mixed_script, censored to four asterisks" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The unprofane.com playground&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Unprofane is a profanity filter API built for the two problems at the top of this post. Full disclosure, I built this one.&lt;/p&gt;

&lt;p&gt;You send it a username, a chat message or a review, and it answers allow, review or block, with the evidence: which words it found, where they were, how each one was disguised, and a censored copy to display. It undoes 13 measured kinds of disguise, from a swapped digit to a look-alike letter out of another alphabet.&lt;/p&gt;

&lt;p&gt;It also answers on Perspective's path, with Perspective's JSON and Perspective's errors, so a Python or Node client moves over by changing one line, and Coral Talk or Discourse by changing two.&lt;/p&gt;

&lt;p&gt;On the numbers: in my own head-to-head, read once on 2026-09-28 against 9,029 texts labeled blind by AI judges, it caught 89.0% of the 2,672 toxic ones while flagging 7.0% of the clean ones. Perspective, read at the threshold where it flags that same 7.0%, caught 58.4%. On the public split of the Scunthorpe Test (run of 2026-10-19) its rules flagged 0 of 549 innocent words and names, though that list is the engine's own, so it was built not to flag them. Your own innocent words are not in it.&lt;/p&gt;

&lt;p&gt;Where it falls short: there is no free plan, only free test keys at 100 checks a day for building. It reads text, not images or video. It's a young product. And the benchmark above is mine, with labels from AI judges, not an independent lab's.&lt;/p&gt;

&lt;p&gt;Plans start at $9 a month for 25,000 checks, half price for the first three months until December 31, 2026.&lt;/p&gt;

&lt;p&gt;There are lots of other features also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;17 languages, each with its own words and spellings&lt;/li&gt;
&lt;li&gt;Catches insults and threats written in ordinary words, not only listed ones&lt;/li&gt;
&lt;li&gt;Typed SDKs for JavaScript and Python&lt;/li&gt;
&lt;li&gt;An MCP server, so a coding agent can write the call itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checkout here: &lt;a href="https://unprofane.com/bench/" rel="noopener noreferrer"&gt;unprofane.com/bench&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. OpenAI Moderation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Finxhttoz8e401yrop123.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Finxhttoz8e401yrop123.webp" alt="OpenAI's moderation guide on developers.openai.com" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: OpenAI's moderation guide&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenAI's moderation endpoint is free to use, and if you already have an OpenAI key, it's one request away. That's a real advantage worth taking seriously.&lt;/p&gt;

&lt;p&gt;The current model, omni-moderation-latest, classifies text and images into thirteen categories across harassment, hate, illicit, self-harm, sexual and violence, with their subcategories. You get a flagged boolean, plus a flag and a score for each category.&lt;/p&gt;

&lt;p&gt;The catch for this particular job: none of those thirteen categories is profanity. It's built to find harm, not swearing. On my test set, read at the threshold where it flags the same 7.0% of clean text as the others, it caught 40.8% of the toxic texts. Its own flagged verdict catches more, 62.2%, but flags 20.6% of the clean text, which is a lot of false alarms to hand your moderators.&lt;/p&gt;

&lt;p&gt;There are lots of other features also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free&lt;/li&gt;
&lt;li&gt;Reads images as well as text&lt;/li&gt;
&lt;li&gt;OpenAI's data controls list /v1/moderations as not used for training&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checkout here: &lt;a href="https://developers.openai.com/api/docs/guides/moderation" rel="noopener noreferrer"&gt;OpenAI moderation guide&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Perspective API
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9bkzaut989eergc0e59e.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9bkzaut989eergc0e59e.webp" alt="The perspectiveapi.com home page" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: perspectiveapi.com&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Perspective was the free default for years, and it earned that. Coral Talk and Discourse both shipped it, at a 0.7 threshold, which is why so many comment queues look the way they do.&lt;/p&gt;

&lt;p&gt;You ask for the attributes you want, such as TOXICITY, SEVERE_TOXICITY, INSULT, PROFANITY and THREAT, and it answers with a probability for each, for the whole comment and optionally per sentence. TOXICITY is listed for seventeen languages, the other production attributes for seven.&lt;/p&gt;

&lt;p&gt;One thing worth knowing about that 0.7 default: on my test set it flags only 1.0% of clean text there, which is lovely, but it catches 33.0% of the toxic text. Quiet queues are not the same as clean comments.&lt;/p&gt;

&lt;p&gt;It's on this list as a warning, though. Jigsaw says the service remains active until December 31, 2026, and the FAQ says access cannot be extended past the final date. Requests for new access were taken only until February 2026. The sunset notice on its own home page adds the part that matters most if you're integrated: there will be no direct migration support. If it's in your stack today, you need a plan before then.&lt;/p&gt;

&lt;p&gt;There are lots of other features also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free, with a default quota averaging one query per second per project&lt;/li&gt;
&lt;li&gt;Experimental attributes, including ones trained on New York Times comments&lt;/li&gt;
&lt;li&gt;A doNotStore flag, which the docs tell you to set for private text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checkout here: &lt;a href="https://perspectiveapi.com/" rel="noopener noreferrer"&gt;perspectiveapi.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. WebPurify
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff22nuok6uk6r1ymel8ih.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff22nuok6uk6r1ymel8ih.webp" alt="WebPurify's documentation for its check method" width="800" height="445"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: webpurify.com documentation&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;WebPurify does more than text, and that's the reason to pick it. Its in-house team moderates images around the clock, every day of the year, and it says it doesn't crowdsource that work. Video gets AI review across 16 or more categories, with the audio transcribed and read too.&lt;/p&gt;

&lt;p&gt;For text, its API checks against a word list it maintains, and answers with a count, the words it found, or the text with them masked. You can add your own block and allow lists per license key, by API or in the admin.&lt;/p&gt;

&lt;p&gt;Two things to check before you build on it. The documented text responses don't tell you where each word was, what category it is, how severe it is, or how it was disguised, so you can't highlight a span or tune by category. And language isn't detected: the caller names it. Its docs list twelve languages plus three in beta.&lt;/p&gt;

&lt;p&gt;There are lots of other features also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email addresses, phone numbers and URLs can count as profanity&lt;/li&gt;
&lt;li&gt;Plugins for WordPress, Drupal, Joomla and bbPress&lt;/li&gt;
&lt;li&gt;Endpoints in the US, the EU and Asia-Pacific&lt;/li&gt;
&lt;li&gt;Texts up to 30,000 characters, with no cap on queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checkout here: &lt;a href="https://www.webpurify.com/" rel="noopener noreferrer"&gt;webpurify.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. CleanSpeak
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2cshdds5cgtlpk7e9ckd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2cshdds5cgtlpk7e9ckd.webp" alt="The cleanspeak.com home page" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: cleanspeak.com&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;CleanSpeak is the heavyweight. It's a filter plus a full moderation console: approval queues, alerts, user reputation, ban and mute tools, and reports by tag, severity and top offenders. If you have actual moderators, that console is worth more than any accuracy number.&lt;/p&gt;

&lt;p&gt;Its filter answers well, too. Each match comes back with its position, length, matched text, root word, a severity and tags such as vulgarity, sexual or bullying. You can self-host it or let them run a private server for you, and 22 or more base language lists come included for you to edit.&lt;/p&gt;

&lt;p&gt;Three things to know. Prices aren't published, so you'll be talking to sales. The machine-learning models cost extra, and its features page says those models come from Google Perspective, which ends after December 31, 2026; I couldn't find a page that says what happens to them then. And self-hosting wants MySQL 8 or Postgres 10 and an OpenSearch or Elasticsearch cluster beside it.&lt;/p&gt;

&lt;p&gt;There are lots of other features also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filters for email addresses, phone numbers, URLs and usernames&lt;/li&gt;
&lt;li&gt;An allowlist mode for children's chat&lt;/li&gt;
&lt;li&gt;Webhooks for approvals, user actions, edits and deletes&lt;/li&gt;
&lt;li&gt;A 30-day free trial with no credit card&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checkout here: &lt;a href="https://cleanspeak.com/" rel="noopener noreferrer"&gt;cleanspeak.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Azure AI Content Safety
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy0yuhs86vv4y3fzm7xbl.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy0yuhs86vv4y3fzm7xbl.webp" alt="Azure AI Content Safety's harm categories on learn.microsoft.com" width="800" height="556"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: Azure AI Content Safety docs&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you're already on Azure, this is the natural place to look, and keeping it in one bill is a genuine reason to choose it.&lt;/p&gt;

&lt;p&gt;It reads text and images for four harm categories: hate and fairness, sexual, violence and self-harm. Each comes back with a severity, 0 to 7, or four levels by default. You get a categoriesAnalysis array and, if you use blocklists, the list name and the item that matched.&lt;/p&gt;

&lt;p&gt;Profanity isn't a category here. Microsoft's own harm-category docs put curse words, profanity and insults that don't target an identity group at level 2 of hate, the lowest band above nothing. So a plain "is this rude?" answer means tuning thresholds rather than reading a field. You'll also need an Azure subscription and a resource in a supported region before your first call.&lt;/p&gt;

&lt;p&gt;There are lots of other features also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your own blocklists, stored in your resource's region&lt;/li&gt;
&lt;li&gt;Prompt Shields for LLM apps, plus groundedness detection in preview&lt;/li&gt;
&lt;li&gt;Inputs not stored during detection and not used for training&lt;/li&gt;
&lt;li&gt;Trained and tested on eight languages; others may work at varying quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checkout here: &lt;a href="https://learn.microsoft.com/en-us/azure/ai-services/content-safety/overview" rel="noopener noreferrer"&gt;Azure AI Content Safety&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. obscenity (npm)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffa28hwirffuqmigb18w8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffa28hwirffuqmigb18w8.webp" alt="The obscenity repository on GitHub" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: obscenity on GitHub&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you don't want an API at all, obscenity is the npm package I'd reach for. It runs inside your own Node process, which means no network call, no per-check cost, and the text never leaves your server.&lt;/p&gt;

&lt;p&gt;It matches more than an exact string: repeated letters, look-alike Unicode letters, and a word hidden inside a longer one. It tells you where each match is and can censor it for you. It's MIT licensed, and you can add your own words on top of the built-in list.&lt;/p&gt;

&lt;p&gt;Its README is honest about the limit, and says to treat the output as a heuristic. A word list reads words, not meaning, so "nobody wants you here" goes straight through, and so does a disguise nobody has added yet. For a side project, that trade is often exactly right.&lt;/p&gt;

&lt;p&gt;There are lots of other features also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero cost and no network latency&lt;/li&gt;
&lt;li&gt;Works anywhere Node runs&lt;/li&gt;
&lt;li&gt;Your own word list on top of the default English one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checkout here: &lt;a href="https://github.com/jo3-l/obscenity" rel="noopener noreferrer"&gt;github.com/jo3-l/obscenity&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That's the list.&lt;/p&gt;

&lt;p&gt;How I'd pick, honestly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want harassment and self-harm flags more than swearing, for free: OpenAI Moderation.&lt;/li&gt;
&lt;li&gt;You need images or video moderated, or people in the loop: WebPurify.&lt;/li&gt;
&lt;li&gt;You have moderators and want them a console: CleanSpeak.&lt;/li&gt;
&lt;li&gt;You're already deep in Azure: Content Safety.&lt;/li&gt;
&lt;li&gt;It's a side project and a heuristic is fine: obscenity, for nothing.&lt;/li&gt;
&lt;li&gt;Usernames and chat that people will deliberately spell around, or a Perspective integration to replace: that's the gap I built Unprofane for, and the migration guide is at &lt;a href="https://unprofane.com/perspective/" rel="noopener noreferrer"&gt;unprofane.com/perspective&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One last thing, whichever way you go. The 0.7 threshold everybody inherited from Perspective is a choice, not a default, and it's a quiet one. Measure your own false alarms before you ship, on your own text, in your own languages. Every number in this post came with a false-alarm rate beside it for exactly that reason.&lt;/p&gt;

&lt;p&gt;If you've built a filter yourself, or you've seen a disguise none of these catch, drop it in the comments. I'm always looking for the next one.&lt;/p&gt;

&lt;p&gt;Thanks for reading :)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I build Unprofane, an API that decides whether a text is profane.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>api</category>
      <category>moderation</category>
    </item>
    <item>
      <title>Google Is Shutting Down Perspective API on December 31. Here’s What Breaks, and When</title>
      <dc:creator>Tomer N</dc:creator>
      <pubDate>Sun, 25 Oct 2026 10:58:42 +0000</pubDate>
      <link>https://dev.to/tomer_n/google-is-shutting-down-perspective-api-on-december-31-heres-what-breaks-and-when-2gal</link>
      <guid>https://dev.to/tomer_n/google-is-shutting-down-perspective-api-on-december-31-heres-what-breaks-and-when-2gal</guid>
      <description>&lt;p&gt;December 31, 2026 is the last day Google's Perspective API will answer. Jigsaw's own notice puts it plainly: the service "will remain active until December 31, 2026." There's no extension and no migration help from Google.&lt;/p&gt;

&lt;p&gt;I went through what happens to the tools built on top of it, and the answer surprised me. Most of them won't crash on January 1. They'll keep accepting comments and quietly stop checking them. &lt;em&gt;(AI-assisted draft; I checked each claim against its source.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So here's the whole picture, by date, with the steps I'd take if Perspective sits anywhere in my stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dates
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftescfs06zssa8cfcajrq.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftescfs06zssa8cfcajrq.webp" alt="A timeline: February 2026, the last usage and quota requests answered; December 31, 2026, the last day the API answers; January 1, 2027, nothing after it" width="800" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;From Google's notice at perspectiveapi.com&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;February 2026: requests closed.&lt;/strong&gt; Google stopped taking usage and quota increase requests in February. If your traffic grew this year, you're living on whatever quota you already had. The FAQ puts the default at an average of one query per second per project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now until December 31: it still works.&lt;/strong&gt; Nothing has changed in the API itself. Same endpoint, same JSON, still free. That's the catch, honestly. There's no warning in the response, so nothing in your logs will remind you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;January 1, 2027: no more answers.&lt;/strong&gt; Google hasn't said what the endpoint will return after that, whether it's an error code, a timeout or a domain that doesn't resolve. So plan for all three.&lt;/p&gt;
&lt;h2&gt;
  
  
  What actually breaks
&lt;/h2&gt;

&lt;p&gt;A lot of Perspective calls don't come from code you wrote. They come from comment systems, forum plugins and old scripts, and each of those decided long ago what to do when Google doesn't answer. I read the code for two that ship it built in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coral by Vox Media (a comment platform for news sites).&lt;/strong&gt; Coral's toxic comment filter sends each new comment to Perspective and holds anything above your threshold for a moderator. If the call fails, its code logs "could not determine comment toxicity" and moves on. The comment goes through the rest of the pipeline as if it were clean. No alert, no error for the reader, no item in the queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2h138021ftdolo83vnu6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2h138021ftdolo83vnu6.webp" alt="Coral's toxic comment filter in toxic.ts: the catch block rethrows a ToxicCommentError and otherwise logs could not determine comment toxicity and returns" width="800" height="269"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Coral's toxic.ts on GitHub: a failed call is logged, and the comment moves on&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discourse's Perspective plugin.&lt;/strong&gt; The official plugin flags posts above a toxicity threshold and can warn people while they type. Two failure paths here. If the network call itself fails, it raises an error and the post isn't flagged. And if Google answers with an error body instead of scores, the plugin's parser falls back to a score of 0.0 for every post. Zero never crosses a threshold, so nothing gets flagged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your own code.&lt;/strong&gt; If you call the API directly, what happens depends on one line you probably wrote years ago: the &lt;code&gt;except&lt;/code&gt; around the call. A lot of moderation code is written to fail open on purpose, because nobody wants a Google outage to block every comment. That was the right call for a five-minute outage. It's the wrong call for a permanent one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Research pipelines.&lt;/strong&gt; If a paper, a dataset or a dashboard depends on fresh Perspective scores, it stops being reproducible on new text in January. Scores you already stored still exist, but you can't add comparable ones.&lt;/p&gt;
&lt;h2&gt;
  
  
  Who should care
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Newsrooms and publishers running Coral with the toxic comment filter on.&lt;/li&gt;
&lt;li&gt;Discourse forums with the discourse-perspective-api plugin enabled.&lt;/li&gt;
&lt;li&gt;Apps calling &lt;code&gt;commentanalyzer.googleapis.com&lt;/code&gt; from a backend, a queue worker or a serverless function.&lt;/li&gt;
&lt;li&gt;Teams with a dashboard or a study built on TOXICITY scores.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're not sure, the next step tells you in a minute.&lt;/p&gt;
&lt;h2&gt;
  
  
  The steps, in order
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Find every call
&lt;/h3&gt;

&lt;p&gt;Search your code, your config and your infrastructure for the host name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"commentanalyzer"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also check the admin settings of anything you didn't write. In Coral it's under Configure, Moderation, Toxic Comment Filter. In Discourse, type "perspective" into the admin settings filter. And look for the API key in your Google Cloud project, since forgotten scripts love to share one.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Make failures loud
&lt;/h3&gt;

&lt;p&gt;Before you replace anything, change what happens when the check doesn't answer. Even if you switch next week, this protects you from the next outage too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_comment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# your current toxicity call
&lt;/span&gt;    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;toxicity check failed, holding comment for review&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# treat None as "send to the review queue"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rule is simple: an unanswered check goes to a human or to a hold, never straight to publish. And add an alert on the error count, so a dead endpoint pages someone instead of filling a log nobody reads.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Save what your thresholds mean
&lt;/h3&gt;

&lt;p&gt;Your thresholds were tuned against Perspective's scale, probably over months. Before December, export a sample of recent comments with the scores Perspective gave them and what your moderators decided. Any replacement will score on its own scale, and that sample is how you'll find the new threshold that flags about the same share of comments.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Pick a replacement and run it side by side
&lt;/h3&gt;

&lt;p&gt;Run the new service in parallel for a week or two, logging both answers, before you switch. The main options, with their trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI's moderation endpoint.&lt;/strong&gt; Free, and it scores harassment, hate, self-harm, sexual content and violence. It has no profanity category, so swearing on its own won't move it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure AI Content Safety.&lt;/strong&gt; Hate, sexual, violence and self-harm on a severity scale. Needs an Azure subscription; plain profanity that doesn't target a group sits at a low level of hate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discourse AI.&lt;/strong&gt; For Discourse forums, its AI triage can classify posts with an LLM prompt you write. Discourse-hosted sites can use Discourse's own hosted small LLM; self-hosted forums need a third-party one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A word list.&lt;/strong&gt; Coral and Discourse both have banned words built in. It's free and predictable, but it misses disguised spellings and can block innocent words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A drop-in for Perspective's API.&lt;/strong&gt; Full disclosure, I built Unprofane, which answers Perspective's own requests on the same path with the same JSON, so Coral moves by changing an endpoint and a key, and Discourse by a one-line change to its plugin. The &lt;a href="https://unprofane.com/perspective/" rel="noopener noreferrer"&gt;migration guides&lt;/a&gt; cover each client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these is a perfect clone, since each model scores differently. If you mostly care about threats and harassment and want free, OpenAI is a reasonable first test. If you have an LLM in your forum already, Discourse AI keeps everything in one place.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Switch before December, keep a way back
&lt;/h3&gt;

&lt;p&gt;Don't plan the switch for the last week of the year, when half your team is out. Pick a date in November, keep the old key working until the end of December, and make the endpoint a config value so rolling back is a settings change, not a deploy.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Clean up in January
&lt;/h3&gt;

&lt;p&gt;After the switch, delete the Perspective key from your Google Cloud project and remove the old code path. A dead integration that still "works" is how the failure in step 2 sneaks back in a year later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Perspective's last day is December 31. The real risk isn't a broken page, it's moderation that silently turns off. Find your calls, make failures hold comments instead of publishing them, save your old scores, test a replacement in parallel and switch in November.&lt;/p&gt;

&lt;p&gt;If you're in the middle of this and hit something odd, tell me in the comments. I'd like to know which tools I missed.&lt;/p&gt;

&lt;p&gt;Good luck with the switch, and thanks for sticking with me to the end :)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm the developer behind Unprofane, a profanity and toxicity API.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>moderation</category>
      <category>google</category>
    </item>
  </channel>
</rss>
