Error Handling Guide
Learn how to properly handle errors from the GTMAPIs validation API.Error Response Format
All errors return an appropriate HTTP status code with JSON body:code when it is present; error is written for people and may be
clarified over time. Official clients preserve server-provided codes and fall
back to a stable status-derived code when an older response does not include one.
Request Correlation
Every application response documented in OpenAPI includes an opaqueX-Request-ID header. Preserve the ID from the terminal response and include it
in support reports so the server-side trace can be located. Do not reuse an ID
from an earlier retry when the final attempt fails before receiving an HTTP
response; that network-only failure has no server-authored request ID.
Official SDK errors, CLI errors, and MCP structured errors expose the sanitized
identifier directly. It is diagnostic metadata only and never affects whether
an address is valid, chargeable, suppressed, or safe to send.
HTTP Status Codes
For example, a validation request that cannot reserve credits returns
code: "insufficient_credits"; a temporary validation dependency failure
returns code: "validation_upstream_failed".
When present, required is the reservation needed for the request and
available is the balance observed by the API. Official SDKs, the CLI, and MCP
preserve both fields; treat available as optional because older and
synchronous responses may omit it.
Official SDKs, the CLI, and the local MCP package automatically attach
idempotency keys to single validation, synchronous bulk, and asynchronous job
writes. Supply a durable caller key when the same logical request may be
retried after a process restart or across agent turns. Reuse it only with the
identical request body; changed semantics return idempotency_key_conflict.
Clients retry network/timeouts, idempotency_request_in_progress, rate limits,
and 5xx responses with bounded attempt budgets while preserving request
identity. They do not retry bad input, authentication, insufficient credits,
forbidden access, changed-body idempotency conflicts, or
control_plane_route_misconfigured. The last code needs operator repair, so an
automatic retry cannot make progress. Unknown future 5xx codes keep the
status-based retry fallback.
Email Validation Codes
Every error fromPOST /v1/validate, POST /v1/validate/bulk, and
POST /v1/validate/batch carries a stable code. Use these codes to decide
whether to repair the request, split it, preserve its idempotency key, or retry:
An email string with invalid syntax is not a request-shape error. It remains a
successful validation response with
result: "invalid", so bulk callers keep
one result per submitted string.
Signed CSV Write Codes
The signed-upload preparation and processing-start routes use these additional codes. Preserve the same idempotency key when the action says to retry:Catch-All Recovery Codes
Recovery start also usesjob_not_found, invalid_job_state, and
insufficient_credits. Its read and operational failures add these codes:
Asynchronous Job Read Codes
The list, detail, and download routes use these additional codes:Email Outcome Codes
Outcome import reusesinvalid_json and payload_too_large. Import, report,
and EmailBison connector routes use these additional codes:
Recovery Feedback Codes
The gated catch-all recovery feedback import reusesinvalid_json,
payload_too_large, missing_outcomes, and too_many_outcomes.
Credit Balance Codes
API Gateway Codes
The public gateway uses these codes when it cannot reach the Product Control Plane. Preserve the request’s idempotency key for a retryable write.Authentication and Access Codes
Every rejection from the shared gates — API key, scope, subscription, product availability, rate limit — carries acode. Each one has a different fix, so
branch on the code rather than on the status alone:
api_key_expired, api_key_inactive, and api_key_revoked are deliberately
separate codes even where the human-readable error string is the same,
because expiry is routine rotation, deactivation is reversible, and revocation
is not. That distinction is only available in code.
Codes are append-only. A code that exists keeps its meaning; a condition that
later needs its own code gets a new one, so a switch you wrote today keeps
matching everything it already handles.
Common Errors
400 Bad Request
Cause: Invalid request format or missing required fieldsMissing Email Field
email field in request body
Invalid Email Syntax
Too Many Emails in Bulk Request
401 Unauthorized
Cause: Missing or invalid API keyMissing API Key Header
X-API-Key header
Invalid API Key
Revoked API Key
403 Forbidden
When a key lacks permission, the API returns the exact scope needed for the endpoint:required_scope to the key,
or rotate to a least-privilege key that includes it, and then retry. Branch on
code; do not parse the human-readable error string.
Other product or entitlement gates can also return 403 without
required_scope. Treat those as non-retryable access failures and preserve the
human-readable error for the operator.
429 Too Many Requests
Cause: Rate limit exceeded (10,000 requests/minute per API key or 1,000 requests/minute per IP)500 Internal Server Error
Cause: Unexpected server error503 Service Unavailable
Cause: Service temporarily unavailable (maintenance or overload)Validation Result Errors
Even with HTTP 200, emails can have validation issues:Invalid Email
Risky Email
Unknown Verification
Error Handling Patterns
Comprehensive Error Handler
Bulk Validation Error Handling
Monitoring and Logging
Log Errors for Analysis
Track Error Rates
Next Steps
Rate Limits
Understand rate limiting strategies
API Integration
Full integration patterns