logo
Schedulin Developers

For AI agents: a documentation index is available at /llms.txt. Every guide and example is also available as markdown by adding .md to its URL.

Error Handling

The API uses standard HTTP status codes. A 2xx status means success; 4xx means the request was rejected; 5xx means something went wrong on our side.

Error shape

Errors return a JSON body with a machine-readable code, the HTTP status, and a data object carrying the human-readable detail:

{
  "defined": false,
  "code": "BAD_REQUEST",
  "status": 400,
  "message": "Bad Request",
  "data": { "message": "instagram requires at least 1 media file." }
}

Read the reason from data.message. The top-level message is just the generic status text.

When the failure comes from a known service error (for example a platform rejecting a request), data also carries:

FieldMeaning
data.userMessageA short, end-user-friendly explanation you can show in your own UI
data.errorTagA stable identifier for the error type, safe to branch on
data.isRetryabletrue if the same request may succeed later; false if it never will
{
  "defined": false,
  "code": "INTERNAL_SERVER_ERROR",
  "status": 500,
  "message": "Internal Server Error",
  "data": {
    "message": "…",
    "userMessage": "…",
    "errorTag": "…",
    "isRetryable": true
  }
}

These fields are omitted when they don't apply. For 5xx errors, internal details are never returned — data.message is a generic message (or the userMessage, if there is one).

Validation errors (422)

Requests that fail schema validation return INPUT_VALIDATION_FAILED with field-level detail under data.fieldErrors:

{
  "defined": false,
  "code": "INPUT_VALIDATION_FAILED",
  "status": 422,
  "message": "INPUT_VALIDATION_FAILED",
  "data": {
    "formErrors": [],
    "fieldErrors": { "caption": ["Invalid input: expected string, received undefined"] }
  }
}

The same 422 is used for business rules checked against your request — for example a scheduledAt in the past when creating or scheduling a post, action: "schedule" without a scheduledAt, or a status you're not allowed to set. Field-specific problems are listed under data.fieldErrors; otherwise read data.message.

Payment required (402)

Returned when creating, updating, or publishing a post needs an active plan your workspace doesn't have. Don't retry; upgrade first. The reason is in data.message.

Generating an AI image (POST /v0/studio/generateImage) without enough AI credits returns 403 with "Insufficient AI credits" in data.message — top up before retrying.

Not found (404)

Returned for a resource that doesn't exist in your workspace (or belongs to another workspace), and for an unknown path (for example a typo in the URL). Both use the same JSON envelope, so you can always parse the body. The message names the resource consistently — "Post not found", "Media not found", "Tag not found", "Social account not found", and so on:

{
  "code": "NOT_FOUND",
  "status": 404,
  "message": "Not Found",
  "data": { "message": "Post not found" }
}

Read code and data.message. Resource 404s also include "defined": false, like the other errors on this page.

Conflicts (409)

Returned when the request clashes with existing state — for example creating a tag whose name already exists, deleting media that is still attached to a post, or updating a post that has already been published:

{
  "defined": false,
  "code": "CONFLICT",
  "status": 409,
  "message": "Conflict",
  "data": { "message": "A tag named \"Launch\" already exists." }
}

Rate limits (429)

There are two 429 shapes depending on which limit you hit — see Rate Limits. Both carry a Retry-After header (seconds). Note that the first shape has no top-level code/status; check for error.code as well as code when detecting a rate limit.

The per-method limit (writes 60/min, reads 300/min):

{
  "error": {
    "message": "Rate limit exceeded. Retry after 12s.",
    "code": "RATE_LIMITED"
  }
}

The overall per-credential limit:

{
  "defined": false,
  "code": "TOO_MANY_REQUESTS",
  "status": 429,
  "message": "Too Many Requests",
  "data": { "message": "You are being rate limited. Please try again later." }
}

Common status codes

StatusMeaning
400Bad request — the request is well-formed but can't be applied (e.g. a platform rule)
401Unauthorized — missing, invalid, or revoked API key or OAuth token
402Payment required — the plan doesn't include this (post writes)
403Forbidden — the credential can't access this resource, an OAuth token lacks the required scope, or not enough AI credits
404Not found — the resource doesn't exist in this workspace
409Conflict — the request clashes with existing state
422Validation error — the body or query failed schema validation, or a business rule (e.g. a past scheduledAt)
429Too many requests — you hit a rate limit
5xxServer error — safe to retry with backoff

Retrying

TIP
Retry (after ) and responses with exponential backoff. If is present, follow it. Do not retry other errors — fix the request instead.

The official SDKs already retry 429 and 5xx responses automatically with backoff; configure this with the client's maxRetries / max_retries option.

Timeouts are client-side and never reach the API: set them with timeoutInSeconds / timeout. In Python a timed-out request raises httpx.TimeoutException (not an API error), so catch it separately if you retry on timeouts.