Migrate Perspective attributeScores without rewriting your parser

Last updated:

⏳ 87 days left — the old API shuts down December 31, 2026.

Keep POST /v1alpha1/comments:analyze and the attributeScores fields your client consumes by changing the host and authentication to the Perspective-compatible Actor. Its scores are independent Detoxify model probabilities, not Google's scores, so validate moderation decisions on your own comments before carrying thresholds across.

Verified on October 4, 2026 against Google's sunset announcement and the Actor's current handlers, contract tests, README and pricing configuration.

Google says service remains active through December 31, 2026, with no direct migration support; new usage and quota requests stopped after February 2026. Start with the shutdown checklist to inventory callers and plan the switch. Google's announcement

Keep the request and response contract

The supported path takes a JSON body. Apify authenticates with your Apify token in Authorization: Bearer; an old Google ?key= value is accepted but ignored.

bash
curl --fail-with-body -X POST "https://dropin-apis--perspective-compat.apify.actor/v1alpha1/comments:analyze" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"comment":{"text":"Thanks for the explanation.","type":"PLAIN_TEXT"},"languages":["en"],"requestedAttributes":{"TOXICITY":{},"INSULT":{}},"spanAnnotations":true,"clientToken":"migration-sample-1"}'

This request asks for TOXICITY and INSULT; the parser can keep reading response.attributeScores.TOXICITY.summaryScore.value. Do not expect a particular numeric result from the example.

Field or behaviorCurrent compatibility implementation
comment.textNonempty plain text, at most 20 KiB of UTF-8 text; this is a byte limit, not 20,480 characters
requestedAttributesRequired nonempty map of supported attribute names to options
summaryScore{"value": number, "type": "PROBABILITY"} under each returned attribute
scoreTypeOnly PROBABILITY; other score types are rejected
scoreThresholdBelow-threshold attributes are omitted; a missing key is not a zero score
spanScoresWith spanAnnotations: true, sentence spans with Unicode code point begin and end offsets; otherwise one whole-text span
languages, detectedLanguagesReturned arrays; explicit language takes priority for selecting a supported scoring language
clientTokenA string is echoed, allowing request/result correlation
context, doNotStoreAccepted but not used for scoring; context does not change the model result

For example, read an optional attribute without turning an absent score into an allow decision:

js
const score = response.attributeScores?.TOXICITY?.summaryScore?.value;
if (typeof score !== 'number') {
  throw new Error('TOXICITY was not returned; inspect thresholds and request options');
}

Check attributes and languages before switching

AttributesSupported languages
TOXICITY, SEVERE_TOXICITY, IDENTITY_ATTACK, INSULT, PROFANITY, THREATEnglish, Spanish, French, Italian, Portuguese, Russian, Turkish (en, es, fr, it, pt, ru, tr)
SEXUALLY_EXPLICITEnglish only
The corresponding *_EXPERIMENTAL namesAliases of the base attributes, with the requested name retained in the response

Attributes such as SPAM, FLIRTATION, ATTACK_ON_AUTHOR and INFLAMMATORY are not scored. Known unsupported attributes and unsupported attribute/language combinations produce 400 INVALID_ARGUMENT with LANGUAGE_NOT_SUPPORTED_BY_ATTRIBUTE. Unknown names and invalid requests also return Google-style error envelopes; some error-detail names are best-effort compatibility, so test every error branch your client parses.

PROFANITY maps to Detoxify's obscene output. That is an approximation, and experimental names do not select Google's experimental models. The response shape does not establish model equivalence or calibration on your community.

Keep a discovery-based Python client

The Actor also serves a discovery document. If your client uses google-api-python-client, configure discovery and credentials as follows, keeping the analyze body above:

python
import os
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build

base = "https://dropin-apis--perspective-compat.apify.actor"
client = build(
    "commentanalyzer", "v1alpha1",
    discoveryServiceUrl=base + "/$discovery/rest?version={apiVersion}",
    static_discovery=False,
    credentials=Credentials(os.environ["APIFY_TOKEN"]),
)
result = client.comments().analyze(body={
    "comment": {"text": "Thanks for the explanation."},
    "requestedAttributes": {"TOXICITY": {}},
    "languages": ["en"],
}).execute()

This recipe follows the Actor's existing Python discovery-client check. Pin and test your actual client version before deploying; compatibility with one client does not prove every wrapper works.

Validate decisions, errors and cost

  1. Run a labeled sample with benign, harmful and borderline comments in every language you use. Compare allow, hide and review decisions while Google is still available.
  2. Test missing scores, empty text, unsupported attributes, UTF-8 byte limits and span offsets containing emoji. Adjust thresholds based on decisions, not numeric similarity.
  3. Check spending-cap handling: the Actor returns 429 RESOURCE_EXHAUSTED when its configured maximum charge is reached. Keep that path separate from a moderation result.
  4. Save the sample, selected thresholds and results with your deployment. DoneLatch's evidence workflow can help an agent record checks after its final change; the checks themselves still need to cover the migration.

The published event price is $0.001 per analyzed comment ($1 per 1,000), plus Apify's run-start charge. Validation failures have no comment-analyzed event. comments:suggestscore acknowledges feedback without storing it or training the model.

Try the Perspective-compatible Actor on Apify with a test sample before sending production traffic.

FAQ

Can I keep reading attributeScores.TOXICITY.summaryScore.value?

Yes for a returned supported attribute. The path and PROBABILITY shape remain available, but scoreThreshold can omit an attribute. Check that the key exists and revalidate decisions because the scores come from an independent model.

Can I reuse my Google API key?

No. Authenticate with an Apify token using a Bearer header or Apify's token query parameter. The Google key parameter is ignored.

Will the same threshold make the same moderation decision?

Not necessarily. Scores are independent Detoxify probabilities, and PROFANITY uses an obscene-label approximation. Tune thresholds against labeled comments and measure disagreement before switching.

Does suggesting scores update the model?

No. The compatible suggestscore endpoint acknowledges the request but does not persist feedback or train the model.