Evidence
Adverse media screening
Screen individuals and companies against real-time news. A GDELT DOC 2.0 fetch and Claude Haiku 4.5 classification return seven risk categories, per-article confidence scores, and human-readable reasoning in a single API call.
Endpoint
POST /v1/adverse-media/screen
Data source
GDELT DOC 2.0
LLM
Claude Haiku 4.5
Cost
~$0.009 / screening
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 COMPANY types, a near10 clause adds investigation-related keywords (lawsuit, fined, fraud, corruption). A tone<-3 filter ensures only negatively-toned articles are considered.
- 2
GDELT DOC 2.0 fetch
Up to 75 articles 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
Articles are truncated to 20 per screening and sent to Claude Haiku 4.5 in a single batch call. The model evaluates relevance, entity match confidence, and assigns a risk category with reasoning. LLM results are cached for 7 days.
- 4
Response assembly
A weighted risk score is computed from article classifications. The score maps to an overallRisk (CLEAR → CRITICAL). Per-article results, quota metadata, and caching status are returned.
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.dev/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. Affects query construction — COMPANY queries include near10 investigation keywords.
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",
"coverage": { "gdelt": "ok", "classifier": "ok", "complete": true },
"overallRisk": "MEDIUM",
"riskScore": 42,
"articlesFound": 20,
"articlesFlagged": 3,
"cached": false,
"llmCost": 0.009,
"promptVersion": "v1.0.0",
"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",
"confidence": 0.91,
"reasoning": "Article describes a regulatory fine directly tied to the entity.",
"isRelevant": true,
"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.
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) | nullWeighted numeric score, or null when no assessment could be made.
articlesFoundnumberTotal articles retrieved from the news index for this entity.
articlesFlaggednumberCount of articles the classifier marked as relevant adverse coverage.
cachedbooleanTrue if result was served from cache (Redis or PostgreSQL).
llmCostnumberEstimated USD cost of the LLM call. 0 when the classifier was not run.
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.
REGULATORYSEC enforcement, FINRA actions, banking violations, and data breaches.
REPUTATIONALMajor scandals, boycotts, and significant public backlash.
NONENo negative information, or negative information 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.dev/pricingquota_exceededYou've used all your adverse media checks this month. Upgrade or wait for the next billing cycle.
https://verifex.dev/pricingrate_limit_exceededYou've hit the rate limit. Slow down or upgrade your plan for higher limits.
https://verifex.dev/docsgdelt_unavailableThe GDELT news service is temporarily unavailable. Retry after a short delay.
https://verifex.dev/docs/adverse-mediavalidation_errorentity and entityType are required. entityType must be INDIVIDUAL or COMPANY.
https://verifex.dev/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);