Errors

Every error answers in the same JSON shape, with an HTTP status that tells you what to do next.

The format

{
  "code": "customer_not_found",
  "message": "No customer with id cus_123",
  "hint": "Check the customer id",
  "statusCode": 404,
  "requestId": "req_0123456789abcdef"
}
  • code: stable and machine-readable; branch on this, not on message.
  • message: a sentence for a human.
  • hint: what to try, when there is something to suggest.
  • statusCode: the HTTP status, repeated.
  • requestId: req_ followed by 16 hex characters. Quote it to QBill support so we find your request.

HTTP codes

Status code When
400 bad_request The body failed validation. When several fields are wrong, the messages are joined by ; . Unknown body fields are dropped.
401 unauthorized No key, or a key that is not valid (see Authentication & keys).
403 publishable_key_forbidden A publishable key used outside the few plan reads it is allowed.
403 api_key_read_only A read_only key tried to write.
403 merchant_suspended The account is suspended.
404 for example customer_not_found The object does not exist.
409 for example sales_link_busy The request conflicts with the current state or with another request. Read code: sales_link_busy may succeed on a retry, coupon_code_taken will not.
429 too_many_requests, rate_limited Rate limit reached, see below.

Other errors carry their own code, such as invoice_not_refundable or key_mode_mismatch.

Rate limits

  • 600 requests per minute per IP address: beyond it the answer is 429 too_many_requests.
  • 300 requests per minute per API key: beyond it the answer is 429 rate_limited. Responses carry X-RateLimit-Limit and X-RateLimit-Remaining; a 429 also carries Retry-After, the time to wait.

Wait for Retry-After before retrying.

In the SDK

The Node SDK (coming to npm) throws a QBillError for any non-2xx answer, with status, code, hint and requestId:

try {
  await qbill.subscriptions.create({ customer, plan });
} catch (e) {
  if (e instanceof QBillError) {
    console.error(e.status, e.code, e.hint, e.requestId);
  }
  throw e;
}

The SDK also retries for you, maxRetries times (2 by default, set it in the constructor options). It honours Retry-After on a 429. Calls that are safe to repeat are also retried after a timeout or a server error. See Webhooks for the events your endpoints receive.