Browse docs
Response envelopes#
Single resources return { data, meta }. Collections return { data, page, meta }. meta.correlationId is the request's trace id — include it when contacting support.
{
"data": [ { "object": "client", "id": "…" }, … ],
"page": { "cursor": null, "nextCursor": "eyJ…", "hasMore": true },
"meta": { "correlationId": "c-…" }
}REST API pagination#
Lists use keyset (cursor) pagination — stable under inserts and fast at any depth. Pass limit (1–100) and the nextCursor from the previous page as cursor. Iterate until hasMore is false.
curl "https://api.skedcast.com/v1/posts?limit=50&cursor=eyJ…" \
-H "Authorization: Bearer sked_live_YOUR_KEY"Idempotency#
Every mutating request — POST, PATCH and DELETE — must carry an Idempotency-Key header (any unique string — a UUID is ideal). Replaying the same key returns the original result instead of creating a duplicate, so a network retry is always safe.
Standard headers#
Rate limits#
Programmatic access is limited per client: each API key or OAuth app has its own request budget for the minute, set by your plan (below). A batch request counts one unit per item, so a call that creates 50 posts uses 50 units. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds until the window resets); a 429 adds Retry-After. Read those headers and back off — don't hard-code delays.
The MCP server has one shared budget per connected client, and a tighter share of it for the tools that delete or cancel (delete a client, campaign or webhook, cancel a post, and the batch action that can cancel many at once). Scheduling and publishing are ordinary writes and use the shared budget only. The 429 says which budget ran out, so an agent can back off on that class alone.
A token is only charged for what it could actually do: a call to a delete, cancel or batch tool that the token's scopes or role do not allow costs the ordinary one unit, so a read-only token cannot use up the allowance your other tokens share. In Settings → Developer, each API key has a Usage view showing its requests and refused requests per day and over the last 24 hours, next to the budget it is counted against.
You can also set a WRITE limit on an individual API key, or on a connected app for your whole workspace, in Settings → Developer. Reads keep your plan's budget; only writes — anything that is not a GET on the REST API, and any tool that changes something over MCP — draw on it. A batch counts its items, and a bulk action counts the destinations it touches, so one call that cancels 500 posts uses 500. A write limit can only tighten your plan's budget, never raise it, so it is a way to let a new agent read freely while it publishes carefully. Leave it unset and nothing changes. A 429 from a write limit says which budget ran out; note that a refused request still counts toward your plan's overall budget, so back off rather than retrying immediately.
Separately, every route has an outer per-IP bound against abuse, and first-party credential endpoints (sign-in and password reset) are tighter at 10 requests per minute per IP. What is actually published still respects each platform's own limits and your plan's post caps, however fast you call.
Errors#
Errors are RFC 9457 application/problem+json. Every error carries a stable machine code, an HTTP status, a human title/detail, and the correlationId. Validation failures (422) add a per-field errors array.
{
"type": "https://skedcast.com/errors/validation_failed",
"title": "Validation failed",
"status": 422,
"category": "bad-body",
"code": "validation_failed",
"detail": "One or more fields are invalid.",
"correlationId": "c-…",
"errors": [ { "path": "content", "message": "Required" } ]
}Status codes#
FAQ
- Why keyset pagination instead of page numbers?
- Keyset (cursor) pagination is stable when rows are inserted or deleted mid-iteration and stays fast at any depth, unlike offset pagination. Just follow nextCursor until hasMore is false.