Overview
The InstaView API uses standard HTTP status codes and returns structured error responses to help you diagnose and handle issues in your integration.Response Structure
All API responses follow a consistent format:{
"success": boolean,
"data": object | array | null,
"error": {
"code": string,
"message": string,
"details": object
} | null,
"timestamp": string
}
Success Response
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Senior Engineer"
},
"error": null,
"timestamp": "2024-01-15T10:30:00Z"
}
Error Response
{
"success": false,
"data": null,
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Job with ID 550e8400-e29b-41d4-a716-446655440000 not found",
"details": {
"resourceType": "Job",
"resourceId": "550e8400-e29b-41d4-a716-446655440000"
}
},
"timestamp": "2024-01-15T10:30:00Z"
}
HTTP Status Codes
- 2xx Success
- 4xx Client Errors
- 5xx Server Errors
| Code | Meaning | Usage |
|---|---|---|
| 200 | OK | Successful GET, PATCH, DELETE requests |
| 201 | Created | Successful POST request creating a resource |
| Code | Meaning | Common Causes |
|---|---|---|
| 400 | Bad Request | Invalid request body, validation errors |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Insufficient permissions, wrong company |
| 404 | Not Found | Resource doesn’t exist |
| 409 | Conflict | Duplicate resource, state conflict |
| 422 | Unprocessable | Valid JSON but semantic errors |
| 429 | Too Many Requests | Rate limit exceeded |
| Code | Meaning | Action |
|---|---|---|
| 500 | Internal Server Error | Retry with exponential backoff |
| 502 | Bad Gateway | Temporary issue, retry |
| 503 | Service Unavailable | Maintenance, retry later |
| 504 | Gateway Timeout | Request timeout, retry |
Error Codes
Authentication Errors
The provided API key is invalid, revoked, or expiredSolutions:
{
"error": {
"code": "INVALID_API_KEY",
"message": "The provided API key is invalid or has been revoked"
}
}
- Verify API key format
- Check if key was revoked in dashboard
- Ensure key hasn’t expired
The API key has been temporarily suspendedSolution: Contact support or check dashboard for details
{
"error": {
"code": "API_KEY_SUSPENDED",
"message": "This API key has been suspended",
"details": {
"suspendedAt": "2024-01-10T00:00:00Z",
"reason": "Unusual activity detected"
}
}
}
Permission Errors
API key lacks required scopeSolution: Add required scope to API key or create new key
{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"message": "This API key does not have the required scope: write:jobs",
"details": {
"required": "write:jobs",
"available": ["read:jobs", "read:candidates"]
}
}
}
Resource belongs to different companySolutions:
{
"error": {
"code": "RESOURCE_ACCESS_DENIED",
"message": "Access denied to this resource"
}
}
- Verify resource belongs to your company
- Check if using correct companyId (ATS keys)
- Ensure resource hasn’t been deleted
Validation Errors
Request validation failedSolution: Fix validation errors in request body
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": {
"fields": {
"email": "Invalid email format",
"phoneNumber": "Phone number is required",
"jobId": "Must be a valid UUID"
}
}
}
}
Required field not provided
{
"error": {
"code": "MISSING_REQUIRED_FIELD",
"message": "Missing required field: jobId",
"details": {
"field": "jobId",
"type": "string (UUID)"
}
}
}
Resource Errors
Requested resource doesn’t exist
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Job not found",
"details": {
"resourceType": "Job",
"resourceId": "550e8400-e29b-41d4-a716-446655440000"
}
}
}
Resource already exists or state conflict
{
"error": {
"code": "RESOURCE_CONFLICT",
"message": "A candidate with this email already exists for this job",
"details": {
"conflictingField": "email",
"existingResourceId": "candidate-uuid"
}
}
}
Rate Limiting
Too many requestsSolution: Wait for
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please retry after 45 seconds",
"details": {
"retryAfter": 45,
"limit": 300,
"window": "minute"
}
}
}
retryAfter seconds, implement exponential backoffBusiness Logic Errors
Company billing limit reachedSolution: Upgrade plan or wait for monthly reset
{
"error": {
"code": "BILLING_LIMIT_EXCEEDED",
"message": "Monthly interview limit exceeded",
"details": {
"limit": 100,
"used": 100,
"resetDate": "2024-02-01T00:00:00Z"
}
}
}
Invalid resource state change
{
"error": {
"code": "INVALID_STATE_TRANSITION",
"message": "Cannot delete job with active candidates",
"details": {
"currentState": "active",
"attemptedAction": "delete",
"reason": "Job has 5 active candidates"
}
}
}
Error Handling Patterns
Basic Error Handling
async function createJob(jobData) {
try {
const response = await fetch('https://api.instaview.sk/jobs', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.INSTAVIEW_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(jobData)
});
const result = await response.json();
if (!result.success) {
throw new InstaViewError(result.error);
}
return result.data;
} catch (error) {
console.error('Failed to create job:', error.message);
throw error;
}
}
class InstaViewError extends Error {
constructor(errorData) {
super(errorData.message);
this.code = errorData.code;
this.details = errorData.details;
this.name = 'InstaViewError';
}
}
import requests
class InstaViewError(Exception):
def __init__(self, error_data):
self.code = error_data['code']
self.message = error_data['message']
self.details = error_data.get('details', {})
super().__init__(self.message)
def create_job(job_data):
try:
response = requests.post(
'https://api.instaview.sk/jobs',
headers={
'Authorization': f'Bearer {os.environ["INSTAVIEW_API_KEY"]}',
'Content-Type': 'application/json'
},
json=job_data
)
result = response.json()
if not result['success']:
raise InstaViewError(result['error'])
return result['data']
except InstaViewError as e:
print(f'Failed to create job: {e.code} - {e.message}')
raise
<?php
class InstaViewError extends Exception {
public $code;
public $details;
public function __construct($errorData) {
$this->code = $errorData['code'];
$this->details = $errorData['details'] ?? [];
parent::__construct($errorData['message']);
}
}
function createJob($jobData) {
$apiKey = getenv('INSTAVIEW_API_KEY');
$ch = curl_init('https://api.instaview.sk/jobs');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($jobData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$result = json_decode($response, true);
curl_close($ch);
if (!$result['success']) {
throw new InstaViewError($result['error']);
}
return $result['data'];
}
try {
$job = createJob($jobData);
} catch (InstaViewError $e) {
echo "Failed: {$e->code} - {$e->getMessage()}\n";
}
?>
Comprehensive Error Handler
class InstaViewClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseURL = 'https://api.instaview.sk/v1';
}
async request(method, path, data = null) {
try {
const response = await fetch(`${this.baseURL}${path}`, {
method,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: data ? JSON.stringify(data) : null
});
const result = await response.json();
if (!result.success) {
return this.handleError(result.error, response.status);
}
return result.data;
} catch (error) {
console.error('Network error:', error);
throw error;
}
}
handleError(error, statusCode) {
switch (error.code) {
case 'INVALID_API_KEY':
case 'API_KEY_SUSPENDED':
console.error('Authentication error:', error.message);
// Notify admin, stop operations
throw new AuthenticationError(error);
case 'INSUFFICIENT_PERMISSIONS':
console.error('Permission denied:', error.message);
console.error('Required:', error.details.required);
console.error('Available:', error.details.available);
throw new PermissionError(error);
case 'VALIDATION_ERROR':
console.error('Validation failed:', error.details.fields);
// Show user-friendly messages
throw new ValidationError(error);
case 'RATE_LIMIT_EXCEEDED':
console.log(`Rate limited. Retry after ${error.details.retryAfter}s`);
// Implement retry logic
throw new RateLimitError(error);
case 'RESOURCE_NOT_FOUND':
console.warn('Resource not found:', error.details);
return null; // Handle gracefully
case 'BILLING_LIMIT_EXCEEDED':
console.error('Billing limit exceeded:', error.message);
// Notify admin, show upgrade prompt
throw new BillingError(error);
default:
console.error('Unknown error:', error);
throw new InstaViewError(error);
}
}
}
Retry with Exponential Backoff
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
// Don't retry client errors (except rate limits)
if (error.statusCode >= 400 && error.statusCode < 500) {
if (error.code === 'RATE_LIMIT_EXCEEDED') {
const delay = error.details.retryAfter * 1000 || baseDelay;
await sleep(delay);
continue;
}
throw error; // Don't retry other client errors
}
// Retry server errors with exponential backoff
if (error.statusCode >= 500) {
const delay = baseDelay * Math.pow(2, attempt);
console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`);
await sleep(delay);
continue;
}
throw error;
}
}
throw lastError;
}
// Usage
const job = await retryWithBackoff(async () => {
return await client.createJob(jobData);
});
Validation Error Handling
Handle field-level validation errors:async function createCandidateWithValidation(candidateData) {
try {
return await client.createCandidate(candidateData);
} catch (error) {
if (error.code === 'VALIDATION_ERROR') {
const fieldErrors = error.details.fields;
// Display user-friendly errors
for (const [field, message] of Object.entries(fieldErrors)) {
showFieldError(field, message);
}
// Or collect all errors
const errors = Object.entries(fieldErrors).map(([field, msg]) => ({
field,
message: msg
}));
return { success: false, errors };
}
throw error;
}
}
function showFieldError(field, message) {
// Display error next to form field
document.getElementById(`${field}-error`).textContent = message;
}
Idempotency
Implement idempotency for safe retries:async function createJobIdempotent(jobData, idempotencyKey) {
try {
return await fetch('https://api.instaview.sk/jobs', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey // Use same key for retries
},
body: JSON.stringify(jobData)
});
} catch (error) {
// Safe to retry with same idempotency key
return await fetch(/* same request */);
}
}
// Generate idempotency key
const idempotencyKey = `job-create-${Date.now()}-${Math.random()}`;
await createJobIdempotent(jobData, idempotencyKey);
Logging and Monitoring
Log errors for debugging and monitoring:class ErrorLogger {
static log(error, context = {}) {
const errorLog = {
timestamp: new Date().toISOString(),
code: error.code,
message: error.message,
details: error.details,
statusCode: error.statusCode,
context: context,
stack: error.stack
};
// Send to logging service
console.error(JSON.stringify(errorLog));
// Send to error tracking (Sentry, etc.)
if (process.env.NODE_ENV === 'production') {
Sentry.captureException(error, {
extra: errorLog
});
}
}
}
// Usage
try {
await createJob(jobData);
} catch (error) {
ErrorLogger.log(error, {
operation: 'createJob',
jobData: jobData,
userId: currentUser.id
});
throw error;
}
Best Practices
Always Check success Field
Always Check success Field
const response = await fetch(url);
const result = await response.json();
if (!result.success) {
// Handle error
throw new Error(result.error.message);
}
return result.data;
Provide Context in Errors
Provide Context in Errors
class DetailedError extends Error {
constructor(error, context) {
super(error.message);
this.code = error.code;
this.details = error.details;
this.context = context; // Add operation context
}
}
throw new DetailedError(apiError, {
operation: 'createCandidate',
candidateEmail: data.email,
jobId: data.jobId
});
Don't Expose Sensitive Data
Don't Expose Sensitive Data
// ❌ Don't expose sensitive data in logs
console.error('Failed:', error, apiKey);
// ✅ Log safely
console.error('Failed:', error.code, error.message);
Implement Circuit Breakers
Implement Circuit Breakers
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.failureCount = 0;
this.threshold = threshold;
this.timeout = timeout;
this.state = 'CLOSED';
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;
}
}
}
Next Steps
Rate Limiting
Handle rate limits properly
Best Practices
Production-ready patterns
Authentication
Fix authentication errors
API Reference
See all error codes per endpoint