Documentation Index Fetch the complete documentation index at: https://gtmapis.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Error Handling Guide
Learn how to properly handle errors from the GTMAPIs validation API.
All errors return an appropriate HTTP status code with JSON body:
{
"error" : "Error Type" ,
"message" : "Detailed error message"
}
HTTP Status Codes
Status Error Description 200 Success Request processed successfully 400 Bad Request Invalid request format or parameters 401 Unauthorized Missing or invalid API key 429 Too Many Requests Rate limit exceeded 500 Internal Server Error Unexpected server error 503 Service Unavailable Service temporarily unavailable
Common Errors
400 Bad Request
Cause : Invalid request format or missing required fields
Missing Email Field
{
"error" : "Bad Request" ,
"message" : "Email field is required"
}
Solution : Include email field in request body
// ❌ Wrong
fetch ( 'https://api.gtmapis.com/v1/validate' , {
body: JSON . stringify ({})
});
// ✅ Correct
fetch ( 'https://api.gtmapis.com/v1/validate' , {
body: JSON . stringify ({ email: 'john@company.com' })
});
{
"error" : "Bad Request" ,
"message" : "Invalid email format"
}
Solution : Validate email format before sending
function isValidEmailFormat ( email ) {
return / ^ [ ^ \s@ ] + @ [ ^ \s@ ] + \. [ ^ \s@ ] + $ / . test ( email );
}
if ( ! isValidEmailFormat ( email )) {
throw new Error ( 'Invalid email format' );
}
Too Many Emails in Bulk Request
{
"error" : "Bad Request" ,
"message" : "Maximum 100 emails per request. Received: 150"
}
Solution : Split into batches of 100
function chunkArray ( array , size ) {
const chunks = [];
for ( let i = 0 ; i < array . length ; i += size ) {
chunks . push ( array . slice ( i , i + size ));
}
return chunks ;
}
const batches = chunkArray ( emails , 100 );
for ( const batch of batches ) {
await validateBulk ( batch );
}
401 Unauthorized
Cause : Missing or invalid API key
{
"error" : "Unauthorized" ,
"message" : "X-API-Key header is required"
}
Solution : Always include X-API-Key header
// ❌ Wrong
fetch ( 'https://api.gtmapis.com/v1/validate' , {
headers: {
'Content-Type' : 'application/json'
}
});
// ✅ Correct
fetch ( 'https://api.gtmapis.com/v1/validate' , {
headers: {
'Content-Type' : 'application/json' ,
'X-API-Key' : process . env . GTMAPIS_API_KEY
}
});
Invalid API Key
{
"error" : "Unauthorized" ,
"message" : "Invalid API key"
}
Solution : Verify API key format and validity
// Check API key format
if ( ! apiKey || ! apiKey . match ( / ^ gtm_ ( test | live ) _ [ a-f0-9 ] {32} $ / )) {
throw new Error ( 'Invalid API key format' );
}
Revoked API Key
{
"error" : "Unauthorized" ,
"message" : "API key has been revoked"
}
Solution : Generate a new API key from the dashboard
429 Too Many Requests
Cause : Rate limit exceeded (1000 requests/minute)
{
"error" : "Rate limit exceeded" ,
"message" : "You have exceeded the rate limit of 1000 requests per minute" ,
"retry_after" : 60
}
Solution : Implement retry logic with exponential backoff
async function validateWithRetry ( email , maxRetries = 3 ) {
for ( let attempt = 1 ; attempt <= maxRetries ; attempt ++ ) {
try {
const response = await fetch ( 'https://api.gtmapis.com/v1/validate' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'X-API-Key' : process . env . GTMAPIS_API_KEY
},
body: JSON . stringify ({ email })
});
if ( response . status === 429 ) {
const retryAfter = parseInt ( response . headers . get ( 'Retry-After' ) || '60' );
console . log ( `Rate limited. Waiting ${ retryAfter } s...` );
await new Promise ( resolve => setTimeout ( resolve , retryAfter * 1000 ));
continue ;
}
if ( ! response . ok ) {
throw new Error ( `HTTP ${ response . status } ` );
}
return await response . json ();
} catch ( error ) {
if ( attempt === maxRetries ) throw error ;
// Exponential backoff
const delay = Math . pow ( 2 , attempt ) * 1000 ;
await new Promise ( resolve => setTimeout ( resolve , delay ));
}
}
}
500 Internal Server Error
Cause : Unexpected server error
{
"error" : "Internal Server Error" ,
"message" : "An unexpected error occurred"
}
Solution : Retry request or contact support
async function validateWithErrorHandling ( email ) {
try {
return await validateEmail ( email );
} catch ( error ) {
if ( error . status === 500 ) {
// Log for investigation
console . error ( 'Server error:' , error );
// Retry once after delay
await new Promise ( resolve => setTimeout ( resolve , 5000 ));
return await validateEmail ( email );
}
throw error ;
}
}
503 Service Unavailable
Cause : Service temporarily unavailable (maintenance or overload)
{
"error" : "Service Unavailable" ,
"message" : "Service temporarily unavailable. Please try again later."
}
Solution : Implement circuit breaker pattern
class CircuitBreaker {
constructor ( threshold = 5 , timeout = 60000 ) {
this . failureCount = 0 ;
this . threshold = threshold ;
this . timeout = timeout ;
this . state = 'CLOSED' ; // CLOSED, OPEN, HALF_OPEN
this . nextAttempt = Date . now ();
}
async execute ( fn ) {
if ( this . state === 'OPEN' ) {
if ( Date . now () < this . nextAttempt ) {
throw new Error ( 'Circuit breaker is OPEN' );
}
this . state = 'HALF_OPEN' ;
}
try {
const result = await fn ();
this . onSuccess ();
return result ;
} catch ( error ) {
this . onFailure ();
throw error ;
}
}
onSuccess () {
this . failureCount = 0 ;
this . state = 'CLOSED' ;
}
onFailure () {
this . failureCount ++ ;
if ( this . failureCount >= this . threshold ) {
this . state = 'OPEN' ;
this . nextAttempt = Date . now () + this . timeout ;
}
}
}
// Usage
const breaker = new CircuitBreaker ();
async function validateWithCircuitBreaker ( email ) {
return await breaker . execute (() => validateEmail ( email ));
}
Validation Result Errors
Even with HTTP 200, emails can have validation issues:
Invalid Email
{
"email" : "notreal@fakeemail123.com" ,
"result" : "invalid" ,
"reason" : "Domain has no MX records" ,
"validation_layer" : "dns"
}
Handling :
if ( result . result === 'invalid' ) {
console . log ( `Email ${ result . email } is invalid: ${ result . reason } ` );
// Remove from list
removeFromList ( result . email );
}
Risky Email
{
"email" : "anyone@catchall.com" ,
"result" : "risky" ,
"reason" : "Catch-all domain detected"
}
Handling :
if ( result . result === 'risky' ) {
console . log ( `Email ${ result . email } is risky: ${ result . reason } ` );
// Mark as low priority or exclude
markAsLowPriority ( result . email );
}
Unknown Verification
{
"email" : "john@restrictive.com" ,
"result" : "unknown" ,
"reason" : "SMTP verification not permitted"
}
Handling :
if ( result . result === 'unknown' ) {
console . log ( `Cannot verify ${ result . email } : ${ result . reason } ` );
// Test with small batch first
addToTestBatch ( result . email );
}
Error Handling Patterns
Comprehensive Error Handler
async function validateEmailSafely ( email ) {
try {
const response = await fetch ( 'https://api.gtmapis.com/v1/validate' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'X-API-Key' : process . env . GTMAPIS_API_KEY
},
body: JSON . stringify ({ email })
});
// Handle HTTP errors
if ( ! response . ok ) {
switch ( response . status ) {
case 400 :
throw new Error ( 'Invalid request format' );
case 401 :
throw new Error ( 'Invalid API key - check your credentials' );
case 429 :
const retryAfter = response . headers . get ( 'Retry-After' ) || 60 ;
throw new Error ( `Rate limited - retry after ${ retryAfter } s` );
case 500 :
throw new Error ( 'Server error - please retry' );
case 503 :
throw new Error ( 'Service unavailable - try again later' );
default :
throw new Error ( `HTTP ${ response . status } ` );
}
}
const result = await response . json ();
// Handle validation results
switch ( result . result ) {
case 'valid' :
return { status: 'success' , data: result };
case 'valid_role_based' :
return {
status: 'warning' ,
message: 'Role-based email (info@, support@)' ,
data: result
};
case 'risky' :
return {
status: 'warning' ,
message: 'Catch-all domain - cannot verify mailbox' ,
data: result
};
case 'invalid' :
return {
status: 'error' ,
message: result . reason || 'Invalid email' ,
data: result
};
case 'unknown' :
return {
status: 'warning' ,
message: 'Cannot verify - server restrictions' ,
data: result
};
default :
return {
status: 'error' ,
message: 'Unexpected result type' ,
data: result
};
}
} catch ( error ) {
// Network or parsing errors
console . error ( 'Validation error:' , error );
return {
status: 'error' ,
message: error . message ,
data: null
};
}
}
// Usage
const result = await validateEmailSafely ( 'john@company.com' );
if ( result . status === 'success' ) {
// Email is valid
console . log ( 'Valid email:' , result . data . email );
} else if ( result . status === 'warning' ) {
// Email has issues but may be usable
console . warn ( result . message , result . data );
} else {
// Email is invalid or error occurred
console . error ( result . message );
}
Bulk Validation Error Handling
async function validateBulkWithErrorHandling ( emails ) {
const results = [];
const errors = [];
for ( let i = 0 ; i < emails . length ; i += 100 ) {
const batch = emails . slice ( i , i + 100 );
try {
const response = await fetch ( 'https://api.gtmapis.com/v1/validate/bulk' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'X-API-Key' : process . env . GTMAPIS_API_KEY
},
body: JSON . stringify ({ emails: batch })
});
if ( ! response . ok ) {
// Log error but continue with next batch
errors . push ({
batch: i / 100 + 1 ,
emails: batch ,
error: `HTTP ${ response . status } `
});
continue ;
}
const batchResults = await response . json ();
results . push ( ... batchResults . results );
} catch ( error ) {
// Network error - log and continue
errors . push ({
batch: i / 100 + 1 ,
emails: batch ,
error: error . message
});
}
// Rate limit delay
await new Promise ( resolve => setTimeout ( resolve , 500 ));
}
return {
results ,
errors ,
summary: {
total: emails . length ,
validated: results . length ,
failed: errors . reduce (( sum , e ) => sum + e . emails . length , 0 )
}
};
}
Monitoring and Logging
Log Errors for Analysis
function logValidationError ( error , context ) {
console . error ( 'Validation Error:' , {
timestamp: new Date (). toISOString (),
error: error . message ,
stack: error . stack ,
context: {
email: context . email ,
apiKey: context . apiKey ?. substring ( 0 , 15 ) + '...' , // Partial key only
endpoint: context . endpoint
}
});
// Send to error tracking service
if ( process . env . SENTRY_DSN ) {
Sentry . captureException ( error , {
contexts: { validation: context }
});
}
}
Track Error Rates
class ErrorTracker {
constructor () {
this . errors = new Map ();
}
track ( errorType ) {
const count = this . errors . get ( errorType ) || 0 ;
this . errors . set ( errorType , count + 1 );
}
getStats () {
return Object . fromEntries ( this . errors );
}
reset () {
this . errors . clear ();
}
}
const errorTracker = new ErrorTracker ();
// Track errors
try {
await validateEmail ( email );
} catch ( error ) {
if ( error . status === 429 ) {
errorTracker . track ( 'rate_limit' );
} else if ( error . status === 500 ) {
errorTracker . track ( 'server_error' );
}
throw error ;
}
// Review stats periodically
setInterval (() => {
console . log ( 'Error stats:' , errorTracker . getStats ());
errorTracker . reset ();
}, 60000 );
Next Steps
Rate Limits Understand rate limiting strategies
API Integration Full integration patterns