TagHelper API
Programmatically run GTM container audits, retrieve scores, and integrate TagHelper into your CI/CD pipeline or client reporting workflow.
Base URL: https://taghelper.io/api/v1Get an API key Authentication
All API requests require authentication via a Bearer token in the Authorization header. API keys are available on the Pro and Agency plans.
Getting your API key:
- Go to Dashboard → API Keys
- Click Create Key and give it a name
- Copy the key immediately — it won't be shown again
Include the key in every request:
Authorization: Bearer th_live_xxxxxxxxxxxxKeep your API keys secret. Do not expose them in client-side code, public repositories, or browser requests. Use environment variables and server-side calls only.
Endpoints
/api/v1/scanStart a new GTM container audit. Returns a job ID that you can poll for status and results.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
container_id | string | Required | GTM container ID (e.g. "GTM-XXXXXX") or a URL containing a GTM container. |
workspace_type | string | Optional | "ecommerce" or "lead_generation". Defaults to "ecommerce". |
Response
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending"
}Example
curl -X POST https://taghelper.io/api/v1/scan \
-H "Authorization: Bearer th_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"container_id": "GTM-XXXXXX",
"workspace_type": "ecommerce"
}'/api/v1/scan/:jobIdCheck the status of a running audit. Poll this endpoint until status is "completed" or "failed".
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
jobId | string | Required | The job ID returned by POST /scan. |
Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"input_type": "gtm_id",
"input_value": "GTM-XXXXXX",
"workspace_type": "ecommerce",
"gtm_container_id": "GTM-XXXXXX",
"audit_id": "660e8400-e29b-41d4-a716-446655440001",
"score": 72,
"score_label": "Good",
"error_message": null,
"created_at": "2026-02-21T10:00:00.000Z",
"started_at": "2026-02-21T10:00:01.000Z",
"completed_at": "2026-02-21T10:00:12.000Z"
}Example
curl https://taghelper.io/api/v1/scan/JOB_ID \
-H "Authorization: Bearer th_live_xxxxxxxxxxxx"/api/v1/scan/:jobId/resultRetrieve the full audit results including scores, issues, detected platforms, and recommendations.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
jobId | string | Required | The job ID returned by POST /scan. |
Response
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"score": 72,
"score_label": "Good",
"workspace_type": "ecommerce",
"gtm_container_id": "GTM-XXXXXX",
"summary": "Your GTM container has a solid foundation...",
"category_scores": {
"core_analytics": 85,
"advertising_platforms": 60,
"conversion_tracking": 75,
"event_quality": 70,
"data_quality": 80
},
"platforms_detected": {
"ga4": true,
"facebook_pixel": true,
"google_ads": false,
"tiktok": false,
"linkedin": false,
"other": ["hotjar"]
},
"events_detected": {
"purchase": true,
"add_to_cart": true,
"begin_checkout": true,
"view_item": false,
"lead": false,
"form_submission": false,
"meeting_booking": false,
"content_download": false,
"custom_events": ["newsletter_signup"]
},
"issues": {
"critical": [
{
"category": "conversion_tracking",
"severity": "critical",
"message": "Missing Google Ads conversion tag",
"recommendation": "Add a Google Ads conversion tracking tag..."
}
],
"warnings": [
{
"category": "event_quality",
"severity": "warning",
"message": "view_item event not detected",
"recommendation": "Implement the view_item event..."
}
],
"passed": [
"GA4 configuration tag present",
"Facebook Pixel properly initialized"
]
},
"created_at": "2026-02-21T10:00:12.000Z"
}Example
curl https://taghelper.io/api/v1/scan/JOB_ID/result \
-H "Authorization: Bearer th_live_xxxxxxxxxxxx"Rate Limits
Rate limits vary by plan tier. Exceeding the limit returns a 429 response with a Retry-After header.
| Limit | Free | Pro | Agency |
|---|---|---|---|
| API access | — | ✓ | ✓ |
| Requests per minute | — | 60 | 100 |
| Audits per day | — | 200 | 1,000 |
| Audits per month | — | 50 | 500 |
| Max API keys | — | 3 | 5 |
Rate limit headers are included in every response:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1708520460Error Codes
All errors return a consistent JSON format:
{
"error": {
"code": "invalid_request",
"message": "container_id is required"
}
}| HTTP Status | Code | Description |
|---|---|---|
400 | invalid_request | Missing or invalid request parameters. |
401 | unauthorized | Missing or invalid API key. |
403 | forbidden | API access not available on your plan, or key revoked. |
404 | not_found | The requested resource (job, audit) was not found. |
422 | unprocessable_entity | Request body is valid JSON but contains semantic errors. |
429 | rate_limit_exceeded | Too many requests. Check Retry-After header. |
500 | internal_error | Something went wrong on our end. Retry or contact support. |
Full Examples
A complete workflow: start a scan, poll for completion, then fetch the full results.
1. Start the scan
curl -X POST https://taghelper.io/api/v1/scan \
-H "Authorization: Bearer th_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"container_id": "GTM-XXXXXX",
"workspace_type": "ecommerce"
}'2. Poll until complete
// Poll until complete (JavaScript)
async function waitForScan(jobId, apiKey) {
while (true) {
const res = await fetch(
`https://taghelper.io/api/v1/scan/${jobId}`,
{ headers: { Authorization: `Bearer ${apiKey}` } }
);
const data = await res.json();
if (data.status === "completed") return data;
if (data.status === "failed") throw new Error(data.error_message);
await new Promise((r) => setTimeout(r, 3000)); // wait 3s
}
}3. Fetch full results
curl https://taghelper.io/api/v1/scan/JOB_ID/result \
-H "Authorization: Bearer th_live_xxxxxxxxxxxx"