REST API v1

API Documentation

Generate coverage reports programmatically. Submit URLs, track progress, retrieve results, and manage credits — all through simple REST endpoints.

Base URL https://app.coveragereport.ai/api/v1

Getting Started

The Coverage Report API lets you generate PR coverage reports without using the web interface. Submit a list of media coverage URLs, and we return a polished report with screenshots, Domain Authority, traffic metrics, and more.

Quick Start

  1. Create an account at app.coveragereport.ai
  2. Go to Settings → API Keys and create a new key
  3. Copy the key (it is only shown once)
  4. Pass the key in every request as the X-API-Key header
Free credits included. New accounts receive 3 free credits to try the API. Purchase additional credits from Settings → Credits.

Authentication

All API requests require an API key passed via the X-API-Key HTTP header. Keys are prefixed with cr_live_ and are scoped to your account.

HTTP Header
X-API-Key: cr_live_abc123def456...

If the key is missing, invalid, or revoked, the API returns 401 Unauthorized.

Keep your keys secret. Do not expose API keys in client-side code or public repositories. If a key is compromised, revoke it immediately from Settings and create a new one.

Rate Limits

API requests are rate-limited to 60 requests per minute per API key. If you exceed the limit, the API returns 429 Too Many Requests.

Rate limit windows are rolling. After receiving a 429 response, wait briefly before retrying. The limit resets every 60 seconds.


Get Credit Balance

GET /api/v1/credits/balance

Returns the current credit balance for the authenticated account.

Response Body
JSON
{
  "balance": 12
}
curl
curl https://app.coveragereport.ai/api/v1/credits/balance \
  -H "X-API-Key: cr_live_YOUR_KEY"

Create Report

POST /api/v1/reports

Submit an array of URLs to generate a coverage report. The report is processed asynchronously — poll the Get Report Status endpoint to check progress. Credits are deducted immediately.

Request Body
Parameter Type Description
urls Required string[] Array of coverage URLs to include in the report. Maximum 500 URLs per request. Duplicates are removed automatically.
reportName Optional string A name for the report. Defaults to "Untitled" if omitted.
Response Body — 202 Accepted
JSON
{
  "id": "a1b2c3d4e5f6...",
  "status": "processing",
  "urlCount": 15,
  "creditsUsed": 1,
  "message": "Report is being generated. Poll GET /api/v1/reports/a1b2c3d4e5f6... for status."
}
curl
curl -X POST https://app.coveragereport.ai/api/v1/reports \
  -H "X-API-Key: cr_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": [
      "https://www.bbc.co.uk/news/example-article",
      "https://www.theguardian.com/example-story",
      "https://techcrunch.com/2026/01/example"
    ],
    "reportName": "January Campaign"
  }'

Get Report Status

GET /api/v1/reports/:id

Retrieve the status and details of a report. When the status is completed, the response includes a shareable URL and aggregate stats.

Path Parameters
Parameter Type Description
id Required string The report ID returned from the Create Report endpoint.
Response — Processing
JSON
{
  "id": "a1b2c3d4e5f6...",
  "status": "processing",
  "reportName": "January Campaign",
  "urlCount": 15,
  "creditsUsed": 1,
  "createdAt": "2026-04-05T10:30:00.000Z",
  "completedAt": null
}
Response — Completed
JSON
{
  "id": "a1b2c3d4e5f6...",
  "status": "completed",
  "reportName": "January Campaign",
  "urlCount": 15,
  "creditsUsed": 1,
  "createdAt": "2026-04-05T10:30:00.000Z",
  "completedAt": "2026-04-05T10:31:42.000Z",
  "shareUrl": "https://app.coveragereport.ai/api/portal/reports/share/abc123...",
  "stats": {
    "totalLinks": 15,
    "screenshots": 12
  }
}
Response — Failed
JSON
{
  "id": "a1b2c3d4e5f6...",
  "status": "failed",
  "error": "Description of what went wrong",
  ...
}
curl
curl https://app.coveragereport.ai/api/v1/reports/a1b2c3d4e5f6... \
  -H "X-API-Key: cr_live_YOUR_KEY"

Get Report HTML

GET /api/v1/reports/:id/html

Returns the fully rendered HTML report. Only available for completed reports. The response Content-Type is text/html. Useful for embedding in your own dashboards or converting to PDF.

Path Parameters
Parameter Type Description
id Required string The report ID.

Returns 409 Conflict if the report is still processing, or 404 Not Found if the report does not exist.

curl
# Save the rendered report to a file
curl https://app.coveragereport.ai/api/v1/reports/a1b2c3d4e5f6.../html \
  -H "X-API-Key: cr_live_YOUR_KEY" \
  -o report.html

List Reports

GET /api/v1/reports

Returns a list of your most recent reports, ordered by creation date (newest first).

Query Parameters
Parameter Type Description
limit Optional integer Number of reports to return. Default: 20, maximum: 100.
Response Body
JSON
{
  "data": [
    {
      "id": "a1b2c3d4e5f6...",
      "status": "completed",
      "reportName": "January Campaign",
      "urlCount": 15,
      "creditsUsed": 1,
      "createdAt": "2026-04-05T10:30:00.000Z",
      "completedAt": "2026-04-05T10:31:42.000Z"
    },
    ...
  ]
}
curl
curl "https://app.coveragereport.ai/api/v1/reports?limit=50" \
  -H "X-API-Key: cr_live_YOUR_KEY"

Credit Pricing

Each report consumes credits based on the number of URLs submitted. Credits are deducted when you create the report, before processing begins.

URLs in Report Credits Used
1 – 100 1 credit
101 – 250 2 credits
251 – 500 3 credits
501+ 3 + 1 per additional 250 URLs

For example: a report with 600 URLs costs 3 + ceil((600 − 500) / 250) = 4 credits. A report with 1,000 URLs costs 3 + ceil((1000 − 500) / 250) = 5 credits.

Duplicate URLs are removed before calculating the credit cost. If you submit 120 URLs but 30 are duplicates, you are charged for 90 unique URLs (1 credit).

Error Codes

All errors return a JSON object with an error field containing a human-readable message. Some errors include additional context fields.

Status Meaning Description
400 Bad Request Invalid request body. The urls array is missing, empty, or exceeds 500 items. Or no valid URLs were found after cleaning.
401 Unauthorized The X-API-Key header is missing, the key does not start with cr_live_, or the key has been revoked.
402 Payment Required Insufficient credits. The response includes required (credits needed) and available (current balance) fields.
403 Forbidden Account is deactivated.
404 Not Found The requested report does not exist or does not belong to your account.
409 Conflict Returned by the HTML endpoint when the report is still processing. Includes the current status.
429 Too Many Requests Rate limit exceeded. Wait before retrying. Limit: 60 requests per minute per API key.
500 Server Error Unexpected server error. If this persists, contact support.

Error Response Examples

401 — Unauthorized
{
  "error": "Missing or invalid API key. Pass X-API-Key header."
}
402 — Insufficient Credits
{
  "error": "Insufficient credits",
  "required": 2,
  "available": 1
}
409 — Report Not Ready
{
  "error": "Report not ready yet",
  "status": "processing"
}

Code Examples

Full working examples showing how to create a report and poll for completion. Replace cr_live_YOUR_KEY with your actual API key.

bash
#!/bin/bash
# Create a coverage report and poll until complete

API_KEY="cr_live_YOUR_KEY"
BASE="https://app.coveragereport.ai/api/v1"

# 1. Check balance
curl -s "$BASE/credits/balance" -H "X-API-Key: $API_KEY"

# 2. Create report
RESPONSE=$(curl -s -X POST "$BASE/reports" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": [
      "https://www.bbc.co.uk/news/example",
      "https://www.theguardian.com/example"
    ],
    "reportName": "Q1 Coverage"
  }')

REPORT_ID=$(echo "$RESPONSE" | jq -r '.id')
echo "Report created: $REPORT_ID"

# 3. Poll for completion
while true; do
  STATUS=$(curl -s "$BASE/reports/$REPORT_ID" \
    -H "X-API-Key: $API_KEY" | jq -r '.status')
  echo "Status: $STATUS"
  if [ "$STATUS" != "processing" ]; then break; fi
  sleep 5
done

# 4. Download HTML report
curl -s "$BASE/reports/$REPORT_ID/html" \
  -H "X-API-Key: $API_KEY" -o report.html

echo "Report saved to report.html"
python
import requests
import time

API_KEY = "cr_live_YOUR_KEY"
BASE_URL = "https://app.coveragereport.ai/api/v1"
headers = {"X-API-Key": API_KEY}


# 1. Check credit balance
balance = requests.get(
    f"{BASE_URL}/credits/balance",
    headers=headers,
).json()
print(f"Credits available: {balance['balance']}")


# 2. Create a report
report = requests.post(
    f"{BASE_URL}/reports",
    headers=headers,
    json={
        "urls": [
            "https://www.bbc.co.uk/news/example",
            "https://www.theguardian.com/example",
            "https://techcrunch.com/2026/01/example",
        ],
        "reportName": "Q1 Coverage Report",
    },
).json()

report_id = report["id"]
print(f"Report created: {report_id} ({report['creditsUsed']} credits)")


# 3. Poll until complete
while True:
    status = requests.get(
        f"{BASE_URL}/reports/{report_id}",
        headers=headers,
    ).json()
    print(f"Status: {status['status']}")

    if status["status"] != "processing":
        break
    time.sleep(5)


# 4. Get results
if status["status"] == "completed":
    print(f"Share URL: {status['shareUrl']}")
    print(f"Stats: {status['stats']}")

    # Download HTML
    html = requests.get(
        f"{BASE_URL}/reports/{report_id}/html",
        headers=headers,
    )
    with open("report.html", "w") as f:
        f.write(html.text)
    print("Report saved to report.html")
else:
    print(f"Report failed: {status.get('error')}")
javascript
const API_KEY = "cr_live_YOUR_KEY";
const BASE_URL = "https://app.coveragereport.ai/api/v1";

const headers = {
  "X-API-Key": API_KEY,
  "Content-Type": "application/json",
};

async function generateReport() {
  // 1. Check balance
  const balanceRes = await fetch(`${BASE_URL}/credits/balance`, { headers });
  const { balance } = await balanceRes.json();
  console.log(`Credits available: ${balance}`);

  // 2. Create report
  const createRes = await fetch(`${BASE_URL}/reports`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      urls: [
        "https://www.bbc.co.uk/news/example",
        "https://www.theguardian.com/example",
        "https://techcrunch.com/2026/01/example",
      ],
      reportName: "Q1 Coverage Report",
    }),
  });
  const report = await createRes.json();
  console.log(`Report created: ${report.id} (${report.creditsUsed} credits)`);

  // 3. Poll until complete
  let status;
  do {
    await new Promise((r) => setTimeout(r, 5000));
    const res = await fetch(`${BASE_URL}/reports/${report.id}`, { headers });
    status = await res.json();
    console.log(`Status: ${status.status}`);
  } while (status.status === "processing");

  // 4. Get results
  if (status.status === "completed") {
    console.log(`Share URL: ${status.shareUrl}`);
    console.log(`Stats:`, status.stats);

    // Download HTML
    const htmlRes = await fetch(
      `${BASE_URL}/reports/${report.id}/html`,
      { headers }
    );
    const html = await htmlRes.text();
    console.log(`HTML report: ${html.length} chars`);
  } else {
    console.error(`Report failed: ${status.error}`);
  }
}

generateReport();

Managing API Keys

API keys are managed from the Coverage Report dashboard. You can also manage them programmatically via the portal endpoints (session-authenticated, not API key).

Creating a Key

Navigate to Settings → API Keys in the dashboard and click Create API Key. Give it a descriptive name. The full key is shown once — copy it immediately. Only the prefix (cr_live_XXXXXXXX) is stored for identification.

Limits

Each account can have up to 5 active API keys. Only owners and admins can create or revoke keys; team members with the "member" role cannot.

Revoking a Key

From the same Settings page, click the revoke button next to any key. Revoked keys stop working immediately and cannot be re-enabled — you will need to create a new key.

Usage tracking. Each key tracks its last_used_at timestamp and request_count, visible in the dashboard. Use this to audit which keys are active and identify unused keys you should revoke.