Security Badges

Embed a security badge on your website or README. The badge shows the letter grade and score from your domain's most recent completed scan, rendered as an SVG you can hotlink directly.

Badges are opt-in. A badge exists only for a domain you monitor and have chosen to publish. Until you publish it, the badge URL returns 404— no one can look up a grade for a domain whose owner hasn't opted in. Publish from the badge prompt on any report page, or via the API (see Publishing your badge below). You can unpublish at any time.

The badge mirrors the stored result of the domain's last full scan — the same number, from the same place, as your report and dashboard. It does not run its own check, so the badge and the report can never disagree.

The default style is the ComplianceLayer card:

ComplianceLayerVERIFIEDGradeB82/100

SVG Badge Endpoint

GET/badge/:domain.svg

Get an embeddable security badge as SVG. Public — no authentication required. Cached for 1 hour.

Path Parameters

ParameterTypeDescription
domainstringDomain to fetch the badge for. Protocol, path, and port are stripped. Invalid domains are rejected with 400.

Query Parameters

ParameterTypeDescription
stylestringOptional. By default the badge renders as the ComplianceLayer card — a dark 420×92 panel with the grade and score. Pass flat for a compact shields-style strip sized for a README.

The response is image/svg+xml with Cache-Control: public, max-age=3600. A domain that is not published — or is monitored but has no completed scan yet — returns 404.

Embedding the badge

<!-- Embed in HTML (default card style) -->
<img
  src="https://api.compliancelayer.net/v1/badge/example.com.svg"
  alt="Security Grade"
/>

<!-- Compact strip for tighter layouts -->
<img
  src="https://api.compliancelayer.net/v1/badge/example.com.svg?style=flat"
  alt="Security Grade"
/>

Publishing Your Badge

PATCH/domains/:domain_id/badge

Publish or unpublish the badge for one of your monitored domains. Requires authentication; the domain must belong to your account.

# Publish
curl -X PATCH "https://api.compliancelayer.net/v1/domains/123/badge" \
  -H "Authorization: Bearer cl_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"badge_enabled": true}'

# Unpublish
curl -X PATCH "https://api.compliancelayer.net/v1/domains/123/badge" \
  -H "Authorization: Bearer cl_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"badge_enabled": false}'

The domain ID comes from GET /v1/domains, which also reports each domain's current badge_enabled state. Publishing a domain that has never completed a scan is allowed — the badge URL simply stays 404 until a grade exists, rather than showing an invented one.

JSON Badge Endpoint

GET/badge/:domain.json

Get badge data as JSON for custom rendering. Requires authentication, and returns data only for domains monitored by your own account.

The JSON endpoint is not public. Unlike the SVG, it returns 401without a valid API key, and it only answers for your own domains. Because it needs a key, it must be called from your server — never from browser JavaScript, which would expose the key to anyone who opens devtools.
curl "https://api.compliancelayer.net/v1/badge/example.com.json" \
  -H "Authorization: Bearer cl_YOUR_API_KEY"

source is always "last_scan": the grade and score are read from the domain's most recent completed scan. A domain with no completed scan returns 404 rather than a fabricated grade. Your own domains do not need to be published for you to read them here — publishing controls only the public SVG.

Rate Limiting

Both badge endpoints are limited to 30 requests per minute per IP address, which prevents bulk enumeration. Exceeding it returns 429 with {"detail": "Rate limit exceeded"}. This is separate from your plan's API rate limits.

Because the SVG is served with Cache-Control: public, max-age=3600, embedding a badge on a high-traffic page does not consume your allowance — browsers and CDNs serve it from cache.

Badge Grades

The badge uses the same grade bands as the full report — one ladder for the whole product:

GradeScoreCard style (default)Flat style
A90-100GreenBright green
B75-89Light greenYellow-green
C55-74GoldYellow
D35-54OrangeOrange
F0-34RedRed

There are no plus or minus grades. The two styles use the same ladder; the card's colors are brightened for its dark background.

If the last scan could not assess enough of the domain to grade it honestly, the stored result is INCOMPLETE. The SVG then renders "Not assessed" instead of a letter, and any custom rendering of the JSON response must handle that value — a lookup keyed only on A-F will fail the first time a scan cannot complete.

Custom Badge Implementation

To render your own badge from the JSON endpoint, fetch it on the server and pass only the result to the browser. Both examples below keep the API key server-side and treat 404as "no badge to show".

const express = require('express');
const app = express();

// Server-side proxy: the API key never leaves the server.
// Never call the .json endpoint from browser JavaScript.
app.get('/api/security-badge/:domain', async (req, res) => {
  const response = await fetch(
    `https://api.compliancelayer.net/v1/badge/${req.params.domain}.json`,
    { headers: { 'Authorization': `Bearer ${process.env.COMPLIANCELAYER_API_KEY}` } }
  );

  if (response.status === 404) {
    // Not one of your domains, or no completed scan yet — nothing to show.
    return res.status(404).json({ error: 'No badge for this domain' });
  }
  if (!response.ok) {
    return res.status(502).json({ error: 'Badge lookup failed' });
  }

  const badge = await response.json();

  // Mirror the upstream 1-hour cache so you stay well inside 30 req/min per IP
  res.set('Cache-Control', 'public, max-age=3600');
  res.json(badge);
});

Best Practices

1. Cache Badge Images

  • SVG badges are cached for 1 hour by ComplianceLayer
  • Your CDN should cache badges for at least 30 minutes
  • Respect the Cache-Control header rather than cache-busting the URL

2. Provide Alt Text

<img
  src="https://api.compliancelayer.net/v1/badge/example.com.svg"
  alt="ComplianceLayer security grade for example.com"
/>

Keep the grade out of the alt text unless you regenerate the page when the badge changes, otherwise the two will drift apart. The SVG already carries an accessible aria-label and <title> with the current grade and score.

3. Link to ComplianceLayer

Make badges clickable so visitors can learn what the grade means:

<a
  href="https://compliancelayer.net"
  target="_blank"
  rel="noopener noreferrer"
>
  <img
    src="https://api.compliancelayer.net/v1/badge/example.com.svg"
    alt="ComplianceLayer security grade for example.com"
  />
</a>

There is no public page showing your stored report, so link to the site rather than constructing a report URL. To share full scan results, generate a report through the scan API and host it yourself.

4. Keep Expectations Straight

  • The badge shows the result of the domain's most recent completed scan — running a new scan updates it (allowing up to an hour of edge caching)
  • The badge never runs a scan of its own; a domain with no completed scan has no badge
  • Unpublishing takes effect immediately at the API, though embedded copies may persist in caches for up to an hour

Example Implementations

Footer Badge

<footer>
  <div class="security-badge-container">
    <p>Security Powered by ComplianceLayer</p>
    <a href="https://compliancelayer.net">
      <img
        src="https://api.compliancelayer.net/v1/badge/example.com.svg"
        alt="ComplianceLayer security grade for example.com"
      />
    </a>
  </div>
</footer>

README Badge

# MyApp

![Security Grade](https://api.compliancelayer.net/v1/badge/myapp.com.svg?style=flat)
![License](https://img.shields.io/badge/license-MIT-blue.svg)

Secure, reliable cloud platform...

Related Topics