AnswerLine Sign in Start free

Authentication and errors

API keys

Send your key as Authorization: Bearer <key>. Keys start with sk_ followed by 40 letters and digits. One key grants every endpoint; keys have no scopes.

Create and revoke keys on the API keys page of the dashboard. A revoked key stops working within a minute. A key spends your account's credits, so keep it on servers and out of code you ship to browsers or apps.

Error format

Errors carry a stable code to branch on, a message for people reading logs, and details where the code has any:

{
  "error": {
    "code": "CONCURRENT_LIMIT_EXCEEDED",
    "message": "Concurrent limit exceeded",
    "details": { "limit": 1 },
    "timestamp": "2026-09-15T12:00:00.000Z"
  }
}

Validation failures use a different shape, with one entry per invalid field:

{
  "success": false,
  "error": "Request validation failed",
  "details": [{ "field": "prompt", "message": "Prompt cannot be empty" }]
}

Items of a batch fail inside a 200 response instead, with codes VALIDATION_ERROR, RESOURCE_ALREADY_EXISTS or INSUFFICIENT_CREDITS; see batches.

Error codes

StatusCodeMeaning
400 / 422 The body failed validation: 400 on /v1/monitor/*, 422 on /v1/async/*. details lists each field.
400 BAD_REQUEST The request could not be read.
401 MISSING_API_KEY No bearer token in the Authorization header.
401 INVALID_API_KEY_FORMAT The token is not shaped like an API key.
401 INVALID_OR_EXPIRED_API_KEY The key is unknown or revoked.
403 INSUFFICIENT_CREDITS Your balance does not cover the request's maximum cost.
404 RESOURCE_NOT_FOUND Unknown route or task id, or a monitor endpoint of an engine that is not available yet (as a task, that engine fails validation).
405 METHOD_NOT_ALLOWED The path exists, but not with this method.
409 RESOURCE_CONFLICT A task with this idempotencyKey already exists.
413 PAYLOAD_TOO_LARGE The body is larger than the API accepts.
415 UNSUPPORTED_MEDIA_TYPE The body was not sent as application/json.
429 RATE_LIMIT_EXCEEDED Too many requests in the current one-second window.
429 CONCURRENT_LIMIT_EXCEEDED A synchronous call found every concurrency slot busy; details.limit is your limit.
429 QUEUE_LIMIT_EXCEEDED The tasks would overflow your queue; the whole submission is rejected.
500 INTERNAL_SERVER_ERROR, or the error "Maximum retries exceeded" The request failed after automatic retries or ran out of time. No credits are charged.
502 EXTERNAL_SERVICE_ERROR The engine failed in a way retrying would not fix. No credits are charged.

Retrying safely

  • 429 is always safe to retry: the request was not admitted, so nothing ran or was charged. It carries no Retry-After; back off with jitter. A rate-limit 429 clears within a second, a concurrency 429 when a running job finishes.
  • 5xx, timeouts and connection errors are safe to retry for GET and DELETE, and for task creation when every task carries an idempotencyKey. A repeat of a task that was created answers 409 RESOURCE_CONFLICT (in a batch, RESOURCE_ALREADY_EXISTS): the task exists.
  • Synchronous monitor calls are charged on success and may still be running when a connection drops. Retry them only when the request never reached the server (connection refused, host not resolved). A 500 or 502 from them is final: the server has already retried.
  • Other 4xx will fail the same way again; fix the request.

The SDKs apply exactly this policy, with 2 retries by default (maxRetries / max_retries), capped exponential backoff with full jitter, and per-attempt timeouts of 30 seconds, or 330 seconds for synchronous calls (timeoutMs and syncTimeoutMs / timeout and sync_timeout). Errors they give up on are raised as ApiError:

TypeScript

import { ApiError, Client } from "@answerline/sdk";

try {
  await client.chatgpt({ prompt: "Best CRM for small agencies", country: "US" });
} catch (err) {
  if (!(err instanceof ApiError)) throw err;
  console.error(err.status, err.code, err.meta.requestId, err.body);
}

Python

from answerline import ApiError

try:
    client.chatgpt("Best CRM for small agencies", "US")
except ApiError as err:
    print(err.status, err.code, err.meta.request_id, err.body)

Request ids

Every response carries an X-Request-Id header. Send your own (1 to 128 letters, digits, -, _, . or :) to tie a request to your logs; otherwise one is generated. Quote it when you contact support. The SDKs expose it as meta.requestId / meta.request_id and include it in ApiError messages.

curl

curl -i "https://api.answerline.dev/v1/credits" \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Request-Id: nightly-run-42"

TypeScript

const { data, meta } = await client.request("GET", "/v1/credits");
console.log(meta.requestId);

Python

data, meta = client.request("GET", "/v1/credits")
print(meta.request_id)