Megacall (Batch Operations)
Run multiple analysis types in a single API call for efficiency.
Why Use Megacall?
A single request is faster than multiple requests for each kind of analysis. It is also more cost effective: each check in a megacall is charged at 10% less than the same check called on its own, and a check that returns null is not charged. See Billing.
First-Pass Screening
Every check in a megacall (comment score, spam, relevance and dogwhistle) is classified first by a fast first-pass screen, run once for the whole request:
- A check the screen clears is answered straight from the screen, with no full assessment. It has the same shape as any other answer, but its finding lists and reasoning are empty, since there is nothing to explain.
- A comment the screen is confident is spam is answered as spam straight away. The response has
spam_checkfilled in and every other checknull, and only the spam check is charged. Spam is deleted, not coached, so there is nothing to gain from running the other checks. Itsreasoningsays it was classified by first-pass screening. - A comment the screen suspects is spam (
spam_check.suspected_spamistrue) is not spam. The other checks still run and are returned as usual, andspam_check.suggestiontells the commenter what to change. Send the comment back for revision with the suggestion, together with any other feedback, rather than deleting it. See Spam Detection. - A check the screen flags, or is unsure about, gets the full assessment, which explains the problem and, for the comment score, suggests improvements.
A megacall that asked for spam, relevance and comment score, for a comment answered as spam:
{
"comment_score": null,
"spam_check": {
"is_spam": true,
"confidence": 0.96,
"reasoning": "Classified as spam by first-pass screening.",
"suspected_spam": false,
"suggestion": ""
},
"relevance_check": null,
"dogwhistle_check": null,
"perspective": null
}
So a null check in a megacall response means either that it was not requested, or that the comment was answered as spam. When spam_check.is_spam is true, do not expect the other results. Only is_spam being true does this: when spam_check.suspected_spam is true, every requested check is returned. The spam answer is only short-circuited like this when spam was one of the checks requested.
Perspective-compatible scoring (run_perspective) is not part of this screen; it is scored on its own, as on the standalone endpoint.
For the comment score, your comment health levels also decide how cautious the screen is.
Selecting Which Checks to Run
Each analysis type is optional. Only request what you need to minimize latency and cost.
- TypeScript
- Python
- PHP
- REST API
Pass an options object with boolean flags to select analyses:
| Parameter | Analysis |
|---|---|
includeSpam: true | Spam detection |
includeRelevance: true | On-topic and banned topics check |
includeCommentScore: true | Full quality analysis |
includeDogwhistle: true | Coded language detection |
includePerspectiveAnalyzeComment: true | Perspective-compatible analyzeComment scoring |
Results are null for any analysis not requested, and for every analysis except spam when the comment is answered as spam.
Use boolean parameters to select analyses:
| Parameter | Analysis |
|---|---|
include_spam=True | Spam detection |
include_relevance=True | On-topic and banned topics check |
include_comment_score=True | Full quality analysis |
include_dogwhistle=True | Coded language detection |
include_perspective_analyze_comment=True | Perspective-compatible analyze_comment scoring |
Results are None for any analysis not requested, and for every analysis except spam when the comment is answered as spam.
Pass an array of service names as the third parameter:
| Service | Analysis |
|---|---|
'spam' | Spam detection |
'relevance' | On-topic and banned topics check |
'commentscore' | Full quality analysis |
'dogwhistle' | Coded language detection |
'perspectiveAnalyzeComment' | Perspective-compatible analyzeComment scoring |
$client->megacall($comment, $articleId, ['spam', 'relevance']);
Results are null for any service not included, and for every service except spam when the comment is answered as spam.
Use boolean fields in the request body:
| Field | Analysis |
|---|---|
run_spam_check | Spam detection |
run_relevance_check | On-topic and banned topics check |
run_comment_score | Full quality analysis |
run_dogwhistle_check | Coded language detection |
run_perspective | Perspective-compatible analyzeComment scoring |
Response fields are null for any check not requested, and for every check except spam_check when the comment is answered as spam.
Basic Usage
- TypeScript
- Python
- PHP
- REST API
const result = await client.megacall({
comment: "This is a test comment",
articleId: articleId,
includeSpam: true,
includeRelevance: true,
includeCommentScore: true,
includeDogwhistle: true,
});
// Access results (null if not requested)
if (result.spam_check) {
console.log(`Is spam: ${result.spam_check.is_spam}`);
}
if (result.relevance_check) {
console.log(`On topic: ${result.relevance_check.on_topic.on_topic}`);
}
if (result.comment_score) {
console.log(`Comment health: ${result.comment_score.overall_score}`);
}
if (result.dogwhistle_check) {
console.log(`Dogwhistles detected: ${result.dogwhistle_check.detection.dogwhistles_detected}`);
}
- Blocking Client
- Async Client
result = client.megacall(
"This is a test comment",
article_id,
include_spam=True,
include_relevance=True,
include_comment_score=True,
include_dogwhistle=True
)
# Access results (None if not requested)
if result.spam:
print(f"Is spam: {result.spam.is_spam}")
if result.relevance:
print(f"On topic: {result.relevance.on_topic.on_topic}")
if result.comment_score:
print(f"Comment health: {result.comment_score.overall_score}")
if result.dogwhistle:
print(f"Dogwhistles detected: {result.dogwhistle.detection.dogwhistles_detected}")
result = await client.megacall(
"This is a test comment",
article_id,
include_spam=True,
include_relevance=True,
include_comment_score=True,
include_dogwhistle=True
)
# Access results (None if not requested)
if result.spam:
print(f"Is spam: {result.spam.is_spam}")
if result.relevance:
print(f"On topic: {result.relevance.on_topic.on_topic}")
if result.comment_score:
print(f"Comment health: {result.comment_score.overall_score}")
if result.dogwhistle:
print(f"Dogwhistles detected: {result.dogwhistle.detection.dogwhistles_detected}")
$client->megacall(
"This is a test comment",
$articleId,
['spam', 'relevance', 'commentscore', 'dogwhistle']
)->then(function ($result) {
if ($result->spam) {
echo "Is spam: " . ($result->spam->isSpam ? 'Yes' : 'No') . "\n";
}
if ($result->relevance) {
echo "On topic: " . ($result->relevance->onTopic->onTopic ? 'Yes' : 'No') . "\n";
}
if ($result->commentScore) {
echo "Comment health: " . $result->commentScore->overallScore . "\n";
}
if ($result->dogwhistle) {
echo "Dogwhistles: " . ($result->dogwhistle->detection->dogwhistlesDetected ? 'Yes' : 'No') . "\n";
}
});
$client->run();
curl -X POST https://app.respectify.ai/v0.2/megacall \
-H "X-User-Email: your@email.com" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"comment": "This is a test comment",
"article_context_id": "550e8400-e29b-41d4-a716-446655440000",
"run_spam_check": true,
"run_relevance_check": true,
"run_comment_score": true,
"run_dogwhistle_check": true
}'
Response:
{
"comment_score": {
"logical_fallacies": [],
"objectionable_phrases": [],
"negative_tone_phrases": [],
"appears_low_effort": false,
"overall_score": 4,
"toxicity_score": 0.05,
"toxicity_explanation": "",
"exceeds_tolerance": false,
"health_score": 4.47,
"health_items": {
"threat": { "score": 5, "minimum": 5, "is_acceptable": true },
"identity_attack": { "score": 5, "minimum": 5, "is_acceptable": true },
"sexual_content": { "score": 5, "minimum": 5, "is_acceptable": true },
"harassment": { "score": 5, "minimum": 5, "is_acceptable": true },
"insult": { "score": 5, "minimum": 4, "is_acceptable": true },
"profanity": { "score": 5, "minimum": 4, "is_acceptable": true },
"dismissive_tone": { "score": 5, "minimum": 4, "is_acceptable": true },
"logical_fallacy": { "score": 5, "minimum": 4, "is_acceptable": true },
"low_effort": { "score": 5, "minimum": 4, "is_acceptable": true },
"quality": { "score": 4, "minimum": 3, "is_acceptable": true }
},
"tolerances": {
"threat": "standard",
"identity_attack": "standard",
"sexual_content": "standard",
"harassment": "standard",
"insult": "standard",
"profanity": "standard",
"dismissive_tone": "standard",
"logical_fallacy": "standard",
"low_effort": "standard",
"quality": "standard"
}
},
"spam_check": {
"reasoning": "",
"confidence": 0.98,
"is_spam": false,
"suspected_spam": false,
"suggestion": ""
},
"relevance_check": {
"on_topic": {
"reasoning": "The comment directly addresses the main topic of the article.",
"on_topic": true,
"confidence": 0.95
},
"banned_topics": {
"reasoning": "No banned topics were detected in this comment.",
"banned_topics": [],
"quantity_on_banned_topics": 0,
"confidence": 0.99
}
},
"dogwhistle_check": {
"detection": {
"reasoning": "No dogwhistles detected in this comment.",
"dogwhistles_detected": false,
"confidence": 0.95
}
}
}
With Additional Parameters
- TypeScript
- Python
- PHP
- REST API
const result = await client.megacall({
comment: "Comment text here",
articleId: articleId,
includeSpam: true,
includeRelevance: true,
includeDogwhistle: true,
bannedTopics: ["politics", "religion"],
sensitiveTopics: ["controversial topic"],
dogwhistleExamples: ["known coded phrase"],
});
- Blocking Client
- Async Client
result = client.megacall(
"Comment text here",
article_id,
include_spam=True,
include_relevance=True,
include_dogwhistle=True,
banned_topics=['politics', 'religion'],
sensitive_topics=['controversial topic'],
dogwhistle_examples=['known coded phrase']
)
result = await client.megacall(
"Comment text here",
article_id,
include_spam=True,
include_relevance=True,
include_dogwhistle=True,
banned_topics=['politics', 'religion'],
sensitive_topics=['controversial topic'],
dogwhistle_examples=['known coded phrase']
)
$client->megacall(
"Comment text here",
$articleId,
['spam', 'relevance', 'dogwhistle'],
['politics', 'religion'], // banned topics
null, // reply_to_comment
['controversial topic'], // sensitive topics
['known coded phrase'] // dogwhistle examples
);
curl -X POST https://app.respectify.ai/v0.2/megacall \
-H "X-User-Email: your@email.com" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"comment": "Comment text here",
"article_context_id": "550e8400-e29b-41d4-a716-446655440000",
"run_spam_check": true,
"run_relevance_check": true,
"run_dogwhistle_check": true,
"banned_topics": ["politics", "religion"],
"sensitive_topics": ["controversial topic"],
"dogwhistle_examples": ["known coded phrase"]
}'
With Comment Health Tolerances
The comment score in a megacall is judged under the comment health levels you pass in tolerances, like a standalone comment score. Any item you leave out is standard; nothing is saved on your API key. Tolerances are used only when the comment score is requested.
Act on comment_score.exceeds_tolerance rather than comment_score.toxicity_score alone.
- TypeScript
- Python
- PHP
- REST API
const result = await client.megacall({
comment: "Comment text here",
articleId: articleId,
includeCommentScore: true,
tolerances: { profanity: "relaxed", quality: "strict", threat: "zero_tolerance" },
});
if (result.comment_score?.exceeds_tolerance) {
// Coach the commenter
}
- Blocking Client
- Async Client
from respectify import ModerationTolerances, ToleranceLevel
result = client.megacall(
"Comment text here",
article_id,
include_comment_score=True,
tolerances=ModerationTolerances(
profanity=ToleranceLevel.RELAXED,
quality=ToleranceLevel.STRICT,
threat=ToleranceLevel.ZERO_TOLERANCE,
),
)
if result.comment_score and result.comment_score.exceeds_tolerance:
... # Coach the commenter
from respectify import ModerationTolerances, ToleranceLevel
result = await client.megacall(
"Comment text here",
article_id,
include_comment_score=True,
tolerances=ModerationTolerances(
profanity=ToleranceLevel.RELAXED,
quality=ToleranceLevel.STRICT,
threat=ToleranceLevel.ZERO_TOLERANCE,
),
)
if result.comment_score and result.comment_score.exceeds_tolerance:
... # Coach the commenter
$tolerances is the last parameter of megacall():
use Respectify\Moderation\ModerationCategory;
use Respectify\Moderation\ModerationTolerances;
use Respectify\Moderation\ToleranceLevel;
$tolerances = new ModerationTolerances([
ModerationCategory::PROFANITY => ToleranceLevel::RELAXED,
'quality' => ToleranceLevel::STRICT,
ModerationCategory::THREAT => ToleranceLevel::ZERO_TOLERANCE,
]);
$client->megacall(
"Comment text here",
$articleId,
['commentscore'],
null, // banned topics
null, // reply_to_comment
null, // sensitive topics
null, // dogwhistle examples
$tolerances
)->then(function ($result) {
if ($result->commentScore && $result->commentScore->exceedsTolerance) {
// Coach the commenter
}
});
$client->run();
Add a tolerances object to the request body. It applies only when run_comment_score is true.
curl -X POST https://app.respectify.ai/v0.2/megacall \
-H "X-User-Email: your@email.com" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"comment": "Comment text here",
"article_context_id": "550e8400-e29b-41d4-a716-446655440000",
"run_comment_score": true,
"tolerances": {
"profanity": "relaxed",
"quality": "strict",
"threat": "zero_tolerance"
}
}'
Invalid tolerances (for example, permissive, relaxed or off on a floored category such as threat) are rejected with HTTP 400. See Comment Health → Errors.
Handling Stale Article Contexts
Article context IDs are tied to the API key that created them. If you rotate your key, previously stored IDs no longer resolve, and megacall raises ArticleContextNotFoundError (or ArticleContextNotFoundException in PHP). Regenerate the context, store the new ID, and retry the call.
- TypeScript
- Python
- PHP
- REST API
import { RespectifyClient, ArticleContextNotFoundError } from "@respectify/client";
async function evaluateWithRetry(comment: string, articleUrl: string, articleId: string) {
try {
return await client.megacall({ comment, articleId, includeCommentScore: true });
} catch (err) {
if (!(err instanceof ArticleContextNotFoundError)) throw err;
const fresh = await client.initTopicFromUrl(articleUrl);
// Store fresh.article_id so later requests reuse it.
return await client.megacall({
comment,
articleId: fresh.article_id,
includeCommentScore: true,
});
}
}
from respectify import RespectifyClient, ArticleContextNotFoundError
def evaluate_with_retry(client, comment, article_url, article_id):
try:
return client.megacall(comment, article_id, include_comment_score=True)
except ArticleContextNotFoundError:
fresh = client.init_topic_from_url(article_url)
# Store fresh.article_id so later requests reuse it.
return client.megacall(comment, fresh.article_id, include_comment_score=True)
use Respectify\Exceptions\ArticleContextNotFoundException;
$client->megacall($comment, $articleId, ['commentscore'])->then(
function ($result) { /* use $result */ },
function (\Throwable $e) use ($client, $articleUrl, $comment) {
if (!($e instanceof ArticleContextNotFoundException)) {
throw $e;
}
// Regenerate, store the new ID, retry.
return $client->initTopicFromUrl($articleUrl)->then(
fn ($freshId) => $client->megacall($comment, $freshId, ['commentscore'])
);
}
);
The API returns a 400 with this body:
{
"error": "Article Context Not Found",
"message": "Article context not found - it may need to be regenerated",
"code": 400
}
Match on "Article context not found" in the message field to distinguish this from other 400s. To recover, POST /inittopic with text or url, store the new article_id, and retry the original call.
Avoid retry loops — initTopic is a paid call. Retry once, store the new ID, and let later requests reuse it.