1. 5-minute quickstart
The ConsentLab public API (/v1) exposes your consent statistics, your register, the banner configuration and scan triggering — all in REST/JSON, authenticated by key. It is included on the Business (site ≥ 500,000 sessions) and Agency plans.
Create an API key
From your dashboard → Settings → API, create a key by choosing the scopes (permissions) you need and the Site(s) it may reach. The key, in the format cl_live_…, is shown only once: copy it immediately into a secrets manager.
First authenticated call
Every request carries the Authorization: Bearer cl_live_…header. Let's list your sites:
# Authenticate every request with the Authorization: Bearer header
curl "https://api.consentlab.eu/v1/sites" \
-H "Authorization: Bearer $CONSENTLAB_API_KEY"Response:
[
{ "id": "3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f", "name": "mysite.com", "created_at": "2026-01-15T09:24:00Z" }
]The site id (e.g. 3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f) is the parameter for every other endpoint. A staging environment is available for your tests:
# Production
https://api.consentlab.eu/v1
# Staging (pré-production)
https://dev.api.consentlab.eu/v12. Recipes by use case
Agency reporting
For a client dashboard, combine the aggregates (consent-stats) — acceptance rate, volumes — with the detail when needed (consents):
# Consent aggregates for a site (acceptance rate, volumes…)
curl "https://api.consentlab.eu/v1/sites/3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f/consent-stats" \
-H "Authorization: Bearer $CONSENTLAB_API_KEY" # scope: read:statsArchiving the register
To keep the consent proof in your own warehouse, paginate consents by cursor (limit ≤ 100) until it is exhausted:
#!/usr/bin/env bash
# Exports the whole consent register as JSON Lines, page by page.
# The key lives in an environment variable — never hard-coded in the script.
SITE="3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f"
cursor=""
while : ; do
resp=$(curl -s "https://api.consentlab.eu/v1/sites/$SITE/consents?limit=100&cursor=$cursor" \
-H "Authorization: Bearer $CONSENTLAB_API_KEY") # scope: read:consents
echo "$resp" | jq -c '.data[]' >> register.jsonl
# Exact pagination field: see /v1/openapi.json
cursor=$(echo "$resp" | jq -r '.next_cursor // empty')
[ -z "$cursor" ] && break
doneBanner config in CI/CD
Version your banner configurations and apply them from your pipeline with PUT banner-config:
# Apply the banner config from your pipeline (scope: write:config).
# The JSON body is illustrative — exact schema: /v1/openapi.json
curl -X PUT "https://api.consentlab.eu/v1/sites/3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f/banner-config" \
-H "Authorization: Bearer $CONSENTLAB_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "primary_color": "#0F766E", "position": "bottom" }'write:config key can read or delete nothing else: that is least privilege.3. Endpoint reference
Six endpoints cover the whole v1 surface. Parameter and response-schema details are on the interactive docs.
| Method | Path | Required scope | Description |
|---|---|---|---|
| GET | /v1/sites | — | List the sites the key can access. |
| GET | /v1/sites/:id/consent-stats | read:stats | Consent aggregates (rates, volumes). |
| GET | /v1/sites/:id/consents | read:consents | Consent register, cursor-paginated. |
| GET | /v1/sites/:id/banner-config | read:config / write:config | Current banner configuration. |
| PUT | /v1/sites/:id/banner-config | write:config | Update the banner configuration. |
| POST | /v1/sites/:id/scans | trigger:scan | Trigger a cookie scan for the site. |
The scopes shown match the v1 contract; the interactive docs are authoritative if an endpoint evolves.
4. Limits & errors
Error codes
Errors follow standard HTTP statuses, with a stable machine code in the JSON body:
| HTTP | Error code | Cause |
|---|---|---|
| 401 | INVALID_API_KEY | Missing, malformed, revoked or expired key. |
| 403 | INSUFFICIENT_SCOPE | The key lacks the scope required by the endpoint. |
| 403 | API_ACCESS_REQUIRED | The plan does not include API access (Business ≥ 500k or Agency). |
| 403 | IP_NOT_ALLOWED | Call made from an IP outside the key's configured allowlist. |
| 404 | SITE_NOT_FOUND | Site does not exist or is outside the key scope. |
| 404 | CONFIG_NOT_FOUND | No banner configuration exists yet for this site. |
| 429 | RATE_LIMIT_EXCEEDED | Rate exceeded — see Retry-After. |
Rate limiting
Every response carries the quota headers. The guaranteed floors are 300 requests/min/IP and 600 requests/min/key; X-RateLimit-Reset is expressed in seconds remaining. Counters are shared across all our instances, so the limits are exact and enforced against the real client IP, with no drift from load balancing.
HTTP/1.1 200 OK
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 598
X-RateLimit-Reset: 42 # seconds until the window resets
# On a 429, the Retry-After header tells you how long to wait.429 RATE_LIMIT_EXCEEDED, honour the Retry-After header before retrying (backoff). Do not loop without waiting: you would stay blocked.Cursor pagination
Large collections (consents) are walked by cursor: ?cursor=&limit= with limit capped at 100. Pass the previous page's cursor until it comes back empty (see the archiving recipe above). The cursor is opaque: never build it by hand.
5. Versioning & deprecation
The API is versioned in the path (/v1/). Backwards-compatible changes (new fields, new endpoints) ship without a version bump. Any breaking change would go through a new version (/v2/).
Changelog
- v1 — Initial release: per-Site scoped API keys, endpoints
sites,consent-stats,consents,banner-config,scans, cursor pagination and rate limiting.
6. Key security
- Format & one-time display: a
cl_live_…key is shown only at creation. We store only a fingerprint of it: it can never be shown again. - Least privilege: grant only the scopes you need among the five available (
read:stats,read:consents,read:config,write:config,trigger:scan). A reporting job only needs read access; to read the banner config without being able to change it,read:configalone is enough. - Per-Site scoping: a key can only reach the Sites you attached it to. Create one key per integration rather than a single catch-all key.
- Time-to-live (TTL): at creation, set an expiry (
expiresInDays, from 1 to 3650 days). Past that point the key is automatically rejected (401 INVALID_API_KEY): ideal for temporary access (audit, contractor, POC). - Two-step rotation: create the new key, deploy it, then revoke the old one. A 24 h grace period lets your services switch over without downtime.
- Immediate revocation: a revoked key stops working instantly (
401 INVALID_API_KEY). Revoke at the slightest doubt. - Auto-revocation on leak: our key prefixes (
cl_live_…) are registered with GitHub secret scanning. If a key is accidentally pushed to a public repository, we are alerted and the key is revoked automatically — a leaked key does not stay usable. - Never commit a key: environment variable or secrets manager, never in the repo nor in front-end code (keys are server-to-server).
Restrict a key to IPs
You can restrict a key to a list of source IP addresses or CIDR ranges (IPv4 and IPv6). Any call coming from an IP outside that allowlist is rejected with a 403 IP_NOT_ALLOWED, however valid the key is. Ideal for pinning a server integration to the fixed egress IP of your infrastructure or CI runner.
Ready to automate your compliance?
Create a key from your dashboard, or explore the interactive API reference.