Batch Operations
Scan or compare multiple domains simultaneously for efficient security assessments across your entire portfolio. Perfect for MSPs, agencies, and organizations managing many domains.
Why Use Batch Operations?
- Efficiency: Scan up to 50 domains in a single API request
- Comparison: Side-by-side analysis of 2-5 domains
- Portfolio Overview: Quickly assess security across all assets
- Benchmarking: Compare your domains against competitors
Batch Scanning
Scan multiple domains in parallel and get aggregated results:
/batch/scanSubmit up to 50 domains for parallel scanning. Returns aggregated statistics and individual domain results.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
domains | string[] | Yes | Array of domains to scan (1-50 domains) |
sort_by | enum | No | Sort results by: risk, score, or domain (default: risk) |
curl -X POST "https://api.compliancelayer.net/v1/batch/scan" \
-H "Authorization: Bearer cl_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domains": [
"example.com",
"example.org",
"example.net",
"test-site.com",
"demo-app.io"
],
"sort_by": "risk"
}'Response
{
"total": 5,
"summary": {
"avg_score": 78.2,
"critical_risk": 1,
"passing": 3,
"grade_distribution": {
"A": 1,
"B": 2,
"C": 1,
"D": 0,
"F": 1
}
},
"results": [
{
"domain": "test-site.com",
"overall_score": 42,
"overall_grade": "F",
"modules": {
"ssl": {
"score": 0,
"grade": "F",
"issues": 3
},
"dns_email": {
"score": 65,
"grade": "D",
"issues": 2
}
},
"error": null
},
{
"domain": "example.com",
"overall_score": 87,
"overall_grade": "B+",
"modules": {
"ssl": {
"score": 95,
"grade": "A",
"issues": 0
},
"dns_email": {
"score": 82,
"grade": "B",
"issues": 1
}
},
"error": null
}
]
}Results are sorted by risk level (F grades first) by default. Each domain includes module-level breakdowns for quick identification of problem areas.
Domain Comparison
Compare 2-5 domains side-by-side to benchmark security posture:
/batch/compareCompare 2-5 domains with side-by-side module scoring and ranked results. Ideal for competitive analysis or before/after assessments.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
domains | string[] | Yes | Array of domains to compare (2-5 domains) |
curl -X POST "https://api.compliancelayer.net/v1/batch/compare" \
-H "Authorization: Bearer cl_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domains": [
"your-site.com",
"competitor-a.com",
"competitor-b.com"
]
}'Response
{
"ranked": [
{
"rank": 1,
"domain": "your-site.com",
"overall_score": 92,
"overall_grade": "A-"
},
{
"rank": 2,
"domain": "competitor-a.com",
"overall_score": 78,
"overall_grade": "C+"
},
{
"rank": 3,
"domain": "competitor-b.com",
"overall_score": 65,
"overall_grade": "D"
}
],
"winner": "your-site.com",
"score_gap": 14,
"module_comparison": {
"ssl": {
"your-site.com": 95,
"competitor-a.com": 82,
"competitor-b.com": 70
},
"dns_email": {
"your-site.com": 90,
"competitor-a.com": 75,
"competitor-b.com": 60
},
"headers": {
"your-site.com": 88,
"competitor-a.com": 72,
"competitor-b.com": 58
}
},
"full_results": [...]
}Practical Examples
MSP Portfolio Assessment
Scan all client domains and generate a security report:
async function assessClientPortfolio(clientDomains) {
// Split into batches of 50
const batches = [];
for (let i = 0; i < clientDomains.length; i += 50) {
batches.push(clientDomains.slice(i, i + 50));
}
// Scan each batch
const allResults = [];
for (const batch of batches) {
const response = await fetch(
'https://api.compliancelayer.net/v1/batch/scan',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
domains: batch,
sort_by: 'risk'
})
}
);
const batchResults = await response.json();
allResults.push(...batchResults.results);
// Respect rate limits - wait 1 second between batches
await new Promise(r => setTimeout(r, 1000));
}
// Analyze results
const criticalDomains = allResults.filter(r => r.overall_score < 60);
const passingDomains = allResults.filter(r => r.overall_score >= 80);
console.log(`Portfolio Assessment Complete`);
console.log(`Total domains: ${allResults.length}`);
console.log(`Critical risk: ${criticalDomains.length}`);
console.log(`Passing: ${passingDomains.length}`);
console.log(`\nDomains requiring attention:`);
criticalDomains.forEach(domain => {
console.log(`- ${domain.domain}: ${domain.overall_grade}`);
});
return {
total: allResults.length,
critical: criticalDomains,
passing: passingDomains,
all: allResults
};
}
// Usage
const clients = [
'client-a.com',
'client-b.com',
'client-c.com',
// ... up to 50+ domains
];
const report = await assessClientPortfolio(clients);Competitive Analysis
Compare your security posture against competitors:
async function competitiveAnalysis(yourDomain, competitors) {
const domains = [yourDomain, ...competitors];
const response = await fetch(
'https://api.compliancelayer.net/v1/batch/compare',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ domains })
}
);
const comparison = await response.json();
// Find your ranking
const yourRank = comparison.ranked.find(r => r.domain === yourDomain);
console.log(`Your Security Position:`);
console.log(`Rank: #${yourRank.rank} out of ${comparison.ranked.length}`);
console.log(`Score: ${yourRank.overall_score} (${yourRank.overall_grade})`);
if (yourRank.rank === 1) {
console.log(`\n🏆 You're the security leader!`);
} else {
const leader = comparison.ranked[0];
const gap = leader.overall_score - yourRank.overall_score;
console.log(`\nGap to leader: ${gap} points`);
}
// Identify weak areas
console.log(`\nModule Analysis:`);
Object.entries(comparison.module_comparison).forEach(([module, scores]) => {
const yourScore = scores[yourDomain];
const avgCompetitor = Object.entries(scores)
.filter(([domain]) => domain !== yourDomain)
.reduce((sum, [, score]) => sum + score, 0) / competitors.length;
if (yourScore < avgCompetitor) {
console.log(`⚠️ ${module}: Below average (yours: ${yourScore}, avg: ${avgCompetitor.toFixed(0)})`);
}
});
return comparison;
}
// Usage
const results = await competitiveAnalysis(
'your-company.com',
['competitor-a.com', 'competitor-b.com', 'competitor-c.com']
);Before/After Migration Testing
Compare old and new infrastructure during migrations:
async function migrationTest(oldDomain, newDomain) {
const response = await fetch(
'https://api.compliancelayer.net/v1/batch/compare',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
domains: [oldDomain, newDomain]
})
}
);
const comparison = await response.json();
const oldResult = comparison.full_results.find(r => r.domain === oldDomain);
const newResult = comparison.full_results.find(r => r.domain === newDomain);
console.log(`Migration Security Assessment`);
console.log(`
Old Infrastructure (${oldDomain}):`);
console.log(` Score: ${oldResult.overall_score}`);
console.log(`
New Infrastructure (${newDomain}):`);
console.log(` Score: ${newResult.overall_score}`);
const diff = newResult.overall_score - oldResult.overall_score;
if (diff > 0) {
console.log(`
✅ Security improved by ${diff} points`);
} else if (diff < 0) {
console.log(`
⚠️ Security degraded by ${Math.abs(diff)} points`);
} else {
console.log(`
➡️ No change in security score`);
}
// Module-level differences
console.log(`
Module Changes:`);
Object.keys(comparison.module_comparison).forEach(module => {
const oldScore = comparison.module_comparison[module][oldDomain];
const newScore = comparison.module_comparison[module][newDomain];
const change = newScore - oldScore;
if (change !== 0) {
const arrow = change > 0 ? '⬆' : '⬇';
console.log(` ${arrow} ${module}: ${oldScore} → ${newScore} (${change > 0 ? '+' : ''}${change})`);
}
});
return comparison;
}
// Usage
await migrationTest('old-site.example.com', 'new-site.example.com');Best Practices
1. Respect Rate Limits
- Each domain in a batch counts toward your monthly scan quota
- Scanning 50 domains uses 50 of your monthly scans
- Use
/usage/limitsbefore large batches to check remaining quota - For very large portfolios (>50 domains), split into multiple batches with delays
2. Optimize Batch Sizes
// Check quota before batch
async function smartBatchScan(domains) {
// Check remaining quota
const limitsResponse = await fetch(
'https://api.compliancelayer.net/v1/usage/limits',
{
headers: {
'Authorization': `Bearer ${process.env.API_KEY}`
}
}
);
const limits = await limitsResponse.json();
const remaining = limits.usage.scans_remaining;
if (domains.length > remaining) {
console.warn(`⚠️ Batch size (${domains.length}) exceeds remaining quota (${remaining})`);
console.log(`Scanning only first ${remaining} domains`);
domains = domains.slice(0, remaining);
}
// Proceed with batch
return await batchScan(domains);
}3. Handle Errors Gracefully
Individual domain scans may fail without affecting others:
async function batchScanWithRetry(domains) {
const response = await fetch(
'https://api.compliancelayer.net/v1/batch/scan',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ domains })
}
);
const results = await response.json();
// Separate successful and failed scans
const successful = results.results.filter(r => r.error === null);
const failed = results.results.filter(r => r.error !== null);
if (failed.length > 0) {
console.log(`⚠️ ${failed.length} domains failed:`);
failed.forEach(result => {
console.log(` - ${result.domain}: ${result.error}`);
});
// Optionally retry failed domains
if (failed.length < 5) {
console.log('Retrying failed domains...');
const retryDomains = failed.map(r => r.domain);
// ... retry logic
}
}
return { successful, failed };
}4. Use Appropriate Sorting
sort_by: "risk"— Prioritize critical issues (default)sort_by: "score"— Show best performers firstsort_by: "domain"— Alphabetical order
5. Cache Comparison Results
Batch comparisons are computationally expensive. Cache results for at least 1 hour:
const cache = new Map();
async function cachedCompare(domains) {
const cacheKey = domains.sort().join(',');
// Check cache
if (cache.has(cacheKey)) {
const cached = cache.get(cacheKey);
if (Date.now() - cached.timestamp < 3600000) { // 1 hour
console.log('Using cached comparison');
return cached.data;
}
}
// Fetch fresh data
const comparison = await compareDomains(domains);
// Cache result
cache.set(cacheKey, {
data: comparison,
timestamp: Date.now()
});
return comparison;
}Limitations
| Operation | Minimum | Maximum |
|---|---|---|
| Batch Scan | 1 domain | 50 domains |
| Batch Compare | 2 domains | 5 domains |
Related Topics
- Running Scans - Individual domain scanning
- Usage Analytics - Monitor batch operation consumption
- Rate Limits - Understanding batch operation limits