Skip to content

Error Handling

Errors in the API come in two layers, and you handle them very differently:

Layer Where to look Meaning Examples
Request ret in the response body is not 1 This HTTP request wasn’t accepted — no task was created and nothing was charged Invalid parameters, insufficient credits, invalid API key
Task The task’s status is failed or canceled, with the reason in error_code The task was created but didn’t produce an answer — nothing is charged Platform crawl failed, API key invalidated before dispatch

When a request fails, the HTTP status code is still 200, and the body looks like this:

{
"ret": 1023,
"msg": "{\"zh\":\"点数余额不足,请充值后重试\",\"en\":\"Insufficient credits. Please top up and try again.\"}"
}
  • Always branch on ret in code. Message wording may change, so don’t build logic on it.
  • msg is a JSON string. Parsing it gives you {"zh": "…", "en": "…"} — the message in Chinese and English — which you can show to your users. To be safe, fall back to the raw msg if parsing fails.
  • The message for invalid parameters (1001) names the offending field, for example “Invalid parameters: query must be at most 4096 characters”.
ret Meaning Can occur on Retry? What to do
1 Success All — —
1001 Invalid parameters: the body isn’t a single JSON object, exceeds 64 KiB, contains undefined fields, or has an invalid value All No Fix the request as the message says. Field names are snake_case, e.g. task_id, wait_seconds
1022 Developer API billing isn’t enabled Submit a Task No Contact sales to enable it
1023 Insufficient credits Submit a Task After topping up Top up, or switch to a cheaper platform / P2
1024 Too many tasks waiting for dispatch or running Submit a Task Yes, later Wait for some tasks to finish, then submit again. See Rate Limits & Quotas
1025 Too many submissions Submit a Task Yes, later Slow down and retry with backoff
1029 Platform unsupported or under maintenance Submit a Task With another platform Fetch the platform list again and pick an available platform
1030 API key missing, invalid, disabled, or expired All No Check your key — see Authentication errors
1031 Task not found Retrieve a Task No Check the task_id; you can only retrieve tasks under your own account
1003 No permission (account permission issue, e.g. ownership of the key’s account was transferred) All No Rare — please contact us
-1 Service temporarily unavailable All Yes, with backoff Retry with exponential backoff. On submit, first confirm whether the task was created — see below

Request errors (except -1) all happen before a task is created: a rejected submission creates no task and charges no credits.

405 (the method isn’t POST) and 404 (wrong path) mean the request never reached the business logic, so no task was created — fix your code and send it again.

Otherwise, if you get a non-200 status such as 502 or 504, or the connection times out or drops, something went wrong on the network path and you can’t tell whether the server processed the request:

  • Read endpoints (List Platforms, Retrieve a Task, List Tasks): simply retry.
  • Submit a Task: follow the steps in “When a submission’s outcome is unknown” below before deciding whether to submit again.

When a task’s status is failed or canceled, error_code explains why. Failed and canceled tasks are never charged; the reserved credits are released.

error_code status Meaning What to do
crawl_failed failed The platform didn’t return a usable answer (timeout, platform error, etc.) Retry later or switch platforms; if timing isn’t critical, use P2, which waits in the queue while the platform is unavailable
credential_invalid failed The API key that submitted the task was disabled, deleted, or expired before dispatch Resubmit with a valid key
crawler_rejected failed The platform isn’t supported or has been retired Fetch the platform list again and switch platforms
crawler_internal_error failed The crawler hit an internal error and didn’t accept the task Retry later
crawler_submit_failed failed The crawler didn’t accept the task Retry later
crawler_invalid_request failed The crawler rejected the request parameters Check the query and country, then resubmit
crawler_method_unavailable failed The crawler hasn’t enabled raw-result crawling (a server-side configuration issue) Please contact us
crawler_auth_failed failed A server-side configuration issue Please contact us
crawl_canceled canceled The task was canceled Resubmit if you still need it

These two values appear on unfinished tasks and don’t indicate failure:

error_code status Meaning
dispatch_uncertain dispatching The dispatch outcome is being confirmed. The server confirms it on its own and never dispatches twice — keep polling
crawler_query_unavailable Unfinished Progress temporarily can’t be synced. Keep polling; contact us if it doesn’t change for a long time

error_message uses the same format as msg on request errors: a JSON string that parses to {"zh": "…", "en": "…"}. Log it, or show the message in your user’s language; if parsing fails, use the raw string. Don’t build logic on the wording — branch on error_code. raw_result.error (and error inside result_json) use the same format.

Situation What to do
-1, network errors, non-200 status (read endpoints) Retry with exponential backoff, e.g. 2, 4, 8 seconds…, a few times at most
1024, 1025 No task was created, so retrying after a wait is safe; also lower your concurrency and submission rate
-1, timeout, dropped connection (Submit a Task) Don’t retry right away — first confirm whether the task was created (see the next section)
1001, 1003, 1022, 1023, 1029, 1030, 1031 Don’t retry automatically; fix the problem first
Task failed / canceled Resubmitting creates a new task, charged only if it succeeds; for crawl_failed, wait a while or switch platforms

If a submit request times out, the connection drops, or it returns -1, the task may already have been created. Retrying blindly creates a second task — and if both succeed, you’re charged twice. Do this instead:

  1. Call List Tasks with the question text as keyword (up to 256 characters, matched as “contains” — for a longer question, pass its first 256 characters) and start_time set to just before the submission.
  2. Found it: keep polling that task_id.
  3. Not found: submit again.
# BASE_URL and HEADERS are defined as in the Quickstart
import json
import time
import requests
RETRYABLE = {-1, 1024, 1025}
class MaxAEOError(Exception):
def __init__(self, ret, msg):
self.ret = ret
try:
self.message = json.loads(msg).get("en") or msg
except (TypeError, ValueError, AttributeError):
self.message = msg
super().__init__(f"ret={ret}: {self.message}")
def post(path, body):
resp = requests.post(BASE_URL + path, headers=HEADERS, json=body, timeout=30)
resp.raise_for_status()
data = resp.json()
if data["ret"] != 1:
raise MaxAEOError(data["ret"], data.get("msg", ""))
return data["data"]["ent"]
def get_with_retry(path, body, retries=4):
"""Read endpoints: safe to retry."""
for attempt in range(retries + 1):
try:
return post(path, body)
except (requests.RequestException, MaxAEOError) as e:
retryable = not isinstance(e, MaxAEOError) or e.ret in RETRYABLE
if not retryable or attempt == retries:
raise
time.sleep(2 ** (attempt + 1))
def submit_safely(platform, query):
"""Submit a task, resubmitting only once we've confirmed none was created."""
started = int(time.time()) - 60 # allow for clock skew
for attempt in range(4):
try:
return post("/task/submit", {"platform": platform, "query": query})
except MaxAEOError as e:
if e.ret in (1024, 1025): # no task was created; wait and try again
time.sleep(15 * (attempt + 1))
continue
if e.ret != -1:
raise
except requests.RequestException:
pass
# Outcome unknown: look for the submission we just made
found = get_with_retry("/task/key/list", {
"keyword": query.strip()[:256], "platform": platform, "start_time": started, "page_size": 5,
})
if found["items"]:
return get_with_retry("/task/get", {"task_id": found["items"][0]["task_id"]})
raise RuntimeError("Submission failed after several attempts; try again later")

When you need our help investigating, please include:

  • the task_id (for task-related issues);
  • the request time (to the minute) and the endpoint you called;
  • the request trace ID from the ipalfish-trace-id response header, if present;
  • the full ret and msg, or error_code and error_message.

Never send your full API key to anyone, including our staff.