Evidence
Adverse media screening
Screen individuals and companies against the last 56 days of news coverage. A GDELT DOC 2.0 fetch and Claude Haiku 5.5 classification return, per article, the subject's role, the category of any wrongdoing alleged, two confidence scores and human-readable reasoning in a single API call.
Endpoint
POST /v1/adverse-media/screen
Data source
GDELT DOC 2.0
Search window
Last 56 days
LLM
Claude Haiku 5.5
From news article to risk category in ~2 seconds
The pipeline is four stages: query construction, article fetch, LLM classification, and response assembly.
- 1
Query construction
The entity name is escaped and wrapped in exact-phrase quotes, for people and companies alike. A tone<-3 filter keeps only negatively-toned articles. No keyword pre-filter is applied: whether an article is adverse, and in which category, is decided by the classifier against the full taxonomy.
- 2
GDELT DOC 2.0 fetch
Up to 75 articles from the last 56 days are fetched from GDELT with 3 retries and exponential backoff. Social media domains, press releases, and known content farms are filtered out. Results are cached in Redis for 24 hours.
- 3
LLM classification
Up to 20 articles per screening are classified in a single call with a fixed output schema. For each headline the classifier states the subject's role (accused, victim, only mentioned, or unclear), the category of any wrongdoing alleged against the subject, how clearly the headline describes an adverse act by the subject, and how sure it is that the article is about this subject rather than a namesake. Results are cached for 7 days.
- 4
Response assembly
An article is flagged only when the subject is accused, the category is adverse, the article is about this subject, and the headline clearly describes the act. A serious allegation (sanctions, crime, fraud, corruption) whose subject role is unclear is not flagged but is marked needsReview. The risk score comes from the most severe flagged incident, with a small capped increase for further distinct incidents; it maps to overallRisk (CLEAR → CRITICAL), and any flagged or needs-review article keeps it off CLEAR. Every assessed article is returned, flagged or not.
POST /v1/adverse-media/screen
Authenticate with a Bearer token and provide the entity name, type, and optional context. The full schema is in the OpenAPI reference.
curl -X POST https://api.verifex.tech/v1/adverse-media/screen \
-H "Authorization: Bearer vfx_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"entity": "Acme Corporation",
"entityType": "COMPANY",
"context": {
"country": "US",
"industry": "pharmaceuticals"
}
}'entitystringrequiredThe name of the individual or company to screen. Exact-phrase matched against article content.
entityTypeenumrequiredINDIVIDUAL or COMPANY. Recorded with the result and used by the classifier; the news query is the same for both.
context.countrystring (ISO-3166-1 alpha-2)Optional. Helps disambiguate entities with common names.
context.industrystringOptional. Provides additional disambiguation context for the LLM classifier.
Structured risk assessment with per-article detail
Every response includes an overall risk level, a numeric score, flagged article count, and full per-article results with confidence and reasoning.
{
"entityName": "Acme Corp",
"screenedAt": "2026-05-15T10:31:00Z",
"status": "COMPLETE",
"outcome": "review_required",
"headline": "Review required",
"searchWindowDays": 56,
"coverage": { "gdelt": "ok", "classifier": "ok", "complete": true },
"overallRisk": "MEDIUM",
"riskScore": 42,
"articlesFound": 20,
"articlesFlagged": 1,
"articlesNeedingReview": 0,
"cached": false,
"llmCost": 0.0008,
"promptVersion": "v2.0.0",
"classifierModel": "claude-haiku-5-5",
"quota": { "used": 12, "limit": 50, "remaining": 38, "resetsAt": "2026-06-01T00:00:00Z" },
"results": [
{
"title": "Acme Corp fined $2M for compliance violations",
"url": "https://example.com/article/123",
"domain": "reuters.com",
"publishedAt": "2026-05-15T10:30:00Z",
"riskCategory": "REGULATORY",
"subjectRole": "ACCUSED",
"sanctionsContext": null,
"adverseConfidence": 0.91,
"entityMatchConfidence": 0.95,
"confidence": 0.91,
"reasoning": "Article describes a regulatory fine directly tied to the entity.",
"isRelevant": true,
"needsReview": false,
"sentiment": 0
}
]
}Contract
Outcome states
status is COMPLETE or NO_RELEVANT_MEDIA the screen ran fully and a CLEAR risk is trustworthy. Any other status (UPSTREAM_UNAVAILABLE, CLASSIFIER_UNAVAILABLE, RATE_LIMITED, PARTIAL, ERROR) means coverage was incomplete: overallRisk is UNKNOWN, riskScore is null, and the check does not consume quota. Always branch on status before trusting a clear.statusstringOutcome: COMPLETE, NO_RELEVANT_MEDIA, PARTIAL, UPSTREAM_UNAVAILABLE, CLASSIFIER_UNAVAILABLE, RATE_LIMITED, or ERROR. Only the first two are trustworthy clear results.
headline / messagestringPlain-language result. A result with no adverse findings states the period searched, for example "No adverse news found in the last 56 days"; older coverage was not searched.
searchWindowDaysnumberHow many days back the news search reached. Nothing older was searched.
coverageobjectPer-provider state { gdelt, classifier, complete }. complete=true is the only condition under which a CLEAR is reliable.
overallRiskstringCLEAR, LOW, MEDIUM, HIGH, CRITICAL, or UNKNOWN when coverage was incomplete. Never CLEAR on a failure.
riskScorenumber (0-100) | nullSeverity of the most serious flagged incident, plus at most 10 for further distinct incidents; null when no assessment could be made.
articlesFoundnumberTotal articles retrieved from the news index for this entity.
articlesFlaggednumberArticles in which the subject is accused of wrongdoing in an adverse category.
articlesNeedingReviewnumberSerious allegations whose subject role the headline leaves unclear. Not flagged, not cleared: an analyst must review them.
results[].subjectRolestringACCUSED, VICTIM, MENTIONED or UNCLEAR: what the subject is in the article. Only ACCUSED can be flagged.
results[].sanctionsContextstring | nullFor SANCTIONS findings, what the news says about the subject's sanctions: DESIGNATION, DELISTING, EXTENSION, CHALLENGE, EVASION or OTHER. A delisting is still flagged, so the analyst sees the subject's sanctions history; null for other categories.
results[].adverseConfidence / entityMatchConfidencenumber (0-1)How clearly the headline describes an adverse act by the subject, and how sure the classifier is that it is this subject. Gates for flagging, not risk scores. confidence is deprecated and equals adverseConfidence.
cachedbooleanTrue if result was served from cache (Redis or PostgreSQL).
llmCostnumberEstimated USD cost of the LLM call. 0 when the classifier was not run.
classifierModelstringThe model that classified the articles.
quotaobjectUsed, limit, remaining, and resetsAt for the billing period. A failed screen does not consume quota.
Seven risk categories
The classifier assigns every article exactly one riskCategory: the type of adverse content. This is a different axis from overallRisk, which is the screening’s severity (UNKNOWN, CLEAR, LOW, MEDIUM, HIGH, CRITICAL). Six categories describe adverse content; NONE means none was found.
FRAUDSecurities fraud, wire fraud, accounting fraud, embezzlement, and Ponzi schemes.
CORRUPTIONBribery, kickbacks, extortion, and misuse of public office.
SANCTIONSSanctions evasion, trade restrictions, and embargo violations.
CRIMEViolent crime, drug trafficking, organized crime, arrests, and convictions.
REGULATORYEnforcement by a regulator or authority against the subject: a fine, a licence action, a consent order, a formal investigation. A data breach or outage alone is not REGULATORY.
REPUTATIONALA major scandal or significant misconduct by the subject not covered above.
OPERATIONALOutages, product incidents, layoffs, ordinary business news, customers targeted by third-party scams. Negative news, but not AML/KYC risk: shown, never flagged.
NONENot adverse for AML/KYC purposes, or about a different entity. Unrecognized values returned by the model are coerced to NONE.
Quota-gated by plan
Adverse media screening is available on Growth, Pro, and Enterprise plans. Free and Starter plans receive a 403 response.
Growth
- Quota
- 50 / month
- Overage
- €0.15 / check
Pro
- Quota
- 200 / month
- Overage
- €0.10 / check
Enterprise
- Quota
- Unlimited
- Overage
- Included
Quota is enforced atomically via Redis Lua script. Cache hits (Redis or PostgreSQL) consume zero quota. LLM failures also consume zero quota. Quota resets monthly with the billing cycle.
Expected errors and how to handle them
plan_access_deniedAdverse media screening is not available on your plan. Upgrade to Growth or higher.
https://verifex.tech/pricingquota_exceededYou've used all your adverse media checks this month. Upgrade or wait for the next billing cycle.
https://verifex.tech/pricingrate_limit_exceededYou've hit the rate limit. Slow down or upgrade your plan for higher limits.
https://verifex.tech/docsgdelt_unavailableThe GDELT news service is temporarily unavailable. Retry after a short delay.
https://verifex.tech/docs/adverse-mediavalidation_errorentity and entityType are required. entityType must be INDIVIDUAL or COMPANY.
https://verifex.tech/docs/adverse-mediaOfficial SDKs for common backends
The adverse media endpoint is accessible via the same SDKs as the rest of the Verifex API.
import { Verifex } from "verifex";
const verifex = new Verifex({ apiKey: "vfx_your_api_key" });
const result = await verifex.adverseMediaScreen({
entity: "Acme Corporation",
entityType: "COMPANY",
context: { country: "US" },
});
console.log(result.overallRisk, result.articlesFlagged);from verifex import VerifexClient
client = VerifexClient("vfx_your_api_key")
result = client.adverse_media_screen(
entity="Acme Corporation",
entity_type="COMPANY",
context={"country": "US"},
)
print(result.overall_risk, result.articles_flagged)import (
"context"
verifex "github.com/Verifex-dev/verifex-go-sdk"
)
client := verifex.New("vfx_your_api_key")
result, err := client.AdverseMediaScreen(context.Background(), verifex.AdverseMediaRequest{
Entity: "Acme Corporation",
EntityType: "COMPANY",
})
fmt.Println(result.OverallRisk, result.ArticlesFlagged)use verifex::{Verifex, AdverseMediaRequest};
let client = Verifex::new("vfx_your_api_key");
let result = client
.adverse_media_screen(AdverseMediaRequest {
entity: "Acme Corporation".into(),
entity_type: "COMPANY".into(),
..Default::default()
})
.await?;
println!("{} — {} flagged", result.overall_risk, result.articles_flagged);