API v1 — REST · JSON · Bearer auth

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.

Base URLhttps://ga4-audit-api-1083343464429.europe-west2.run.app
API keys require Enterprise plan

Introduction

Every audit, check result, and report is accessible through this API. Same infrastructure as the web application — no separate integration layer.

FormatJSON (except binary report downloads)
AuthBearer token (session or API key)
TLSHTTPS only — HTTP requests are rejected
Versionv1 — all endpoints are versioned under / (no prefix required)

Authentication

Every request must include an Authorization header with a Bearer token. Two token types are supported:

Session tokenAll plans

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.

Header
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
API keyEnterprise

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.

Header
Authorization: Bearer ga4a_live_abc123def456...

All requests must be made over HTTPS. Unauthenticated requests return 401 Unauthorized.

Base URL

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.

queued
running
completed
failed

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.

Node.js
// 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_idNameChecksPriority
property_configProperty Configuration30P0
tag_consentTag & Consent Validation47P0
utm_campaignUTM & Campaign Integrity30P1
data_qualityData Quality & Events61P1
ecommerceE-commerce Integrity30P1

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:

CheckResult schema
{
  "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 earned
failCheck failed — no score earned
warningPartial concern — partial score
errorCheck could not execute
skippedInsufficient data or access

Severity levels

criticalFundamental data integrity risk
highMajor accuracy impact
mediumModerate quality concern
lowMinor improvement opportunity

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 rangeGradeInterpretation
97–100A+Exceptional
93–96AExcellent
90–92A−Very good
87–89B+Good
83–86BAbove average
80–82B−Average
77–79C+Below average
73–76CFair
70–72C−Poor
67–69D+Very poor
63–66DCritical issues present
60–62D−Near failing
0–59FFailing — 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.

GET
/properties

Return all properties connected to the authenticated user's account.

POST
/properties/connect

Connect a new GA4 property. The property_id must be a valid GA4 numeric property ID.

GET
/properties/{id}

Return full detail for a single connected property including last audit summary.

PATCH
/properties/{id}

Update display_name or website_url for a connected property.

DELETE
/properties/{id}

Disconnect and remove a property. All associated audits and results are deleted.

POST /properties/connect — Request body

JSON
{
  "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

JSON
{
  "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.

POST
/audits

Start a new audit. Returns immediately with status: queued.

GET
/audits

List audits with pagination. Filter by property_id or status.

GET
/audits/{id}

Full audit detail including all module scores and check results.

POST
/audits/{id}/pause

Pause a running audit. The audit can be resumed from the dashboard.

DELETE
/audits/{id}

Permanently delete an audit and all associated check results and reports.

GET
/audits/{id}/compare/{other_id}

Compare two audits for the same property. Returns diff of scores and status changes.

GET
/audits/history

Recent audit history for a property. Params: property_id (required), limit.

POST /audits — Request body

JSON
{
  "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

ParameterTypeDescription
limitintegerMax results to return (default 20, max 100)
offsetintegerPagination offset (default 0)
property_idUUIDFilter by connected property UUID
statusstringFilter by status: queued | running | completed | failed | paused

AuditDetail object

JSON
{
  "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.

GET
/reports/{audit_id}/pdf

Download a PDF report. Returns a binary PDF stream.

GET
/reports/{audit_id}/pptxPro+

Download a PowerPoint report.

GET
/reports/{audit_id}/xlsxPro+

Download an Excel workbook with all check data.

GET
/reports/sample

Return a sample report summary JSON. No authentication required.

Binary response headers

Content-Type: application/pdf
Content-Type: application/vnd.openxmlformats-officedocument.presentationml.presentation
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="ga4-audit-{audit_id}.pdf"

Dashboard

Aggregated analytics endpoints for building your own dashboards and trend views.

GET
/audits/dashboard/score-trend

Historical score time series for a property. Returns one data point per audit.

GET
/audits/dashboard/top-issues

Top failing checks across all recent audits for a property, ranked by severity.

score-trend query parameters

ParameterTypeDescription
property_idUUIDRequired. Internal property UUID.
daysintegerLookback window in days (default 30, max 365).

score-trend response

JSON
[
  { "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

JSON
[
  {
    "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

Enterprise plan required

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.

GET
/auth/api-keys

List all API keys for the account. Key values are not returned — only prefixes.

POST
/auth/api-keys

Create a new API key. Returns the full key once — store it immediately.

DELETE
/auth/api-keys/{id}

Permanently revoke an API key. Any requests using this key will return 401.

POST /auth/api-keys — Request body

JSON
{
  "name": "Production backend" // required — a human label for this key
}

Response (key shown once)

JSON
{
  "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

Loading developer tools...

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.

HTTP
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:

JSON
{
  "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:

JSON
{ "detail": "Audit not found" }
CodeNameMeaning
400Bad RequestMalformed request body or invalid parameter value.
401UnauthorizedMissing, expired, or invalid Bearer token.
403ForbiddenValid token but insufficient permissions, or plan limit reached.
404Not FoundThe requested resource does not exist or is not accessible by this user.
422Validation ErrorRequest body failed schema validation. Check the detail for field-level errors.
429Rate LimitedToo many requests. Slow down and respect the Retry-After header.
500Internal Server ErrorUnexpected 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 groupLimit
Audit start (POST /audits)10 / min
Report download10 / min
Auth endpoints (/auth/*)10 / min
All other endpoints30 / 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:

X-RateLimit-Limit: 30
X-RateLimit-Remaining: 28
X-RateLimit-Reset: 1712761200
Retry-After: 12 # only present on 429

Webhooks

Coming soon

Register a URL in account settings. GA4 Audits will POST a signed payload — no polling needed.

audit.completed

Fired when an audit finishes all modules successfully.

audit.failed

Fired when an audit encounters a fatal error and cannot complete.

Planned payload shape

JSON
{
  "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.

Contact us