Skip to main content
TinyFish surfaces errors in two layers, and you need to handle both:
  1. API-level errors — returned as non-2xx HTTP responses (auth, validation, rate limits, server errors).
  2. Run-response error codes — returned with HTTP 200 inside the error object of a run response (agent failures, infrastructure issues that map to a finished run, cancellations).
Knowing which layer you’re looking at tells you whether the issue is with the request, your account, or the automation itself.

Error Response Format

API-level errors contain an error object with a code, a message, and optional details. The shape of details depends on the error code:
details is optional. For INVALID_INPUT (400), it can be an array of Zod validation issues containing code, path, message, and issue-specific fields. For VAULT_RECONNECT_REQUIRED (401), it can be an object containing providers, an array of provider ID strings such as 1password and bitwarden.
/v1/automation/run-batch can return a per-run validation envelope; see Batch Errors. Vault reconnect failures before enqueueing use the top-level error shape shown above.

API-level Error Codes

MISSING_API_KEY

HTTP Status: 401 The X-API-Key header was not included in the request.
Solution: Add the X-API-Key header to your request:

INVALID_API_KEY

HTTP Status: 401 The provided API key does not exist or has been revoked.
Solutions:
  1. Verify your API key is correct (no extra whitespace)
  2. Check if the key was deleted in the API Keys dashboard
  3. Generate a new key if needed

UNAUTHORIZED

HTTP Status: 401 Authentication failed for a reason other than missing/invalid key. For example, required user context may be missing. Expired vault credentials use VAULT_RECONNECT_REQUIRED.
Solutions:
  1. Check your account status at agent.tinyfish.ai/api-keys
  2. For Vault-related calls, reconnect your vault under Settings → Vault
  3. Try generating a new API key

VAULT_RECONNECT_REQUIRED

HTTP Status: 401 A vault connection needs replacement credentials. Reconnect the affected provider from the Vault page, then retry the request. Vault-backed runs reject credentials already marked expired before enqueueing; other provider-scoped runs can remain usable. The TypeScript and Python SDKs preserve code = "VAULT_RECONNECT_REQUIRED" on the authentication exception. Check this code to distinguish vault recovery from an invalid TinyFish API key. Responses may identify affected provider IDs in error.details.providers. CLI users can reconnect with tinyfish vault connection add --provider 1password or tinyfish vault connection add --provider bitwarden --client-id <client-id>. Supply the replacement 1Password token through TINYFISH_VAULT_TOKEN, or Bitwarden secrets through TINYFISH_VAULT_CLIENT_SECRET and TINYFISH_VAULT_MASTER_PASSWORD, then retry. CLI guidance requires an updated CLI version.

INVALID_INPUT

HTTP Status: 400 The request body failed validation.
Common Causes:
  • url is missing or not a valid URL (must include https://)
  • goal is empty or missing
  • browser_profile is not "lite" or "stealth"
  • proxy_config.country_code is not a supported 2-letter code (US, GB, CA, DE, FR, JP, AU)
  • output_schema is invalid JSON Schema
  • Missing required query parameter (e.g. query on /v1/search)
Solution: Check the details field for specific validation errors. Each entry includes the failing field path and a human-readable message.

FORBIDDEN

HTTP Status: 403 Authentication succeeded, but the request was rejected — usually because the account lacks an entitlement, capability, or remaining credits. The credit case applies to legacy credit/subscription accounts only; wallet accounts get INSUFFICIENT_CREDITS at 402 instead.
Common Causes:
  • No remaining credits or expired subscription on a legacy credit/subscription account
  • proxy_config.type: "custom" requested without the custom-proxy entitlement
  • output_schema provided without the output-schema entitlement
  • capture_config requests a capability the account isn’t enabled for
  • Attempting to access a resource you don’t own
Solution: Check your account balance and subscription at agent.tinyfish.ai/api-keys, or contact support to enable a specific capability. The message field always specifies which entitlement or condition failed.

INSUFFICIENT_CREDITS (wallet accounts)

HTTP Status: 402 The account’s TinyFish wallet balance is too low to start this run. This is a pre-flight check — the run is never created, and it is not retryable until money is added. Runs are never cut off mid-flight: a run already in progress finishes even if it takes the balance negative, and it’s the next one that gets denied.
Common Causes:
  • Wallet balance is zero or negative
  • Auto-reload is paused because the payment method on file was declined
details carries machine-readable fields (balance, currency, wallet_url, minimum_top_up, auto_reload_needs_payment_fix) so REST callers can act on the denial without parsing message. /v1/automation/run-batch includes the same details object on its wallet-denial error slot. Solution: Add money at agent.tinyfish.ai/wallet (minimum top-up $10), or update the payment method if auto-reload is paused, then retry.

FEATURE_NOT_AVAILABLE

HTTP Status: 404 The account is not enabled for the requested feature. Currently emitted only by GET /v1/wallet, when the account is on legacy credit/subscription billing rather than a wallet — this is the discriminator for telling the two billing modes apart at request time.
Solution: The account isn’t wallet-enabled. Use the legacy credits/subscription flow instead — check your account at agent.tinyfish.ai/api-keys.

NOT_FOUND

HTTP Status: 404 The requested resource does not exist.
Common Causes:
  • Invalid run_id in GET /v1/runs/:id
  • Vault connection not found
  • Browser context profile not found
  • Step HTML/screenshot not found (GET /v1/runs/:id/steps/:stepId/...)
  • Run was deleted or never existed
  • Run ID belongs to a different token scope — CLI/REST run IDs and MCP run IDs live in separate scopes, so a CLI lookup for an MCP-created run returns 404 by design
Solution: Verify the resource ID is correct. Run IDs are returned from /v1/automation/run-async or can be listed via GET /v1/runs.

RETRY_REQUIRED

HTTP Status: 409 A transient conflict prevented the request from completing. Currently emitted by Browser Context Profile setup endpoints (/v1/profiles/:id/save, /v1/profiles/:id/setup-session, /v1/profiles/:id/setup-session/cancel) when the setup session is in an intermediate state.
Solution: Wait briefly and retry. Use a short delay (1–2s) plus exponential backoff if the conflict persists.

RATE_LIMIT_EXCEEDED

HTTP Status: 429 Too many requests in a short period.
The message wording varies by endpoint. Read details instead of parsing it: details is present when your account’s per-minute limit rejected the call, which is the case this section describes. Two other conditions also return RATE_LIMIT_EXCEEDED and deliberately omit details, because no change to your plan would clear them:
  • The upstream search or fetch provider throttled the request. Retry with backoff.
  • Our rate-limit store was unavailable and the request failed closed. Retry with backoff.
  • The automation pending-run cap was hit. Wait for existing runs to finish.
Treat a missing details as “retry, do not upsell”. Rate limits depend on your account’s limits and which API you’re calling:
  • /v1/search and /v1/fetch enforce per-minute request limits, applied per API key.
  • /v1/automation/run, /run-async, and /run-batch enforce a pending-run cap tied to your account’s concurrency limit.
See the Search and Fetch references for the specific numbers. To raise a limit, follow details.upgrade_url from the error body. Response headers:
The automation pending-run cap (429 from /v1/automation/run, /run-async, /run-batch) does not set Retry-After or X-RateLimit-Limit — wait for existing runs to finish and retry. The body message includes your current count and the maximum.
Solutions:
  1. Respect Retry-After when present, otherwise implement exponential backoff
  2. Space out requests (recommended: 1-2 seconds between calls)
  3. Use batch endpoints for high-volume workloads
  4. Request a higher limit by following details.upgrade_url from the error body
Example: Exponential Backoff

DAILY_LIMIT_EXCEEDED

HTTP Status: 429 The account reached its daily allowance for a capability. Research (POST /v1/automation/run-research) counts runs started per Pacific calendar day, including runs still in progress. Runs that fail, time out, or are cancelled do not count.
Response headers: Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix seconds). Solution: Wait for resets_at, or contact support to raise the allowance. Unlike RATE_LIMIT_EXCEEDED, backoff within the same day will not clear this error.

INTERNAL_ERROR

HTTP Status: 500 An unexpected error occurred on the server.
Solutions:
  1. Retry the request after a brief delay
  2. If the error persists, check agent.tinyfish.ai/status for outages
  3. Contact support with your request details and timestamp (include X-Request-ID if you have it)

Run-response Error Codes

Once a run is accepted, completion-time failures are reported inside the run response body — not as HTTP error codes. The HTTP response is 200, and you inspect status + error:
The category field gives you a quick branching key: Optional fields:
  • retry_after — suggested delay in seconds (null if not retryable)
  • help_url — link to troubleshooting docs
  • help_message — short human-readable guidance

SERVICE_BUSY

Category: SYSTEM_FAILURE · In-body HTTP: 200 The platform is temporarily out of capacity (browser pool exhausted, dependent service unavailable). Equivalent to a 503 if returned at the HTTP layer. Solution: Retry with exponential backoff.

TIMEOUT

Category: SYSTEM_FAILURE · In-body HTTP: 200 An infrastructure or request timeout terminated the run. Equivalent to a 504. Solution: Retry. If the goal is large/multi-step, consider splitting it or simplifying.

BILLING_REJECTED

Category: BILLING_FAILURE · In-body HTTP: 200 The run was rejected at the billing check before execution — the wallet balance was too low, or a legacy account was out of credits. The agent never started, so the run lands in a terminal CANCELLED status with no steps recorded. Runs are never cut off mid-flight: one already in progress finishes even if it takes the balance negative, and it’s the next one that gets denied. The same condition can also be caught earlier and reported at the HTTP layer, before the run record is created — INSUFFICIENT_CREDITS at 402 for wallet accounts, FORBIDDEN at 403 for legacy credit/subscription accounts. Handle both. Solution: Add money at agent.tinyfish.ai/wallet — minimum top-up $10. Legacy accounts add credits at agent.tinyfish.ai/api-keys. Then retry.

CONTENT_POLICY_VIOLATION

Category: AGENT_FAILURE · In-body HTTP: 200 The request — typically the goal text or a target URL — was blocked by content policy. Solution: Adjust the goal or target. Contact support if you believe the block was a false positive.

MAX_STEPS_EXCEEDED

Category: AGENT_FAILURE · In-body HTTP: 200 The automation hit the configured maximum step count without producing a result. Solution: Simplify the goal, split it into multiple runs, or raise the step limit if your account’s limits allow it.

SITE_BLOCKED

Category: AGENT_FAILURE · In-body HTTP: 200 The target site blocked the automation (anti-bot, CAPTCHA, IP block). The status field is still FAILED. Solution: Switch browser_profile to "stealth", attach a proxy via proxy_config, or use a Browser Context Profile with a warmed session. See the anti-bot guide.

TASK_FAILED

Category: AGENT_FAILURE · In-body HTTP: 200 The agent ran but couldn’t achieve the goal — navigation failed, content not found, authentication required, the result was incorrect, or the task wasn’t achievable as described. Solution: Make the goal more concrete (which page, which selector-equivalent description, which field). See the prompting guide. For auth flows, use a vault credential or a saved profile.

CANCELLED

Category: N/A · Status: CANCELLED · In-body HTTP: 200 The run was cancelled — either by you (POST /v1/runs/:id/cancel), by SDK cancellation, or because the async task lifecycle was terminated. Not an error in the usual sense. Solution: No action needed unless the cancellation was unintended.

Batch Errors

/v1/automation/run-batch validates the whole request first, then dispatches each child run. Batch-level failures (auth, missing capability, validation) follow the standard { "error": { "code", "message" } } shape, with an optional details object on wallet-denial (INSUFFICIENT_CREDITS, 402) errors — see the example above. Per-run dispatch errors are returned inside the batch response under each run’s slot. Treat the batch envelope as “did the batch get accepted?” and inspect each child for “did the individual run get scheduled?”

Run Status vs Error Codes

HTTP error codes (the table below) indicate request-level failures — your request didn’t make it to the agent. Run-response error codes indicate completion-level outcomes — the run reached the worker and reported back.For more on COMPLETED-but-failed runs, see Understanding Run Status.

HTTP Status Code Summary

Authentication

API key setup and troubleshooting

FAQ

Common questions and issues