API Reference
Programmatic access to every audit, check result, and report. Trigger audits from CI/CD, export results to your data warehouse, or embed scores in client portals.
https://ga4-audit-api-1083343464429.europe-west2.run.appIntroduction
Every audit, check result, and report is accessible through this API. Same infrastructure as the web application — no separate integration layer.
/ (no prefix required)Authentication
Every request must include an Authorization header with a Bearer token. Two token types are supported:
The JWT issued by Supabase Auth when a user signs in. This is the same token the web application uses internally. Retrieve it from supabase.auth.getSession() on the client side.
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Long-lived service-to-service keys for backend integrations. API keys are prefixed with ga4a_. Generate them from the API Keys section below. API keys do not expire but can be revoked at any time.
Authorization: Bearer ga4a_live_abc123def456...
All requests must be made over HTTPS. Unauthenticated requests return 401 Unauthorized.
Base URL
https://ga4-audit-api-1083343464429.europe-west2.run.app
Set Content-Type: application/json on POST/PATCH requests. Binary report downloads (PDF, PPTX, Excel) return a binary stream.
Versioning
Current version: v1. No URL prefix. Breaking changes announced with at least 30 days notice.
Breaking changes
Removing or renaming fields, changing field types, removing endpoints, altering authentication mechanisms. Announced with at minimum 30 days notice.
Non-breaking additions
New optional fields in responses, new endpoints, new optional request parameters. These may be added without prior notice.
Subscribe to the changelog to receive notifications when the API changes.
How audits work
Audits are asynchronous. When you start an audit via POST /audits, the API immediately returns an audit object with status: "queued". The audit is dispatched to a Celery task queue and processed in the background by dedicated workers.
Recommended polling pattern: after starting an audit, poll GET /audits/{id} every 2.5 seconds and check the status field. Stop when status is completed or failed.
// Poll until complete
async function waitForAudit(id: string) {
while (true) {
const res = await fetch(`https://ga4-audit-api-1083343464429.europe-west2.run.app/audits/${id}`, {
headers: { Authorization: "Bearer <token>" },
});
const audit = await res.json();
if (audit.status === "completed" || audit.status === "failed") {
return audit;
}
await new Promise((r) => setTimeout(r, 2500));
}
}Webhook delivery (firing on audit.completed and audit.failed) is coming soon and will eliminate the need to poll.
Modules
Each audit runs up to 5 independent modules. You can run all modules (the default) or pass a subset via the modules array when starting an audit. Each module returns its own score, grade, and list of check results.
| module_id | Name | Checks | Priority |
|---|---|---|---|
property_config | Property Configuration | 30 | P0 |
tag_consent | Tag & Consent Validation | 47 | P0 |
utm_campaign | UTM & Campaign Integrity | 30 | P1 |
data_quality | Data Quality & Events | 61 | P1 |
ecommerce | E-commerce Integrity | 30 | P1 |
P0 modules run with every audit and have no external data dependencies. P1/P2 modules require Google Analytics Data API or BigQuery access respectively and are automatically skipped if credentials are unavailable.
Check results
Each module runs a set of checks. Every check returns a CheckResult object with the following structure:
{
"check_id": "PC-012",
"check_name": "Data retention window",
"module_id": "property_config",
"status": "fail", // pass | fail | warning | error | skipped
"severity": "high", // critical | high | medium | low
"score_earned": 0,
"score_possible": 10,
"message": "Data retention is set to 2 months. Recommended minimum is 14 months.",
"recommendation": "Navigate to Admin → Data Settings → Data Retention and set to 14 months.",
"details": { ... } // check-specific diagnostic data
}Status values
passCheck passed — full score earnedfailCheck failed — no score earnedwarningPartial concern — partial scoreerrorCheck could not executeskippedInsufficient data or accessSeverity levels
Grades and scores
Overall and per-module scores are expressed as a 0–100 integer. The score is calculated as sum(score_earned) / sum(score_possible) * 100 across all non-skipped checks. Scores map to letter grades as follows:
| Score range | Grade | Interpretation |
|---|---|---|
| 97–100 | A+ | Exceptional |
| 93–96 | A | Excellent |
| 90–92 | A− | Very good |
| 87–89 | B+ | Good |
| 83–86 | B | Above average |
| 80–82 | B− | Average |
| 77–79 | C+ | Below average |
| 73–76 | C | Fair |
| 70–72 | C− | Poor |
| 67–69 | D+ | Very poor |
| 63–66 | D | Critical issues present |
| 60–62 | D− | Near failing |
| 0–59 | F | Failing — immediate action required |
Properties
A property represents a connected GA4 property. Properties must be connected before audits can be run against them. The user must have analytics.readonly access in Google Analytics for the property to be connectable.
/propertiesReturn all properties connected to the authenticated user's account.
/properties/connectConnect a new GA4 property. The property_id must be a valid GA4 numeric property ID.
/properties/{id}Return full detail for a single connected property including last audit summary.
/properties/{id}Update display_name or website_url for a connected property.
/properties/{id}Disconnect and remove a property. All associated audits and results are deleted.
POST /properties/connect — Request body
{
"property_id": "312155177", // required — GA4 numeric property ID
"display_name": "My Website", // required — human label for this property
"account_name": "Client Account", // optional — GA4 account name
"website_url": "https://example.com" // optional — used for crawl-based checks
}Property object
{
"id": "1bdf6d89-6e1b-4eb3-b05a-8f4fc86222dc", // internal UUID
"property_id": "312155177", // GA4 numeric ID
"display_name": "My Website",
"account_name": "Client Account",
"measurement_id": "G-ABCDEFG123", // GA4 measurement ID
"website_url": "https://example.com",
"last_audit_score": 82.0,
"last_audit_at": "2026-04-10T14:00:00Z",
"is_connected": true,
"created_at": "2026-01-15T09:00:00Z"
}Audits
Audits are the core resource. Start an audit against a connected property and optionally restrict which modules run. Poll or receive a webhook when the audit completes, then read the full check results.
/auditsStart a new audit. Returns immediately with status: queued.
/auditsList audits with pagination. Filter by property_id or status.
/audits/{id}Full audit detail including all module scores and check results.
/audits/{id}/pausePause a running audit. The audit can be resumed from the dashboard.
/audits/{id}Permanently delete an audit and all associated check results and reports.
/audits/{id}/compare/{other_id}Compare two audits for the same property. Returns diff of scores and status changes.
/audits/historyRecent audit history for a property. Params: property_id (required), limit.
POST /audits — Request body
{
"property_id": "1bdf6d89-6e1b-4eb3-b05a-8f4fc86222dc", // required — internal property UUID
"modules": [ // optional — omit to run all modules
"property_config",
"tag_consent",
"utm_campaign",
"data_quality",
"ecommerce"
]
}GET /audits query parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Max results to return (default 20, max 100) |
offset | integer | Pagination offset (default 0) |
property_id | UUID | Filter by connected property UUID |
status | string | Filter by status: queued | running | completed | failed | paused |
AuditDetail object
{
"id": "e7e79e67-6ab5-497d-b669-cb1a73e9d059",
"property_uuid": "1bdf6d89-6e1b-4eb3-b05a-8f4fc86222dc",
"property_id": "312155177",
"property_name": "My Website",
"measurement_id": "G-ABCDEFG123",
"website_url": "https://example.com",
"status": "completed", // queued | running | completed | failed | paused
"overall_score": 82,
"grade": "B",
"checks_total": 198,
"checks_passed": 174,
"checks_failed": 18,
"checks_warning": 12,
"checks_completed": 204,
"modules": [ // ModuleResult[]
{
"module_id": "property_config",
"module_name": "Property Configuration",
"score": 90,
"grade": "A-",
"checks_passed": 27,
"checks_failed": 2,
"checks_warning": 1,
"checks_skipped": 0
}
],
"checks": [ // CheckResult[] — see Check results section
{
"check_id": "PC-001",
"check_name": "Data retention window",
"module_id": "property_config",
"status": "pass",
"severity": "high",
"score_earned": 10,
"score_possible": 10,
"message": "Data retention is set to 14 months.",
"recommendation": null,
"details": {}
}
],
"created_at": "2026-04-10T14:00:00Z",
"completed_at": "2026-04-10T14:03:42Z"
}Reports
Once an audit is completed, download it as a PDF, PPTX, or Excel report. Reports are generated on demand and cached. PPTX and Excel reports require a Pro plan or higher.
/reports/{audit_id}/pdfDownload a PDF report. Returns a binary PDF stream.
/reports/{audit_id}/pptxPro+Download a PowerPoint report.
/reports/{audit_id}/xlsxPro+Download an Excel workbook with all check data.
/reports/sampleReturn a sample report summary JSON. No authentication required.
Binary response headers
Dashboard
Aggregated analytics endpoints for building your own dashboards and trend views.
/audits/dashboard/score-trendHistorical score time series for a property. Returns one data point per audit.
/audits/dashboard/top-issuesTop failing checks across all recent audits for a property, ranked by severity.
score-trend query parameters
| Parameter | Type | Description |
|---|---|---|
property_id | UUID | Required. Internal property UUID. |
days | integer | Lookback window in days (default 30, max 365). |
score-trend response
[
{ "date": "2026-03-01", "score": 78, "grade": "C+", "audit_id": "..." },
{ "date": "2026-03-08", "score": 82, "grade": "B", "audit_id": "..." },
{ "date": "2026-04-10", "score": 86, "grade": "B+", "audit_id": "..." }
]top-issues response
[
{
"check_id": "PC-012",
"check_name": "Data retention window",
"module_id": "property_config",
"severity": "high",
"status": "fail",
"property_name": "My Website",
"audit_id": "e7e79e67-6ab5-497d-b669-cb1a73e9d059",
"audit_date": "2026-04-10T14:00:00Z"
}
]API Keys
API keys enable service-to-service access without a user session. Keys are scoped to the creating user's account and have the same permissions as a session token for that user. Each key is only shown in full once at creation time — store it securely.
/auth/api-keysList all API keys for the account. Key values are not returned — only prefixes.
/auth/api-keysCreate a new API key. Returns the full key once — store it immediately.
/auth/api-keys/{id}Permanently revoke an API key. Any requests using this key will return 401.
POST /auth/api-keys — Request body
{
"name": "Production backend" // required — a human label for this key
}Response (key shown once)
{
"id": "9f4ab2c1-...",
"name": "Production backend",
"api_key": "ga4a_live_abc123def456...", // full key — store this now
"key_prefix": "ga4a_live_abc1", // prefix shown in list view
"created_at": "2026-04-10T14:00:00Z"
}Manage API keys
Request format
All request bodies must be valid JSON. Set Content-Type: application/json on all POST and PATCH requests. Query parameters are used for filtering and pagination on GET endpoints.
POST /audits HTTP/1.1
Host: api.ga4audits.com
Authorization: Bearer <token>
Content-Type: application/json
Accept: application/json
{
"property_id": "1bdf6d89-6e1b-4eb3-b05a-8f4fc86222dc",
"modules": ["property_config", "tag_consent"]
}Response format
All responses are JSON unless you are downloading a binary report. Timestamps are ISO 8601 UTC strings. UUIDs use the standard hyphenated format. Nullable fields are present in the response body with a null value rather than omitted.
Paginated list endpoints return an object with a total count alongside the results array:
{
"audits": [ ... ],
"total": 47,
"limit": 20,
"offset": 0
}Error codes
All error responses return a consistent JSON body with a detail field containing a human-readable message:
{ "detail": "Audit not found" }| Code | Name | Meaning |
|---|---|---|
| 400 | Bad Request | Malformed request body or invalid parameter value. |
| 401 | Unauthorized | Missing, expired, or invalid Bearer token. |
| 403 | Forbidden | Valid token but insufficient permissions, or plan limit reached. |
| 404 | Not Found | The requested resource does not exist or is not accessible by this user. |
| 422 | Validation Error | Request body failed schema validation. Check the detail for field-level errors. |
| 429 | Rate Limited | Too many requests. Slow down and respect the Retry-After header. |
| 500 | Internal Server Error | Unexpected server error. Contact support if it persists. |
Rate limits
Rate limits are applied per user (or per API key for Enterprise). Limits reset on a rolling 60-second window.
| Endpoint group | Limit |
|---|---|
| Audit start (POST /audits) | 10 / min |
| Report download | 10 / min |
| Auth endpoints (/auth/*) | 10 / min |
| All other endpoints | 30 / min |
When you are rate limited, the response will be 429 Too Many Requests. The following headers are returned on every response to help you stay within limits:
Webhooks
Register a URL in account settings. GA4 Audits will POST a signed payload — no polling needed.
audit.completedFired when an audit finishes all modules successfully.
audit.failedFired when an audit encounters a fatal error and cannot complete.
Planned payload shape
{
"event": "audit.completed",
"created_at": "2026-04-10T14:03:42Z",
"data": {
"audit_id": "e7e79e67-6ab5-497d-b669-cb1a73e9d059",
"property_id": "1bdf6d89-6e1b-4eb3-b05a-8f4fc86222dc",
"overall_score": 82,
"grade": "B",
"status": "completed"
}
}All webhook payloads will include an X-GA4Audits-Signature header for request verification.
Changelog
April 2026
v1.2
- Added GET /audits/{id}/compare/{other_id} for audit diff
- Added GET /audits/history endpoint
- POST /audits now accepts an optional modules[] array to scope which modules run
- All report endpoints now return Content-Disposition headers
February 2026
v1.1
- Introduced Enterprise API keys (ga4a_ prefix) via /auth/api-keys
- Added PATCH /properties/{id} for updating display_name and website_url
- score-trend endpoint now returns grade alongside numeric score
- Rate limit headers added to all responses
January 2026
v1.0
- Initial API release
- Properties, Audits, Reports, and Dashboard endpoints
- Bearer token auth via Supabase session
Want to be notified of changes?
Contact us to subscribe to API changelog emails.