Invalid regex pattern in include.links.includePatterns
Empty search.query
Authentication Errors (401)
Returned when authentication fails:
{ "_tag": "AuthFailed", "reason": "InvalidApiKey", "description": "The provided API key is not valid"}
Reason
Description
InvalidApiKey
The API key is missing or invalid
InvalidToken
The bearer token is invalid
InvalidSession
The session has expired
InvalidTenant
Rate Limit Errors (429)
Returned when you exceed your rate limit. See Rate Limiting for details.
{ "_tag": "TooManyRequests"}
Fetch Errors (403, 413, 502, 504, 529)
Returned when Fetch cannot return page content. FetchCaptureTimeout is the conservative fallback when no publishable result arrived by the request deadline and Expand cannot prove the run stayed queue-only. It does not prove that capture started or that the target page caused the timeout. FetchCapacityTimeout means Expand positively identified a queue-only capacity timeout:
_tag
Code
Meaning
FetchBlocked
403
The site refused automated access (bot protection, auth wall, CAPTCHA).
The SDKs do not retry the page verdicts or FetchCaptureTimeout: 403 and 413 are not retryable statuses, and the 502 and 504 tags are explicitly exempted. Keeping ambiguous 504s out of automatic retry loops avoids amplifying a control-plane incident; a deliberate manual retry may still be appropriate. The SDKs do retry 529 FetchCapacityTimeout with their normal backoff; the response also advertises Retry-After for direct HTTP clients.
Capture Timeouts (504)
Returned when no publishable result reached the API inside the server-side request deadline and Expand could not prove the run remained queue-only. This includes captures known to have started and the conservative fallback when classification is missing, fails, or exceeds its five-second budget:
Synchronous Fetch uses one 150-second capture-work budget created before Hatchet dispatch. Dispatch, retries, worker reassignments, browser work, and result publication all share its immutable epoch. At expiry, timeout classification has one additional five-second budget and is disconnected from a hung control-plane call. A result that arrives during classification can still win and be returned successfully; otherwise, a typed timeout is produced by 155 seconds at the latest.
timeoutMs reports the configured request ceiling (150000 by default), not a remaining worker budget.
The SDK does not retry this automatically. This avoids retry amplification when the control plane is unhealthy; it is not proof the page was at fault. A deliberate manual retry may still be appropriate and starts a separate capture that can consume another full 150-second budget.
Use a synchronous client timeout of 180000 ms. If timeout classification wins, the server produces a typed timeout by 155 seconds, leaving 25 seconds for response serialization and network transit. The TypeScript SDK's timeoutMs default of 60000 aborts long captures client-side. See TypeScript SDK.
Batched items receive their epoch at browser-child dispatch. A child that first starts after expiry persists FetchCapacityTimeout; a replacement for a child that started earlier stays on the 504 side.
Capacity Timeouts (529)
Returned when Expand proves the request remained queue-only until the request deadline:
This is Expand capacity exhaustion, not a verdict about the target page.
Responses include Retry-After: 5, a short overload backoff that avoids an immediate retry stampede while using little of the next request's 150-second budget.
The TypeScript and Python SDKs retry this automatically. Direct HTTP clients should wait at least the advertised interval before retrying.
If a worker started and later disappeared, the request remains 504 FetchCaptureTimeout; the historical start proves it was not queue-only.
Internal Errors (500)
Returned when the request failed inside Expand rather than at the target page:
{ "_tag": "InternalError"}
Service Errors (503)
Returned when the service is temporarily unavailable:
{ "_tag": "ServiceUnavailable"}
This typically indicates a temporary issue. Retry your request after a short delay.
Handling Errors
With the TypeScript SDK
import Expand, { ExpandError, APIError, RateLimitError } from 'expandai'const client = new Expand({ apiKey: '{{API_KEY}}' })try { const result = await client.fetch({ url: 'https://example.com' })} catch (error) { if (error instanceof RateLimitError) { // Handle rate limiting - wait and retry console.log('Retry after:', error.retryAfter) } else if
With cURL
When using cURL or raw HTTP, check the response status code and parse the JSON body for error details:
Always check status codes before processing responses
Log error details including the _tag field for debugging
Implement retries for transient errors (429, 503, 529), respecting Retry-After
Do not automatically retry page verdicts (403, 413, 502) or the conservative timeout fallback (504). A deliberate 504 retry may still be appropriate, but each attempt re-runs a full capture
Use a 180000 ms synchronous client timeout: after the 150-second capture-work budget, bounded classification produces a typed timeout by 155 seconds at the latest if no result wins first, leaving 25 seconds for response delivery
Validate inputs before making requests to avoid 400 errors
Handle errors gracefully in your application to provide good user experience
message
"
:
"is missing"
}
]
}
The organization was not found
FetchNavigationFailed
502
The browser could not navigate to the page.
FetchCaptureTimeout
504
No result arrived by the deadline, and a queue-only timeout was not proven.
FetchCapacityTimeout
529
Expand proved the request stayed queue-only until the deadline because capacity was unavailable.