API Reference
The MFA Feed API provides programmatic access to our made-for-advertising domain intelligence. Integrate MFA detection into your ad-tech stack, brand safety tools, or custom workflows.
https://mfa.redvolcano.uk/api/v1Authentication
All API requests require an API key. Create and manage keys from the API Keys page in your dashboard.
Never share your API key publicly. If compromised, revoke it immediately from the API Keys page and create a new one.
- Precedence: if both are present, the
Authorizationheader wins — the?api_key=query param is ignored even if the header is malformed. - Case: the
Bearerscheme is matched case-insensitively (bearer,BEARER, etc. all work). - Caching: all responses carry
Cache-Control: private, no-store. Safe to use with?api_key=since intermediaries will not retain the body. - Duplicate params: repeating a query key (e.g.
?tier=red&tier=amber) returns 400 — we do not pick one silently. - Methods: only
GET,HEAD, andOPTIONSare accepted. Other verbs return 405 with anAllowheader.
GET /api/v1/feed HTTP/1.1 Host: mfa.redvolcano.uk Authorization: Bearer mfa_your_api_key_here
GET /api/v1/feed?api_key=mfa_your_api_key_here
Rate Limits
Rate limit headers are returned on every response from a valid API key (including 404 and 429). 401 responses omit them because there is no authenticated key to identify a bucket.
X-RateLimit-LimitMax requests per hourX-RateLimit-RemainingRemaining in current windowX-RateLimit-ResetUnix timestamp of window resetHTTP/1.1 200 OK Content-Type: application/json X-RateLimit-Limit: 100000 X-RateLimit-Remaining: 99847 X-RateLimit-Reset: 1712700000
Endpoints
/api/v1/feedReturns a paginated list of MFA-flagged domains with scores, tiers, and detection signals. Invalid query values return 400 rather than being silently clamped or ignored — the response body lists each field that failed validation.
Parameters
pageintegerPage number, min 1 (default: 1)
limitintegerResults per page, 1–100 (default: 50). Values outside this range return 400.
tierstringFilter by tier: red, amber, yellow
searchstringSubstring match on domain, max 100 chars
sortstringSort by: score, domain, created_at (default: score)
orderstringSort order: asc, desc (default: desc)
curl "https://mfa.redvolcano.uk/api/v1/feed?tier=red&limit=10" \ -H "Authorization: Bearer mfa_your_key_here"
{
"domains": [
{
"id": 1,
"domain": "example-mfa.com",
"score": 87,
"tier": "red",
"created_at": "2026-03-15T10:30:00Z",
"breakdown": [
{ "key": "ads_txt", "label": "Monetization",
"score": 22, "max": 25 }
],
"signals": [
{ "key": "excessive_ads", "label": "Excessive Ads" }
]
}
],
"total": 1523,
"page": 1,
"totalPages": 31,
"isLimited": false,
"totalAvailable": 1523
}/api/v1/statsReturns aggregate statistics about the MFA domain feed — total count and breakdown by tier.
curl "https://mfa.redvolcano.uk/api/v1/stats" \ -H "Authorization: Bearer mfa_your_key_here"
{
"total": 10103,
"red": 69,
"amber": 1913,
"yellow": 8121
}/api/v1/domain/:domainLook up a specific domain. Returns the full detection record if found, or 404 if the domain is not in the feed.
Parameters
domainstringrequiredThe domain to look up (path parameter)
curl "https://mfa.redvolcano.uk/api/v1/domain/example-mfa.com" \ -H "Authorization: Bearer mfa_your_key_here"
{
"domain": "example-mfa.com",
"score": 87,
"tier": "red",
"created_at": "2026-03-15T10:30:00Z",
"breakdown": [
{ "key": "ads_txt", "label": "Monetization",
"score": 22, "max": 25 },
{ "key": "content", "label": "Content",
"score": 18, "max": 20 }
],
"signals": [
{ "key": "excessive_ads", "label": "Excessive Ads" },
{ "key": "thin_content", "label": "Thin Content" }
]
}/api/v1/ringsReturns MFA ring data — clusters of domains sharing identical ads.txt configurations, indicating coordinated MFA networks. Only hashes with 5 or more domains are returned, sorted by size descending.
curl "https://mfa.redvolcano.uk/api/v1/rings" \ -H "Authorization: Bearer mfa_your_key_here"
{
"rings": [
{
"hash": "a1b2c3d4e5f6",
"size": 12,
"domains": [
{
"domain": "site-a.com",
"tier": "red",
"score": 87,
"created_at": "2026-03-27T20:39:01Z"
}
],
"tiers": { "red": 4, "amber": 6, "yellow": 2 }
}
],
"totalRings": 82,
"totalDomainsInRings": 1532
}/api/v1/feed/exportExport up to 50,000 matching domains as JSON (default) or CSV. Subject to a separate daily export quota.
Parameters
formatstringExport format: json or csv (default: json)
tierstringFilter by tier: red, amber, yellow
# Default — JSON array of domain records curl "https://mfa.redvolcano.uk/api/v1/feed/export?tier=red" \ -H "Authorization: Bearer mfa_your_key_here" # CSV download curl "https://mfa.redvolcano.uk/api/v1/feed/export?format=csv" \ -H "Authorization: Bearer mfa_your_key_here" \ -o mfa-feed.csv
[
{
"id": 1,
"domain": "example-mfa.com",
"score": 87,
"tier": "red",
"created_at": "2026-03-15T10:30:00Z",
"breakdown": [
{ "key": "ads_txt", "label": "Monetization",
"score": 22, "max": 25 }
],
"signals": [
{ "key": "excessive_ads", "label": "Excessive Ads" }
]
}
]CSV format (when ?format=csv)
Signals are joined with ; and wrapped in double quotes. The response carries Content-Type: text/csv and a filename disposition header so browsers download it.
domain,score,tier,detected_at,signals example-mfa.com,87,red,2026-03-15T10:30:00Z,"Excessive Ads;Thin Content" another-site.com,72,amber,2026-03-16T08:14:22Z,"Low Content Ratio"
Code Examples
import requests
API_KEY = "mfa_your_key_here"
BASE = "https://mfa.redvolcano.uk/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
# Get red-tier domains
resp = requests.get(f"{BASE}/feed", headers=headers,
params={"tier": "red", "limit": 100})
data = resp.json()
for d in data["domains"]:
print(f"{d['domain']} — Score: {d['score']} ({d['tier']})")
# Check a specific domain
resp = requests.get(f"{BASE}/domain/example.com", headers=headers)
if resp.status_code == 200:
print("Found:", resp.json())
elif resp.status_code == 404:
print("Not in MFA feed")const API_KEY = "mfa_your_key_here";
const BASE = "https://mfa.redvolcano.uk/api/v1";
const headers = { Authorization: `Bearer ${API_KEY}` };
// Get feed with filters
const res = await fetch(`${BASE}/feed?tier=red&limit=50`, { headers });
const data = await res.json();
console.log(`Found ${data.total} domains`);
// Export full feed as JSON
const exportRes = await fetch(`${BASE}/feed/export`, { headers });
const allDomains = await exportRes.json();Error Codes
| Code | Meaning | Description |
|---|---|---|
| 400 | Bad Request | A query parameter failed validation. Body includes a details array with the failing field(s). |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | API access not available for your account |
| 404 | Not Found | The requested domain was not found in the feed |
| 405 | Method Not Allowed | Only GET/HEAD/OPTIONS are supported. The response includes an Allow header. |
| 429 | Too Many Requests | Rate limit exceeded — check X-RateLimit-Reset header |
| 500 | Server Error | Internal error. Please try again later |
All errors return a JSON body with an error field.