API Reference
API Overview
Base URL, authentication, error format, and full error code reference
Base URL
https://openapi.thunderbit.com/openapi/v1Authentication
All endpoints use HTTP Bearer (API Key):
Authorization: Bearer YOUR_API_KEYGet your key from the Thunderbit Dashboard. Keys are revocable and per-environment — never embed a production key in client-side code.
Error envelope
All error responses share the same envelope. By default (LEGACY format):
{
"success": false,
"error": {
"code": "INVALID_URL",
"status": 400,
"message": "The provided URL is not valid",
"details": null
}
}Set the gateway environment variable API_ERROR_FORMAT=GOOGLE_RPC to switch to the Google Cloud / gRPC convention:
{
"success": false,
"error": {
"code": 400,
"status": "INVALID_URL",
"message": "The provided URL is not valid",
"details": null
}
}Clients should read both fields and treat the string-typed one as the canonical error code.
The details field may carry structured context — for example, field-level validation errors as { field: message } pairs returned on INVALID_PARAMETER.
Canonical error codes
| HTTP | Code | Meaning |
|---|---|---|
| 400 | INVALID_URL | URL format is invalid |
| 400 | INVALID_SCHEMA | JSON Schema is invalid |
| 400 | INVALID_PARAMETER | One or more request parameters failed validation |
| 400 | SCHEMA_OR_PROMPT_REQUIRED | schema (or prompt) is required for extraction |
| 400 | SCHEMA_AND_PROMPT_EXCLUSIVE | schema and prompt cannot both be provided |
| 400 | BATCH_SIZE_EXCEEDED | Batch request exceeds the 100-URL limit |
| 400 | MALFORMED_REQUEST_BODY | Request body is not valid JSON |
| 401 | API_KEY_MISSING | Authorization header missing |
| 401 | API_KEY_INVALID_FORMAT | API key format is invalid |
| 401 | API_KEY_NOT_FOUND | API key not found in the system |
| 401 | API_KEY_REVOKED | API key has been revoked |
| 401 | API_KEY_DISABLED | API key has been disabled |
| 401 | API_KEY_EXPIRED | API key has expired |
| 401 | INVALID_API_KEY | (deprecated, use the specific API_KEY_* codes above) |
| 402 | INSUFFICIENT_CREDITS | Not enough credits on the account |
| 404 | JOB_NOT_FOUND | Batch job not found |
| 404 | RESOURCE_NOT_FOUND | Generic resource not found |
| 408 | REQUEST_TIMEOUT | The API request timed out |
| 408 | SCRAPE_TIMEOUT | The target page took too long to respond |
| 422 | SCRAPE_SSL_ERROR | Target site has SSL/TLS problems |
| 422 | SCRAPE_DNS_RESOLUTION_ERROR | Cannot resolve target hostname |
| 422 | SCRAPE_SITE_ERROR | Target site returned an error |
| 422 | SCRAPE_EMPTY_CONTENT | Target page returned empty content |
| 422 | SCRAPE_CONTENT_TOO_LARGE | Target page exceeds size limits |
| 422 | SCRAPE_TARGET_FORBIDDEN | Target site refused access (403) |
| 422 | SCRAPE_TARGET_NOT_FOUND | Target URL returned 404 |
| 422 | SCRAPE_UNSUPPORTED_FILE | Target file type is not supported |
| 429 | RATE_LIMIT_EXCEEDED | Account rate limit triggered |
| 429 | SCRAPE_TARGET_RATE_LIMITED | Target site rate-limited our request |
| 500 | INTERNAL_ERROR | Generic internal error |
| 500 | DISTILL_FAILED | Distillation pipeline failed |
| 500 | EXTRACT_FAILED | Extraction pipeline failed |
| 500 | PIPELINE_ERROR | Pipeline step reported a failure |
| 500 | AI_EXTRACTION_FAILED | AI extraction step failed |
| 500 | MARKDOWN_CONVERSION_FAILED | HTML-to-Markdown conversion failed |
| 502 | SCRAPE_ALL_PROVIDERS_FAILED | All scraping providers failed |
| 502 | UPSTREAM_BAD_GATEWAY | Upstream returned an invalid response |
| 503 | SCRAPE_PROVIDER_UNAVAILABLE | Scraping provider is unavailable |
| 503 | AI_SERVICE_UNAVAILABLE | AI service is unavailable |
| 503 | DOWNSTREAM_SERVICE_UNAVAILABLE | A downstream service is temporarily unavailable |
| 504 | UPSTREAM_TIMEOUT | Upstream gateway timed out |
| 504 | AI_TIMEOUT | AI service call timed out |
Retry strategy reference
| Code class | Retry? | How |
|---|---|---|
4xx (your input) | ❌ No | Fix the request and try again |
401 (auth) | ❌ No | Rotate / re-issue API key |
402 (credits) | ❌ No | Top up |
408 / 504 (timeout) | ✅ Yes | Exponential backoff, max 3 attempts |
429 (rate-limited) | ✅ Yes | Wait until X-RateLimit-Reset — see Rate Limits |
5xx (server) | ✅ Yes | Exponential backoff, max 3 attempts |
SCRAPE_TARGET_* (target site) | ⚠️ Maybe | Retry once with renderMode upgraded |