layahost
Menu · Perspective API replacement

Perspective API replacement

Google is shutting down the Perspective API on 31 December 2026. layahost implements the same comments:analyze method, request and response, so existing code keeps working after you change the host and the API key.

Endpoints
POST https://layahost.com/v1alpha1/comments:analyze
POST https://layahost.com/v1alpha1/comments:suggestscore
GET  https://layahost.com/$discovery/rest?version=v1alpha1

layahost is not affiliated with Google or Jigsaw. The scores come from a different model, the open Laya model, calibrated so that they read like Perspective's. Check your thresholds on your own comments before you switch; see Accuracy.

Migrate

  1. Create an account and create a key on the API keys page.
  2. Replace commentanalyzer.googleapis.com with layahost.com and your Google API key with the layahost key. The key can stay in ?key=; X-Goog-Api-Key and Authorization: Bearer work too.
comments:analyze
from googleapiclient import discovery

client = discovery.build(
    "commentanalyzer",
    "v1alpha1",
    developerKey=LAYAHOST_API_KEY,
    # was: https://commentanalyzer.googleapis.com/$discovery/rest?version=v1alpha1
    discoveryServiceUrl="https://layahost.com/$discovery/rest?version=v1alpha1",
    static_discovery=False,
)

response = client.comments().analyze(body={
    "comment": {"text": "You are an idiot and nobody wants you here."},
    "requestedAttributes": {"TOXICITY": {}, "INSULT": {}},
}).execute()

print(response["attributeScores"]["TOXICITY"]["summaryScore"]["value"])
comments:analyze
curl "https://layahost.com/v1alpha1/comments:analyze?key=$LAYAHOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": {"text": "You are an idiot and nobody wants you here."},
    "requestedAttributes": {"TOXICITY": {}, "INSULT": {}},
    "doNotStore": true
  }'
comments:analyze
// was: https://commentanalyzer.googleapis.com/v1alpha1/comments:analyze
const res = await fetch(
  `https://layahost.com/v1alpha1/comments:analyze?key=${process.env.LAYAHOST_API_KEY}`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      comment: { text: "You are an idiot and nobody wants you here." },
      requestedAttributes: { TOXICITY: {}, INSULT: {} },
    }),
  },
);

const data = await res.json();
console.log(data.attributeScores.TOXICITY.summaryScore.value);
Response · 200
{
  "attributeScores": {
    "TOXICITY": {
      "spanScores": [{ "begin": 0, "end": 43, "score": { "value": 0.90833, "type": "PROBABILITY" } }],
      "summaryScore": { "value": 0.90833, "type": "PROBABILITY" }
    },
    "INSULT": {
      "spanScores": [{ "begin": 0, "end": 43, "score": { "value": 0.91625, "type": "PROBABILITY" } }],
      "summaryScore": { "value": 0.91625, "type": "PROBABILITY" }
    }
  },
  "languages": ["en"],
  "detectedLanguages": ["en"]
}

Attributes

AttributeMeaning
TOXICITYRude, disrespectful or unreasonable, likely to make people leave a discussion.
SEVERE_TOXICITYVery hateful, aggressive or abusive. Much less sensitive than TOXICITY to mild rudeness or casual swearing. The least accurate attribute; see Accuracy.
IDENTITY_ATTACKNegative or hateful towards people because of their identity, such as race, religion, gender or sexual orientation.
INSULTInsulting, inflammatory or demeaning towards a person or a group of people.
PROFANITYSwear words, curse words or other obscene language.
THREATAn intention to inflict pain, injury or violence on a person or group.
SEXUALLY_EXPLICITReferences to sexual acts, sexual body parts or other lewd content.

Perspective's other experimental attributes (FLIRTATION, ATTACK_ON_AUTHOR and the rest) answer 400. For other decisions, such as spam or your own categories, use POST /v1/decide.

What is supported

FieldBehaviour
comment.text, comment.typeUp to 32,000 characters. HTML is reduced to its text before scoring.
requestedAttributesAny of the attributes above. scoreThreshold leaves out scores below it. scoreType must be PROBABILITY or unset.
languagesUsed as a hint. Without it, the language is detected and returned in detectedLanguages.
doNotStoreHonoured. We never store comment text by default anyway; with doNotStore it is not stored even if you turned on text logging.
clientTokenEchoed back.
spanScoresOne span covering the whole comment, with the same score as summaryScore.
context, communityId, sessionId, spanAnnotationsAccepted and ignored.
comments:suggestscoreAccepted so feedback code keeps working. Free, and nothing is stored.

Accuracy

Every score is P(attribute) from Laya, calibrated against the Civil Comments dataset, where each label is the share of human raters who marked the comment, the same meaning as a Perspective probability. A score of 0.8 means that about 8 in 10 raters would call the comment toxic.

Held-out results on 580 comments from the Civil Comments test split, never used for tuning (2026-09-25):

AttributeROC AUCRank correlationMean abs. error
TOXICITY0.920.770.09
SEVERE_TOXICITY0.570.340.03
IDENTITY_ATTACK0.930.500.04
INSULT0.950.790.07
PROFANITY0.940.540.03
THREAT0.980.520.03
SEXUALLY_EXPLICIT0.880.420.02

ROC AUC is the chance that a comment raters flagged scores higher than one they did not (1.0 is perfect, 0.5 is a coin flip). Mean absolute error is the average distance between our score and the share of raters, on the natural mix of comments.

SEVERE_TOXICITY is barely better than chance at telling very hateful comments from merely toxic ones. It is there so requests that ask for it keep working; don't base decisions on it. Use TOXICITY together with INSULT, THREAT and IDENTITY_ATTACK instead.

PROFANITY combines the model with a list of common swear words: a match sets the score to at least 0.391, the average share of raters who called such comments obscene. Raters were conservative here, so a single swear word scores well below 0.5.

Perspective was trained on this kind of data and scores better on it. Expect different numbers for the same comment, and a different best threshold. Before switching, run a sample of your own comments through both and pick thresholds from ours. The model is English-first: other languages are scored, but these numbers are for English.

Pricing

Each requested attribute is one decision, $5 per million. A comment scored for TOXICITY alone costs $0.000005; all 7 attributes cost $0.000035. Failed requests and suggestscore are free. New accounts get $1 of credit when they verify their email.

Errors

Errors use Google's shape, so googleapiclient raises its usual HttpError:

Response · 400
{
  "error": {
    "code": 400,
    "message": "Unknown attribute: FLIRTATION. Supported: TOXICITY, SEVERE_TOXICITY, IDENTITY_ATTACK, INSULT, PROFANITY, THREAT, SEXUALLY_EXPLICIT.",
    "status": "INVALID_ARGUMENT"
  }
}
StatusWhen
400 INVALID_ARGUMENTInvalid body, unknown attribute, or an invalid API key.
401 UNAUTHENTICATEDNo API key.
402 FAILED_PRECONDITIONYour balance is too low. Top up on the Billing page.
429 RESOURCE_EXHAUSTEDRate limit (600 requests per minute per key) or a busy backend. Retry after retry-after seconds.
503 UNAVAILABLEBackend unavailable. Retry shortly.