SearchJetEngine

Error Handling

# Error Handling SearchJet Engine API returns structured JSON error responses across all platforms. This guide covers error formats, HTTP status codes, and handling strategies for JavaScript, Python, PHP, and other environments. --- ## Error Response Format Every error response follows a consistent JSON structure: ```json { "status": "error", "error": "validation_failed", "message": "The q parameter is required for search requests.", "status_code": 400, "documentation_url": "https://docs.searchjetengine.com/errors/validation_failed" } ``` | Field | Type | Description | |-------|------|-------------| | `status` | string | Always `"error"` for error responses | | `error` | string | Machine-readable error code (snake_case) | | `message` | string | Human-readable explanation | | `status_code` | integer | HTTP status code | | `documentation_url` | string | Link to relevant documentation | --- ## HTTP Status Codes ### 200 OK Request succeeded. Used for successful GET, PUT, and search operations. ```json { "status": "ok", "results": [], "total": 0 } ``` ### 201 Created Resource successfully created. Returned by POST requests that create new resources. ```json { "status": "ok", "data": { "id": "site_abc123", "name": "My Site", "created_at": "2025-01-15T10:30:00Z" } } ``` ### 400 Bad Request The request is malformed or contains invalid parameters. **Common causes:** - Missing required fields in the request body - Invalid JSON syntax - Invalid query parameters - Invalid date formats (use YYYY-MM-DD) - Value out of allowed range ```json { "status": "error", "error": "validation_failed", "message": "The per_page parameter must be between 1 and 100.", "status_code": 400 } ``` ### 401 Unauthorized Authentication failed. The API key is missing, invalid, or expired. **Common causes:** - Missing `Authorization` header - Malformed Bearer token - Expired or revoked API key ```json { "status": "error", "error": "unauthorized", "message": "Invalid API key provided.", "status_code": 401 } ``` ### 403 Forbidden The request is authenticated but the key lacks permission for this operation. **Common causes:** - Public key (`sj_pub_`) used for admin operations - Key does not have access to the requested site - Key permissions do not include the operation ```json { "status": "error", "error": "forbidden", "message": "This key does not have permission to manage sites.", "status_code": 403 } ``` ### 404 Not Found The requested resource does not exist. **Common causes:** - Incorrect `site_id` or `doc_id` - Resource was deleted - Typo in the endpoint URL ```json { "status": "error", "error": "not_found", "message": "Site with ID 'xyz789' was not found.", "status_code": 404 } ``` ### 429 Too Many Requests Rate limit exceeded. Check the `Retry-After` header for wait time. **Common causes:** - Exceeded per-minute request limit - Exceeded daily quota ```json { "status": "error", "error": "rate_limited", "message": "Rate limit exceeded. Try again in 45 seconds.", "status_code": 429 } ``` Headers included: ``` X-RateLimit-Limit: 300 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1690000045 Retry-After: 45 ``` ### 500 Internal Server Error An unexpected error occurred on the server. If this persists, contact support. ```json { "status": "error", "error": "internal_error", "message": "An unexpected error occurred. Please try again later.", "status_code": 500 } ``` --- ## Handling Errors in JavaScript ```javascript async function searchWithRetry(query, siteId) { const maxRetries = 3; let attempt = 0; while (attempt < maxRetries) { try { const response = await fetch( `https://app.searchjetengine.com/api/v1/search?q=${encodeURIComponent(query)}&site_id=${siteId}`, { headers: { 'Authorization': 'Bearer sj_prv_xxxxxxxxxxxxxxxx' } } ); if (!response.ok) { const error = await response.json(); if (response.status === 429) { const retryAfter = response.headers.get('Retry-After') || 5; console.warn(`Rate limited. Retrying in ${retryAfter}s...`); await new Promise(r => setTimeout(r, retryAfter * 1000)); attempt++; continue; } throw new Error(`${error.error}: ${error.message}`); } return await response.json(); } catch (err) { if (attempt === maxRetries - 1) throw err; attempt++; await new Promise(r => setTimeout(r, 1000 * attempt)); } } } // Usage try { const results = await searchWithRetry('react tutorial', 'site_abc'); console.log(results); } catch (err) { console.error('Search failed:', err.message); } ``` --- ## Handling Errors in Python ```python import httpx import time def search_with_retry(query: str, site_id: str, max_retries: int = 3) -> dict: url = "https://app.searchjetengine.com/api/v1/search" headers = {"Authorization": "Bearer sj_prv_xxxxxxxxxxxxxxxx"} for attempt in range(max_retries): try: response = httpx.get( url, params={"q": query, "site_id": site_id}, headers=headers, timeout=10.0 ) if response.status_code == 429: retry_after = int(response.headers.get("Retry-After", 5)) print(f"Rate limited. Retrying in {retry_after}s...") time.sleep(retry_after) continue if response.status_code >= 400: error = response.json() raise Exception(f"{error['error']}: {error['message']}") return response.json() except httpx.TimeoutException: if attempt == max_retries - 1: raise time.sleep(1 * (attempt + 1)) raise Exception("Max retries exceeded") # Usage try: results = search_with_retry("react tutorial", "site_abc") print(results) except Exception as e: print(f"Search failed: {e}") ``` --- ## Handling Errors in PHP ```php use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; function searchWithRetry(string $query, string $siteId, int $maxRetries = 3): array { $client = new Client([ 'base_uri' => 'https://app.searchjetengine.com/api/v1', 'headers' => [ 'Authorization' => 'Bearer sj_prv_xxxxxxxxxxxxxxxx', 'Accept' => 'application/json' ] ]); for ($attempt = 0; $attempt < $maxRetries; $attempt++) { try { $response = $client->get('/search', [ 'query' => ['q' => $query, 'site_id' => $siteId] ]); return json_decode($response->getBody(), true); } catch (ClientException $e) { $response = $e->getResponse(); $statusCode = $response->getStatusCode(); $body = json_decode($response->getBody(), true); if ($statusCode === 429) { $retryAfter = $response->hasHeader('Retry-After') ? (int) $response->getHeaderLine('Retry-After') : 5; sleep($retryAfter); continue; } throw new \Exception( "{$body['error']}: {$body['message']}" ); } } throw new \Exception('Max retries exceeded'); } // Usage try { $results = searchWithRetry('react tutorial', 'site_abc'); print_r($results); } catch (\Exception $e) { echo "Search failed: " . $e->getMessage(); } ``` --- ## Handling Errors in Laravel If using the SearchJet Laravel package, errors are thrown as exceptions: ```php use SearchJet\Facades\SearchJet; use SearchJet\Exceptions\RateLimitedException; use SearchJet\Exceptions\SearchJetException; try { $results = SearchJet::search('react tutorial', [ 'site_id' => 'site_abc', 'per_page' => 10 ]); } catch (RateLimitedException $e) { $retryAfter = $e->getRetryAfter(); Log::warning("SearchJet rate limited, retry in {$retryAfter}s"); // Retry after delay } catch (SearchJetException $e) { Log::error("SearchJet error: " . $e->getMessage()); } ``` --- ## Rate Limit Handling Rate limits are enforced per API key. Handle 429 responses gracefully: 1. **Check the `Retry-After` header** — it contains the number of seconds to wait 2. **Implement exponential backoff** — increase wait time on repeated failures 3. **Queue requests** — use a request queue for high-volume operations 4. **Cache results** — reduce API calls by caching frequent queries ### Exponential Backoff Strategy | Attempt | Wait Time | |---------|-----------| | 1 | 1 second | | 2 | 2 seconds | | 3 | 4 seconds | | 4 | 8 seconds | Always respect the `Retry-After` header. If the header indicates 30 seconds, wait at least 30 seconds regardless of your backoff calculation. --- ## Error Codes Reference | Error Code | HTTP Status | Description | |------------|-------------|-------------| | `validation_failed` | 400 | Invalid request parameters | | `missing_parameter` | 400 | Required parameter not provided | | `invalid_json` | 400 | Request body is not valid JSON | | `unauthorized` | 401 | Invalid or missing API key | | `forbidden` | 403 | Key lacks required permissions | | `not_found` | 404 | Resource does not exist | | `rate_limited` | 429 | Too many requests | | `internal_error` | 500 | Server-side error | --- ## Best Practices 1. **Always handle errors** — Never assume requests will succeed 2. **Log errors** — Include the `error` code and `message` for debugging 3. **Implement retry logic** — For 429 and 500 errors with exponential backoff 4. **Validate inputs client-side** — Reduce 400 errors by validating before sending 5. **Check status before parsing** — Verify the response `status` field before accessing data 6. **Use timeouts** — Prevent hanging requests in all languages --- ## See Also - [API Documentation](/docs/api-documentation) — Full API guide - [API Endpoints Reference](/docs/endpoints) — All endpoints - [API Key Setup](/docs/api-key-setup) — Authentication setup