Verifex

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. 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. 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. 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. 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"
    }
  }'
entitystringrequired

The name of the individual or company to screen. Exact-phrase matched against article content.

entityTypeenumrequired

INDIVIDUAL 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.industrystring

Optional. 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.

json
{
  "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

An upstream or classifier failure is never a clearance. When 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.
statusstring

Outcome: COMPLETE, NO_RELEVANT_MEDIA, PARTIAL, UPSTREAM_UNAVAILABLE, CLASSIFIER_UNAVAILABLE, RATE_LIMITED, or ERROR. Only the first two are trustworthy clear results.

headline / messagestring

Plain-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.

searchWindowDaysnumber

How many days back the news search reached. Nothing older was searched.

coverageobject

Per-provider state { gdelt, classifier, complete }. complete=true is the only condition under which a CLEAR is reliable.

overallRiskstring

CLEAR, LOW, MEDIUM, HIGH, CRITICAL, or UNKNOWN when coverage was incomplete. Never CLEAR on a failure.

riskScorenumber (0-100) | null

Severity of the most serious flagged incident, plus at most 10 for further distinct incidents; null when no assessment could be made.

articlesFoundnumber

Total articles retrieved from the news index for this entity.

articlesFlaggednumber

Articles in which the subject is accused of wrongdoing in an adverse category.

articlesNeedingReviewnumber

Serious allegations whose subject role the headline leaves unclear. Not flagged, not cleared: an analyst must review them.

results[].subjectRolestring

ACCUSED, VICTIM, MENTIONED or UNCLEAR: what the subject is in the article. Only ACCUSED can be flagged.

results[].sanctionsContextstring | null

For 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.

cachedboolean

True if result was served from cache (Redis or PostgreSQL).

llmCostnumber

Estimated USD cost of the LLM call. 0 when the classifier was not run.

classifierModelstring

The model that classified the articles.

quotaobject

Used, 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.

FRAUD

Securities fraud, wire fraud, accounting fraud, embezzlement, and Ponzi schemes.

CORRUPTION

Bribery, kickbacks, extortion, and misuse of public office.

SANCTIONS

Sanctions evasion, trade restrictions, and embargo violations.

CRIME

Violent crime, drug trafficking, organized crime, arrests, and convictions.

REGULATORY

Enforcement 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.

REPUTATIONAL

A major scandal or significant misconduct by the subject not covered above.

OPERATIONAL

Outages, product incidents, layoffs, ordinary business news, customers targeted by third-party scams. Negative news, but not AML/KYC risk: shown, never flagged.

NONE

Not 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

403plan_access_denied

Adverse media screening is not available on your plan. Upgrade to Growth or higher.

https://verifex.tech/pricing
429quota_exceeded

You've used all your adverse media checks this month. Upgrade or wait for the next billing cycle.

https://verifex.tech/pricing
429rate_limit_exceeded

You've hit the rate limit. Slow down or upgrade your plan for higher limits.

https://verifex.tech/docs
503gdelt_unavailable

The GDELT news service is temporarily unavailable. Retry after a short delay.

https://verifex.tech/docs/adverse-media
400validation_error

entity and entityType are required. entityType must be INDIVIDUAL or COMPANY.

https://verifex.tech/docs/adverse-media

Official SDKs for common backends

The adverse media endpoint is accessible via the same SDKs as the rest of the Verifex API.

Node.js
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);
Python
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)
Go
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)
Rust
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);