Error Reference
Complete reference for all WebUplink API error codes with resolution steps.
Error Reference
All errors return a consistent JSON shape:
{
"error": "QUOTA_EXCEEDED",
"message": "Monthly action quota exhausted (250/250). Upgrade to Builder for 1,000 actions/month with overage, or start a free 14-day Builder trial: https://webuplink.ai/dashboard?upgrade=true",
"request_id": "req-abc-123"
}Use error for programmatic handling and request_id for support tickets — include it when emailing support@webuplink.ai. Retryable errors include a retry_after field (seconds) and a Retry-After header.
Error Codes
| Code | HTTP | Retryable | Resolution |
|---|---|---|---|
UNAUTHORIZED | 401 | No | Check API key format and validity |
VALIDATION_ERROR | 400 | No | Check request body against the schema |
DOMAIN_BLOCKED | 403 | No | The URL is on the restricted list (banking, streaming, ticketing — blocked on every plan), or it's a third-party authentication domain (e.g. accounts.google.com) and login automation isn't enabled on your account. Login automation is a verification-gated capability on all plans — enable it in the dashboard (card on file + AUP attestation): dashboard → billing |
SESSION_NOT_FOUND | 404 | No | Session may have expired — create a new one |
SESSION_BUSY | 409 | Yes | Wait for the current request to complete |
SESSION_EXPIRED | 410 | No | Session timed out (~2min idle or ~15min total) — create a new one |
PLAN_RESTRICTED | 402 | No | Feature requires a higher plan — upgrade |
NO_SUBSCRIPTION | 402 | No | Subscribe to access billing features |
ALREADY_SUBSCRIBED | 409 | No | You already have an active subscription — change plans from the dashboard instead of starting a new checkout |
QUOTA_EXCEEDED | 429 | No | Action quota reached. Free: hard cap until the calendar-month reset (or upgrade). Trial: hard cap for the 14-day trial window — subscribe to continue. The body carries a machine-readable upgrade object (see below) |
SPEND_CAP_EXCEEDED | 402 | No | Your monthly spend cap was reached — raise it in the dashboard to continue |
RATE_LIMITED | 429 | Yes | Too many requests — check retry_after field |
CONCURRENCY_EXCEEDED | 503 | Yes | All concurrent session slots for your plan are in use — close a session or retry after retry_after |
CONCURRENCY_UNAVAILABLE | 503 | Yes | Free-tier session admission is temporarily unavailable (fail-closed) — retry after retry_after |
FREE_TIER_DEGRADED | 503 | Yes | The free tier's daily budget is exhausted — tool execution resumes at UTC midnight (observation still works). The body carries the upgrade object; upgrading lifts the degradation immediately |
AI_PROCESSING_ERROR | 502 | Yes | AI processing failed for this page — retry after retry_after (5s) |
SITE_BLOCKED | 502 | No | The site served a bot-verification challenge or an access-denied page instead of real content. The request is not billed and no retry_after is sent — retrying from the same infrastructure hits the same wall. The message names which it was (challenge vs. access denied). See Production Traffic & Bot Detection |
BROWSER_ERROR | 503 | Yes | Browser infrastructure was temporarily unavailable — retry after retry_after (5s) |
INTERNAL_ERROR | 500 | Sometimes | Unexpected server error — retry with backoff only if retry_after is present |
Not every 502/503 carries these codes. Our load balancer can emit its own 502/503 responses without a JSON body (for example during a deploy or upstream connection failure). The SDKs map those to
INTERNAL_ERROR— check for the JSONerrorfield before branching on a code.
Quota errors carry an upgrade CTA
Hard-stop quota responses on the Free and trial plans (QUOTA_EXCEEDED) and free-tier degradation (FREE_TIER_DEGRADED) include a machine-readable upgrade object, so agents and SDKs can surface the exact next step without parsing prose. Quota errors also include a usage snapshot:
{
"error": "QUOTA_EXCEEDED",
"message": "Monthly action quota exhausted (250/250). Upgrade to Builder for 1,000 actions/month with overage, or start a free 14-day Builder trial: https://webuplink.ai/dashboard?upgrade=true",
"request_id": "req-abc-123",
"usage": {
"actionCount": 250,
"actionLimit": 250,
"periodStart": "2026-07-01T00:00:00.000Z"
},
"upgrade": {
"trial": true,
"plans": ["builder", "pro"],
"url": "https://webuplink.ai/dashboard?upgrade=true"
}
}upgrade.trial is true when the 14-day Builder trial (card required, auto-converts on day 14, cancel anytime) is available from your current plan — i.e. on Free. Trialists get trial: false with a subscribe message instead.
Handling Errors in the SDK
import { WebUplink, WebUplinkError } from 'webuplink';
const client = new WebUplink({
apiKey: process.env.WEBUPLINK_API_KEY!,
baseUrl: 'https://api.webuplink.ai',
});
try {
const page = await client.browse('https://example.com');
} catch (err) {
if (err instanceof WebUplinkError) {
console.log(err.code); // 'QUOTA_EXCEEDED'
console.log(err.statusCode); // 429
console.log(err.requestId); // 'req-abc-123'
console.log(err.retryable); // false
console.log(err.retryAfter); // undefined
if (err.retryable && err.retryAfter) {
// Wait and retry
await new Promise(r => setTimeout(r, err.retryAfter! * 1000));
}
}
}Retry Behavior
The SDK automatically retries retryable errors with the following rules:
- Connection errors (network failures) are always retried with linear backoff
- Server errors with
retry_afterare retried after the specified wait time - 429 responses (
RATE_LIMITED,QUOTA_EXCEEDED) are never automatically retried, even withretry_after— auto-retrying a throttle amplifies load SITE_BLOCKEDcarries noretry_after, so it is never automatically retried- Tool execution requests are never automatically retried (non-idempotent)
- Maximum retry attempts: 3 (configurable via
maxRetriesoption)
// Disable retries entirely
const client = new WebUplink({
apiKey: '...',
baseUrl: '...',
retry: false,
});Rate Limits
Rate limits are per-tenant, per-minute:
| Plan | Requests/minute |
|---|---|
| Free | 30 |
| Trial | 120 |
| Builder | 120 |
| Pro | 300 |
Every /v1 response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. When rate limited, the response includes a retry_after field (and Retry-After header) with the number of seconds to wait.