API Documentation

Complete guide to integrating ComplianceLayer into your security workflow.

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
Rate Limits: Batch operations count as multiple requests against your quota. Scanning 10 domains in a batch uses 10 of your monthly scans.

Batch Scanning

Scan multiple domains in parallel and get aggregated results:

POST/batch/scan

Submit up to 50 domains for parallel scanning. Returns aggregated statistics and individual domain results.

Request Body

ParameterTypeRequiredDescription
domainsstring[]YesArray of domains to scan (1-50 domains)
sort_byenumNoSort 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:

POST/batch/compare

Compare 2-5 domains with side-by-side module scoring and ranked results. Ideal for competitive analysis or before/after assessments.

Request Body

ParameterTypeRequiredDescription
domainsstring[]YesArray 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/limits before 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 first
  • sort_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

OperationMinimumMaximum
Batch Scan1 domain50 domains
Batch Compare2 domains5 domains
Plan Requirements: Batch operations are available on all paid plans. Free tier users can only scan domains individually.

Related Topics