VouchHound API
Real-time merchant trust scoring. Cross-checks five independent signals — domain history, web presence, certificate transparency, payment integration, and content authenticity — to score how trustworthy a store actually is.
The API is built for AI agent platforms that need to verify merchants in milliseconds before transacting on a user's behalf, fraud and trust teams at marketplaces who want to triage risky listings, and integrators building consumer-facing shopping tools.
One request, one composite score. No SDK to learn, no model to host, no calibration to maintain.
Authentication
All API requests are authenticated with an API key passed as a Bearer token in the Authorization header.
Authorization: Bearer vh_test_YOUR_KEY_HERE
Keys are scoped to a single account and tier. They look like vh_test_… and are shown to you exactly once at creation — store them in a secrets manager, not source control.
Quickstart
The simplest possible call — paste your key in, run it, get a trust score back.
curl -X POST https://api.vouchhound.com/v1/score \
-H "Authorization: Bearer vh_test_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"url": "allbirds.com"}'
Returns a JSON envelope with the composite trust score, a verdict, and a breakdown of which signals contributed. Full schema below.
POST /v1/score
Run all signals against a single URL and return a composite trust score with a breakdown of contributions.
Request body
| Field | Type | Description |
|---|---|---|
| url | string, required | The store URL to score. Bare domains, full URLs, with or without https://, with or without paths — all work. Maximum 500 characters. |
Response body
Every response is wrapped in a stable envelope. The fields you'll most often care about live under data.
| Field | Type | Description |
|---|---|---|
| version | string | API version that produced this response. Currently always "v1". |
| request_id | string | Unique identifier for this request (echoes the X-Request-Id header). Include this when reporting issues. |
| computed_at | string (ISO 8601) | When the scoring finished, in UTC. |
| data.url | string | The URL that was scored, echoed from the request. |
| data.trust_score | integer 0–100 | Composite trust score. Higher means more trustworthy. |
| data.verdict | enum | One of trusted, caution, high_risk, insufficient_data. |
| data.confidence | number 0–1 | How much of the underlying signal evidence we successfully gathered. Lower confidence means more signals were unavailable. |
| data.top_positive_signals | array<string> | Human-readable reasons supporting trust, ordered by contribution. |
| data.top_negative_signals | array<string> | Human-readable reasons supporting risk, ordered by contribution. |
| data.signal_data | object | Raw output from each underlying signal, keyed by signal name. Useful for clients that want to render signal-level detail. |
| data.errors | array<string> | Signals that failed to gather data this request. Missing signals lower confidence but never penalize the score. |
Example request
POST /v1/score HTTP/1.1
Host: api.vouchhound.com
Authorization: Bearer vh_test_YOUR_KEY_HERE
Content-Type: application/json
{"url": "allbirds.com"}
Example response
{
"version": "v1",
"request_id": "req_7498cd8ea636474db24226dea38b6b98",
"computed_at": "2026-05-27T01:44:39Z",
"data": {
"url": "allbirds.com",
"trust_score": 90,
"verdict": "trusted",
"confidence": 0.9,
"top_positive_signals": [
"Established e-commerce setup on Shopify with: Apple Pay, Shopify Payments",
"Site has been crawled since 2000-06-20 (25.9y ago)",
"About page reads as human-written (15/100)",
"Domain registered 24.4y ago"
],
"top_negative_signals": [],
"signal_data": {
"domain_age": { "creation_date": "2002-01-09", "age_years": 24.4 },
"wayback_first_seen": { "first_seen": "2000-06-20", "first_seen_years_ago": 25.9 },
"payment_providers": {
"platform": "Shopify",
"providers_detected": ["Apple Pay", "Shopify Payments"],
"checkout_accessible": true
},
"about_page_ai_detection": {
"verdict": "likely_human",
"ai_generation_likelihood": 15
}
},
"errors": []
}
}
Code examples
Complete, paste-and-run examples in three languages. Replace the API key placeholder with the one you got from hello@vouchhound.com.
curl -X POST https://api.vouchhound.com/v1/score \
-H "Authorization: Bearer vh_test_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"url": "allbirds.com"}' \
| jq .data.trust_score
import os
import requests
API_KEY = os.environ['VOUCHHOUND_API_KEY'] # or paste directly for testing
resp = requests.post(
'https://api.vouchhound.com/v1/score',
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json',
},
json={'url': 'allbirds.com'},
timeout=30,
)
resp.raise_for_status()
body = resp.json()
data = body['data']
print(f"{data['url']}: {data['trust_score']}/100 — {data['verdict']}")
for signal in data['top_positive_signals']:
print(f' + {signal}')
// Node.js 18+ (native fetch). No dependencies.
const API_KEY = process.env.VOUCHHOUND_API_KEY;
const resp = await fetch('https://api.vouchhound.com/v1/score', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: 'allbirds.com' }),
});
if (!resp.ok) {
throw new Error(`VouchHound API error ${resp.status}: ${await resp.text()}`);
}
const body = await resp.json();
const { data } = body;
console.log(`${data.url}: ${data.trust_score}/100 — ${data.verdict}`);
for (const signal of data.top_positive_signals) {
console.log(` + ${signal}`);
}
Errors
All errors follow a consistent shape. Switch on error.code — it's the stable machine-readable identifier. error.message is human-readable and may change wording without notice.
{
"detail": {
"error": {
"code": "rate_limit_exceeded",
"message": "You've used 100 of your 100 daily requests. Limit resets at 2026-05-28T01:44:39+00:00.",
"docs_url": "https://vouchhound.com/docs/errors#rate_limit_exceeded",
"limit": 100,
"used": 100,
"reset_at": "2026-05-28T01:44:39+00:00"
}
}
}
| HTTP | error.code | Meaning |
|---|---|---|
| 400 | invalid_url | The url field was missing, empty, or longer than 500 characters. |
| 401 | missing_api_key | No Authorization: Bearer header was provided. |
| 401 | invalid_api_key | The provided API key was not recognized. |
| 401 | revoked_api_key | This key has been revoked. Request a new one from hello@vouchhound.com. |
| 429 | rate_limit_exceeded | The daily call limit for your key's tier has been reached. The error body includes limit, used, and reset_at. |
| 500 | scoring_failed | An internal error occurred while scoring. Include the X-Request-Id header value when reporting. |
Rate limits
Per-key, rolling 24-hour window. Every API response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers so well-behaved clients can back off intelligently before getting throttled.
| Tier | Daily calls | How to get it |
|---|---|---|
| free | 100 | Available immediately on key creation. |
| pro | 10,000 | Email hello@vouchhound.com with your use case. |
| enterprise | Custom | Talk to us. Volume pricing, dedicated capacity, SLAs. |
Response times
Honest disclosure: a typical /v1/score call takes 5–10 seconds. We run five independent signal checks in parallel (WHOIS, Wayback Machine, Certificate Transparency, payment integration scan, and an LLM-backed authenticity check on the About page), and the slowest one sets the wall-clock floor.
We're actively working on a sub-3-second tier — primarily through aggressive caching of stable signals (domain age, web presence, certificate history all change rarely) and dropping the slowest of the third-party calls behind a faster fallback. Subscribe to the API changelog at hello@vouchhound.com to hear when that lands.
X-Response-Time-ms response header gives you the exact server-side time for any given request.
Known limitations
VouchHound is calibrated for verifying lesser-known merchants — the merchants AI agents and shoppers most need help evaluating. We're explicit about where the system currently underperforms:
- Enterprise brands with custom payment infrastructure. Major retailers (Nike, Apple, Lululemon, Costco) often process payments through Adyen, Cybersource, or in-house systems rather than Stripe or Shopify. Our payment-provider detection currently misses these, which can produce false negatives on otherwise well-established sites. We're working on this.
- Sites with aggressive bot protection. Some sites block automated trust-checking. We treat ≥2 blocked signals as a fraud signal — correct for most cases, but can occasionally produce false positives on legitimate sites with strict security policies.
- WooCommerce and generic Stripe integrations. Stores using less common platforms may show as "no payment provider detected" even when payments work normally.
- URL-semantic and image-provenance signals not yet shipped. Sites with obviously suspicious domain patterns or stolen product imagery aren't currently caught by our signal suite.