API Reference
REST API for programmatic access to SDKWatch projects, scans, and consent records.
Base URL
https://sdkwatch.com/api#Authentication
All API requests require an API key in the Authorization header:
bash
curl https://sdkwatch.com/api/projects \ -H "Authorization: Bearer sdk_your_api_key_here"
Generate an API key in your dashboard under Settings → API Keys. Keep it secret — it has full access to your account.
#Projects
GET
/api/projectsList all projects in your account.
json
// Response 200
{
"projects": [
{
"id": "proj_a1b2c3",
"name": "My Website",
"domain": "example.com",
"platform": "web",
"snippetId": "sdk_a1b2c3d4e5f6",
"createdAt": "2025-01-15T10:00:00Z",
"lastScanAt": "2025-02-01T14:30:00Z",
"complianceScore": 78
}
]
}POST
/api/projectsCreate a new project.
json
// Request body
{
"name": "My App",
"domain": "example.com",
"platform": "web" // "web" | "ios" | "android"
}
// Response 201
{
"project": {
"id": "proj_xyz789",
"snippetId": "sdk_xyz789abc",
...
}
}GET
/api/projects/:idGet a single project by ID.
DELETE
/api/projects/:idDelete a project and all associated data.
#Scanning
POST
/api/scanTrigger a scan for a project. Returns immediately — scan runs asynchronously.
json
// Request body
{
"projectId": "proj_a1b2c3",
"url": "https://example.com" // optional override
}
// Response 202 Accepted
{
"scanId": "scan_abc123",
"status": "queued",
"estimatedSeconds": 20
}GET
/api/scan/:scanIdGet scan status and results.
json
// Response 200 (completed)
{
"scanId": "scan_abc123",
"status": "completed", // queued | running | completed | failed
"completedAt": "2025-02-01T14:31:15Z",
"complianceScore": 72,
"sdks": [
{
"name": "Google Analytics 4",
"category": "analytics",
"riskLevel": "medium",
"detectedAt": "https://example.com/",
"gdprRelevant": true
},
{
"name": "Meta Pixel",
"category": "advertising",
"riskLevel": "high",
"detectedAt": "https://example.com/",
"gdprRelevant": true
}
],
"recommendations": [
"Add consent gate for Meta Pixel (advertising category)",
"Defer Google Analytics load until analytics consent is given"
]
}#Consent Records
POST
/api/consentRecord a consent decision (called automatically by the banner).
json
// Request body
{
"snippetId": "sdk_a1b2c3d4e5f6",
"consentId": "user-uuid-generated-client-side",
"decisions": {
"analytics": true,
"advertising": false,
"functional": true,
"preferences": true
},
"userAgent": "Mozilla/5.0 ...",
"lang": "en"
}
// Response 201
{
"consentId": "cns_xyz",
"recordedAt": "2025-02-01T14:30:00Z"
}GET
/api/consent/:consentIdLook up a specific consent record by ID.
json
// Response 200
{
"consentId": "cns_xyz",
"snippetId": "sdk_a1b2c3d4e5f6",
"decisions": { "analytics": true, "advertising": false, ... },
"createdAt": "2025-02-01T14:30:00Z",
"updatedAt": "2025-02-01T14:30:00Z"
}#Rate Limits
| Endpoint | Free | Pro |
|---|---|---|
POST /api/scan | 10 / month | 500 / month |
GET /api/projects | 60 / min | 600 / min |
POST /api/consent | 10,000 / day | Unlimited |
Rate limit headers are included in every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
#Error Codes
| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_request | Missing or malformed parameters |
| 401 | unauthorized | Missing or invalid API key |
| 403 | forbidden | Valid key but insufficient permissions |
| 404 | not_found | Resource does not exist |
| 409 | conflict | Duplicate resource (e.g. project name) |
| 429 | rate_limited | Rate limit exceeded |
| 500 | internal_error | Server error — please retry |
json
// Error response shape
{
"error": {
"code": "rate_limited",
"message": "You have exceeded the scan limit for your plan.",
"retryAfter": 1706784000
}
}