Skip to content

Errors and Rate Limits

Understand API error responses, status codes, and rate limiting behavior.

The SkipUp API uses standard HTTP status codes and returns structured error responses so you can handle problems programmatically.

All errors follow the same structure:

{
"error": {
"type": "not_found",
"message": "Couldn't find WorkspaceMembership with 'id'=123"
}
}
FieldTypeDescription
typestringMachine-readable error code. Use this for programmatic handling.
messagestringHuman-readable description of what went wrong.
HTTP StatusTypeDescription
401unauthorizedAPI key is missing, invalid, or expired.
403forbiddenAPI key is valid but missing the required scope for this endpoint.
404not_foundThe requested resource does not exist, or does not belong to your workspace.
422validation_errorThe request data failed validation (e.g., missing required fields, invalid values).
422invalid_requestThe request parameters are malformed or logically invalid.
429rate_limitedYou’ve exceeded the rate limit. Wait and retry.

A 401 response means the API could not identify you.

Terminal window
# Missing header
curl https://api.skipup.ai/api/v1/workspace_members
# Response:
# {"error": "Missing authorization token"}
Terminal window
# Invalid key
curl https://api.skipup.ai/api/v1/workspace_members \
-H "Authorization: Bearer sk_invalid_key"
# Response:
# {"error": "Invalid or expired API key"}

A 403 response means your API key is valid but doesn’t have permission for the requested action. Each endpoint requires a specific scope.

Terminal window
# Key only has members.read, trying to create a member
curl -X POST https://api.skipup.ai/api/v1/workspace_members \
-H "Authorization: Bearer $SKIPUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
# Response:
# {"error": {"type": "forbidden", "message": "API key missing required scope: members.write"}}

To fix this, create a new API key with the required scopes, or update the scopes on your existing key in Settings > API Keys.

A 422 response means the request was understood but the data is invalid. There are two subtypes:

validation_error — A database-level validation failed (e.g., uniqueness constraint, required association).

invalid_request — The request parameters are logically wrong (e.g., missing a required field, trying to deactivate an already-inactive member).

Terminal window
# Missing required field
curl -X POST https://api.skipup.ai/api/v1/workspace_members \
-H "Authorization: Bearer $SKIPUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
# Response:
# {"error": {"type": "invalid_request", "message": "email is required"}}

The API allows 120 requests per minute per API key. When you exceed this limit, you’ll receive a 429 response.

{
"error": {
"type": "rate_limited",
"message": "Too many requests"
}
}
  • Cache responses when possible instead of re-fetching the same data.
  • Use pagination efficiently — fetch larger pages with limit=100 to reduce the total number of requests.
  • Spread requests over time rather than sending them in bursts.

Use exponential backoff when you receive a 429 response:

import time
import requests
def api_request(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code != 429:
return response
wait = 2 ** attempt # 1s, 2s, 4s, 8s, 16s
time.sleep(wait)
return response

All list endpoints use cursor-based pagination.

ParameterTypeDefaultDescription
limitinteger25Number of results per page (1-100)
cursorstringID of the last item from the previous page

Every list response includes a meta object:

{
"data": [...],
"meta": {
"limit": 25,
"has_more": true,
"next_cursor": "mem_02H..."
}
}
FieldTypeDescription
limitintegerThe page size used for this request
has_morebooleantrue if there are more results after this page
next_cursorstringPass this as the cursor parameter to fetch the next page. Only present when has_more is true.
Terminal window
# First page
curl "https://api.skipup.ai/api/v1/workspace_members?limit=50" \
-H "Authorization: Bearer $SKIPUP_API_KEY"
# Next page (using next_cursor from the previous response)
curl "https://api.skipup.ai/api/v1/workspace_members?limit=50&cursor=mem_02H..." \
-H "Authorization: Bearer $SKIPUP_API_KEY"

Continue fetching pages until has_more is false.

All successful API responses use this structure:

Single resource:

{
"data": {
"id": "mem_01H...",
"email": "[email protected]",
...
}
}

List of resources:

{
"data": [...],
"meta": {
"limit": 25,
"has_more": false
}
}
ProblemLikely causeSolution
401 “Missing authorization token”No Authorization headerAdd -H "Authorization: Bearer sk_..." to your request
401 “Invalid or expired API key”Wrong key or key was deletedVerify your key in Settings > API Keys, or create a new one
403 “API key missing required scope”Key doesn’t have the scope this endpoint needsCheck which scope the endpoint requires and create a key with that scope
404 on a resource you know existsResource belongs to a different workspaceVerify you’re using the API key for the correct workspace
422 “email is required”Missing required field in POST bodyInclude all required fields. Check that Content-Type: application/json is set.
422 “User is already a member”Trying to add someone who’s already in the workspaceLook up the existing member instead of creating a duplicate
422 “Member has active meeting requests”Deactivating a member without transferring their meetingsInclude transfer_to with another member’s email in the deactivate request
422 “Member is already active”Trying to reactivate someone who isn’t deactivatedCheck the member’s deactivated_at field before calling reactivate
429 “Too many requests”Exceeded 120 requests/minuteImplement exponential backoff and reduce request frequency
5xx Server errorTemporary server issueRetry with exponential backoff. If it persists, contact support.

For transient errors, use exponential backoff with jitter:

Status codeShould retry?Strategy
401, 403NoFix your API key or scopes. Retrying won’t help.
404NoThe resource doesn’t exist. Check your request.
422NoFix the request data. The same request will fail again.
429YesWait with exponential backoff (1s, 2s, 4s, 8s…)
5xxYesWait with exponential backoff. Cap at 5 retries.