Skip to main content
Every API error returns the same JSON shape, so you can handle all of them the same way: check the status code, read the stable error.code, and branch on it. The human message is for display and logging only — never parse it, because the wording can change.
{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_field",
    "message": "Field required",
    "param": "caption",
    "doc_url": "https://docs.mymarky.ai/errors#missing_field"
  }
}

The error object

FieldDescription
typeThe broad category of the error (see Error types below). Derived from the HTTP status.
codeA stable, machine-readable identifier. Match on this. Every value is listed in the Error codes reference.
messageA human-readable explanation. For display and logging only — do not parse it.
paramThe request field that caused the error, when the error is field-specific.
doc_urlA link to this error’s entry in the Error codes reference.
detailsOn validation errors with more than one bad field, an array of { param, message } for every field that failed.
request_idThe id of the request that failed (also on the X-Request-Id header). Quote it to support so we can find the exact request in our logs.

Request IDs

Every response — success or error — includes an X-Request-Id header (for example req_4f3c…). On errors it’s also in the body as error.request_id. Log it on your side, and include it when you contact support so we can find the exact request fast.
curl -i https://api.mymarky.ai/api/businesses \
  -H "Authorization: Bearer mk_live_your_key"
# ...
# X-Request-Id: req_4f3c9a1b2c3d4e5f6a7b8c9d

Catch errors

Check for a non-2xx status, read error.code, and decide what to do.
import httpx

resp = httpx.post(
    "https://api.mymarky.ai/api/businesses/{business_id}/posts",
    headers={"Authorization": "Bearer mk_live_your_key_here"},
    json={...},
)

if resp.status_code >= 400:
    error = resp.json()["error"]
    code = error["code"]

    if code == "rate_limit_exceeded":
        # Back off and retry — see "Handle rate limits" below.
        ...
    elif code == "INSUFFICIENT_CREDITS":
        # error["required_credits"] tells you how many more are needed.
        ...
    elif code in ("validation_error", "missing_field", "invalid_value"):
        # Fix the request. error["param"] names the offending field.
        ...
    else:
        # Log code + message + the request id from the response headers.
        print(error["code"], error["message"])
const resp = await fetch("https://api.mymarky.ai/api/businesses/{business_id}/posts", {
  method: "POST",
  headers: { Authorization: "Bearer mk_live_your_key_here" },
  body: JSON.stringify({ ... }),
});

if (!resp.ok) {
  const { error } = await resp.json();

  switch (error.code) {
    case "rate_limit_exceeded":
      // Back off and retry — see "Handle rate limits" below.
      break;
    case "INSUFFICIENT_CREDITS":
      // error.required_credits tells you how many more are needed.
      break;
    case "validation_error":
    case "missing_field":
    case "invalid_value":
      // Fix the request. error.param names the offending field.
      break;
    default:
      console.error(error.code, error.message);
  }
}

Error types

type groups errors by what generally went wrong, so you can pick a broad response without listing every code. For the specific reason, use code.
TypeHTTP statusWhat it meansHow to respond
authentication_error401Missing or invalid credentials.Send a valid API key. Reconnect a social account if the code is SOCIAL_TOKEN_EXPIRED.
permission_error403Valid key, but no access to this resource.Check the resource belongs to your org and your plan includes API access.
not_found_error404The resource doesn’t exist or isn’t yours.Check the ID. Don’t retry without changing it.
invalid_request_error400, 409, 413, 422Bad input or a request that can’t run in the current state.Fix the request using code and param. Don’t retry unchanged.
rate_limit_error429Too many requests.Back off and retry (see below).
api_error500, 502A problem on our end or with an upstream provider.Treat as transient and retry with backoff.

Handle rate limits

When you get rate_limit_exceeded (429), wait and retry. Prefer the Retry-After header when it’s present; otherwise back off exponentially.
import time

if resp.status_code == 429:
    delay = int(resp.headers.get("Retry-After", 1))
    time.sleep(delay)
    # retry the request

Handle transient errors

internal_error (500) and UPSTREAM_PROVIDER_ERROR (502) are usually temporary and not a problem with your request. Retry with exponential backoff. For writes, retry the same request rather than creating a new one, so you don’t duplicate work. For anything in the invalid_request_error family, do not blindly retry — the request needs to change first. Use code and param to fix it.

Next steps

See the full Error codes reference for every code, what it means, and how to resolve it. Each error’s doc_url links straight to its entry there.