Online Marketplace Trust & Safety
Screen sellers, scan listings, and protect buyers on peer-to-peer marketplaces.
Peer-to-peer marketplaces like Poshmark, Mercari, OfferUp, and Facebook Marketplace face a constant battle against fraudulent sellers. Fake accounts, counterfeit listings, off-platform payment scams, and phishing links in listings cost these platforms millions in chargebacks, user trust erosion, and moderation overhead. The ScamVerify™ API provides automated seller screening, listing content scanning, and buyer protection signals.
The Problem
Marketplace fraud operates at scale. A single bad actor can create dozens of accounts using disposable VoIP numbers, list items they do not own, collect payments, and disappear. By the time buyer complaints arrive, the seller has moved on to new accounts. Traditional moderation catches these accounts reactively. ScamVerify™ helps you catch them proactively.
Common Fraud Patterns
- Multi-accounting: One person creates 10+ seller accounts using cheap VoIP numbers, rotating through them as accounts get banned
- Off-platform payment scams: Sellers post phone numbers or payment links in listing descriptions to move transactions off-platform where buyers have no protection
- Phishing listings: Product pages that link to external sites mimicking the marketplace's checkout, harvesting payment credentials
- Counterfeit storefronts: Sellers create listings with URLs to fake "brand authorized" sites as proof of authenticity
How ScamVerify™ Helps
- Phone lookup at seller registration catches disposable VoIP numbers before accounts are created
- URL scanning in listing content detects phishing links, malware domains, and suspicious external sites
- Text analysis on listing descriptions flags social engineering patterns and off-platform payment solicitations
- Buyer-side phone checks verify seller contact numbers shared through messaging
Code Example: Seller Registration Verification
class MarketplaceTrustEngine {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://scamverify.ai/api/v1';
}
async callApi(endpoint, body) {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
}
async screenSellerRegistration(phoneNumber) {
const result = await this.callApi('/phone/lookup', {
phone_number: phoneNumber,
});
const signals = {
riskScore: result.risk_score,
lineType: result.signals.line_type,
carrier: result.signals.carrier,
ftcComplaints: result.signals.ftc_complaints,
robocall: result.signals.robocall_detected,
};
// Tiered seller verification
if (result.risk_score >= 60 || result.signals.robocall_detected) {
return {
decision: 'REJECT',
reason: 'Phone number does not meet seller verification requirements.',
signals,
internalReason: result.explanation,
};
}
if (
result.signals.line_type === 'voip' &&
result.risk_score >= 25
) {
return {
decision: 'ENHANCED_VERIFICATION',
reason: 'Additional identity verification required to sell.',
nextSteps: ['government_id', 'selfie_match', 'bank_verification'],
signals,
};
}
if (result.risk_score >= 30 || result.signals.ftc_complaints > 0) {
return {
decision: 'PROBATION',
reason: 'Account approved with selling limits.',
restrictions: {
maxListings: 5,
maxTransactionValue: 100,
payoutHoldDays: 7,
},
signals,
};
}
return {
decision: 'APPROVED',
signals,
restrictions: null,
};
}
async scanListing(listing) {
const issues = [];
// Extract and check URLs in listing description
const urlRegex = /https?:\/\/[^\s)>]+/gi;
const urls = listing.description.match(urlRegex) || [];
for (const url of urls) {
const check = await this.callApi('/url/lookup', { url });
if (check.risk_score >= 25) {
issues.push({
type: 'risky_url',
value: url,
riskScore: check.risk_score,
verdict: check.verdict,
explanation: check.explanation,
});
}
}
// Extract and check phone numbers in listing description
const phoneRegex = /\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/g;
const phones = listing.description.match(phoneRegex) || [];
// Phone numbers in listings are suspicious on their own
// (sellers should use in-app messaging)
if (phones.length > 0) {
for (const phone of phones) {
const check = await this.callApi('/phone/lookup', {
phone_number: phone,
});
issues.push({
type: 'phone_in_listing',
value: phone,
riskScore: check.risk_score,
lineType: check.signals.line_type,
// Any phone in a listing is concerning, high-risk ones doubly so
severity: check.risk_score >= 30 ? 'high' : 'medium',
});
}
}
// Check for off-platform payment solicitation patterns
const offPlatformPatterns = /\b(venmo|zelle|cashapp|cash app|paypal\.me|wire transfer|western union|send money)\b/i;
if (offPlatformPatterns.test(listing.description)) {
issues.push({
type: 'off_platform_payment',
severity: 'high',
detail: 'Listing mentions off-platform payment methods.',
});
}
const hasHighSeverity = issues.some(
i => i.severity === 'high' || i.riskScore >= 50
);
const hasMediumSeverity = issues.some(
i => i.severity === 'medium' || (i.riskScore >= 25 && i.riskScore < 50)
);
return {
listingId: listing.id,
approved: issues.length === 0,
action: hasHighSeverity
? 'REMOVE_LISTING'
: hasMediumSeverity
? 'HOLD_FOR_REVIEW'
: 'APPROVE',
issues,
};
}
}
// Integration with your seller registration
const trust = new MarketplaceTrustEngine(process.env.SCAMVERIFY_API_KEY);
app.post('/api/sellers/register', async (req, res) => {
const { phone, email, name } = req.body;
const screening = await trust.screenSellerRegistration(phone);
switch (screening.decision) {
case 'REJECT':
return res.status(400).json({
error: screening.reason,
code: 'SELLER_VERIFICATION_FAILED',
});
case 'ENHANCED_VERIFICATION':
const pendingAccount = await createSellerAccount({
phone, email, name,
status: 'pending_verification',
requiredSteps: screening.nextSteps,
});
return res.json({
accountId: pendingAccount.id,
status: 'pending_verification',
nextSteps: screening.nextSteps,
});
case 'PROBATION':
const restricted = await createSellerAccount({
phone, email, name,
status: 'active_restricted',
restrictions: screening.restrictions,
});
return res.json({
accountId: restricted.id,
status: 'active',
restrictions: screening.restrictions,
});
case 'APPROVED':
const account = await createSellerAccount({
phone, email, name,
status: 'active',
});
return res.json({
accountId: account.id,
status: 'active',
});
}
});
// Integration with listing creation
app.post('/api/listings/create', async (req, res) => {
const listing = req.body;
const scan = await trust.scanListing(listing);
if (scan.action === 'REMOVE_LISTING') {
await flagSeller(listing.sellerId, scan.issues);
return res.status(400).json({
error: 'This listing could not be published. Please review our seller guidelines.',
});
}
if (scan.action === 'HOLD_FOR_REVIEW') {
await createListing({ ...listing, status: 'pending_review' });
return res.json({ status: 'pending_review' });
}
await createListing({ ...listing, status: 'active' });
return res.json({ status: 'active' });
});import re
import requests
import os
class MarketplaceTrustEngine:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://scamverify.ai/api/v1"
def _call_api(self, endpoint: str, body: dict) -> dict:
response = requests.post(
f"{self.base_url}{endpoint}",
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
},
json=body,
)
response.raise_for_status()
return response.json()
def screen_seller(self, phone_number: str) -> dict:
result = self._call_api("/phone/lookup", {"phone_number": phone_number})
if result["risk_score"] >= 60 or result["signals"]["robocall_detected"]:
return {"decision": "REJECT", "risk_score": result["risk_score"]}
if (
result["signals"]["line_type"] == "voip"
and result["risk_score"] >= 25
):
return {
"decision": "ENHANCED_VERIFICATION",
"risk_score": result["risk_score"],
"next_steps": ["government_id", "selfie_match"],
}
if result["risk_score"] >= 30 or result["signals"]["ftc_complaints"] > 0:
return {
"decision": "PROBATION",
"risk_score": result["risk_score"],
"restrictions": {
"max_listings": 5,
"max_transaction_value": 100,
"payout_hold_days": 7,
},
}
return {"decision": "APPROVED", "risk_score": result["risk_score"]}
def scan_listing(self, description: str, listing_id: str) -> dict:
issues = []
urls = re.findall(r"https?://[^\s)>]+", description, re.IGNORECASE)
for url in urls:
check = self._call_api("/url/lookup", {"url": url})
if check["risk_score"] >= 25:
issues.append({
"type": "risky_url",
"url": url,
"risk_score": check["risk_score"],
})
phones = re.findall(r"\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}", description)
for phone in phones:
issues.append({"type": "phone_in_listing", "phone": phone})
off_platform = re.search(
r"\b(venmo|zelle|cashapp|paypal\.me|wire transfer)\b",
description,
re.IGNORECASE,
)
if off_platform:
issues.append({"type": "off_platform_payment", "severity": "high"})
action = "APPROVE"
if any(i.get("severity") == "high" or i.get("risk_score", 0) >= 50 for i in issues):
action = "REMOVE"
elif issues:
action = "REVIEW"
return {"listing_id": listing_id, "action": action, "issues": issues}Seller Trust Tiers
| Tier | Entry Criteria | Selling Limits | Payout Schedule |
|---|---|---|---|
| Rejected | risk_score >= 60 or robocall detected | Cannot sell | N/A |
| Enhanced Verification | VoIP + risk_score >= 25 | Cannot sell until ID verified | N/A |
| Probation | risk_score: 30-59 or FTC complaints | 5 listings, $100 max per item | 7-day hold |
| Standard | risk_score < 30, clean signals | Unlimited listings | Standard payout |
| Trusted | 90+ days clean history, 50+ completed sales | Premium features | Expedited payout |
Listing Content Scanning Signals
| Signal | What It Means | Action |
|---|---|---|
URL with risk_score >= 50 | Link to a known phishing or malware domain | Remove listing, flag seller account |
URL with risk_score: 25-49 | Link to a suspicious or newly registered domain | Hold for manual review |
| Phone number in listing text | Seller attempting to move communication off-platform | Hold for review, warn seller |
| Off-platform payment mention | Direct violation of marketplace policies on most platforms | Remove listing, issue seller warning |
| Multiple flagged URLs | Pattern of malicious linking, likely a scam operation | Suspend seller account |
Buyer Protection: Messaging Scans
When buyers and sellers communicate through in-app messaging, scan messages that contain URLs or phone numbers.
app.post('/api/messages/send', async (req, res) => {
const { senderId, recipientId, text } = req.body;
// Check for URLs in messages
const urls = text.match(/https?:\/\/[^\s)>]+/gi) || [];
for (const url of urls) {
const check = await trust.callApi('/url/lookup', { url });
if (check.risk_score >= 40) {
await blockMessage(senderId, recipientId, {
reason: 'Message contains a potentially unsafe link.',
url,
riskScore: check.risk_score,
});
return res.status(400).json({
error: 'This message contains a link that did not pass our safety check.',
});
}
}
await deliverMessage(senderId, recipientId, text);
return res.json({ status: 'sent' });
});Seller feedback loop. When a seller is flagged by ScamVerify™ and later confirmed as fraudulent through buyer reports, log that correlation. Over time, this data helps you fine-tune your risk thresholds for your specific marketplace category.
Scaling Moderation
For marketplaces with thousands of new listings per day, automated scanning replaces the first layer of human moderation.
- Auto-approve listings with no flagged content (typically 85-90% of listings)
- Auto-reject listings with confirmed malicious URLs or critical signals (typically 1-3%)
- Queue for human review listings with medium-risk signals (typically 7-12%)
This reduces human moderation workload by 85-90% while catching more fraud than manual review alone.
Best Practices
- Screen at registration, not just at listing. Catching bad accounts before they list anything is cheaper than removing listings and handling buyer complaints.
- Use graduated responses. Not every VoIP number is fraud. Probationary periods with selling limits let legitimate sellers prove themselves while containing the damage from fraudulent ones.
- Scan listing content on creation and on edit. Fraudsters sometimes create clean listings to pass initial review, then edit in malicious content later.
- Protect your messaging channel. Off-platform payment scams start in marketplace messaging. Scanning messages for URLs and phone numbers blocks the most common attack vector.
Getting Started
- Create an API key and set up your account
- Follow the Quickstart guide to make your first lookup
- Review the Phone Lookup API and URL Verification API reference docs
- Integrate seller registration screening first, then expand to listing and messaging scans