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.
Error response format
Section titled “Error response format”All errors follow the same structure:
{ "error": { "type": "not_found", "message": "Couldn't find WorkspaceMembership with 'id'=123" }}| Field | Type | Description |
|---|---|---|
type | string | Machine-readable error code. Use this for programmatic handling. |
message | string | Human-readable description of what went wrong. |
Error types
Section titled “Error types”| HTTP Status | Type | Description |
|---|---|---|
| 401 | unauthorized | API key is missing, invalid, or expired. |
| 403 | forbidden | API key is valid but missing the required scope for this endpoint. |
| 404 | not_found | The requested resource does not exist, or does not belong to your workspace. |
| 422 | validation_error | The request data failed validation (e.g., missing required fields, invalid values). |
| 422 | invalid_request | The request parameters are malformed or logically invalid. |
| 429 | rate_limited | You’ve exceeded the rate limit. Wait and retry. |
Authentication errors (401)
Section titled “Authentication errors (401)”A 401 response means the API could not identify you.
# Missing headercurl https://api.skipup.ai/api/v1/workspace_members
# Response:# {"error": "Missing authorization token"}# Invalid keycurl https://api.skipup.ai/api/v1/workspace_members \ -H "Authorization: Bearer sk_invalid_key"
# Response:# {"error": "Invalid or expired API key"}Scope errors (403)
Section titled “Scope errors (403)”A 403 response means your API key is valid but doesn’t have permission for the requested action. Each endpoint requires a specific scope.
# Key only has members.read, trying to create a membercurl -X POST https://api.skipup.ai/api/v1/workspace_members \ -H "Authorization: Bearer $SKIPUP_API_KEY" \ -H "Content-Type: application/json" \
# 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.
Validation errors (422)
Section titled “Validation errors (422)”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).
# Missing required fieldcurl -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"}}Rate limiting (429)
Section titled “Rate limiting (429)”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" }}Staying within limits
Section titled “Staying within limits”- Cache responses when possible instead of re-fetching the same data.
- Use pagination efficiently — fetch larger pages with
limit=100to reduce the total number of requests. - Spread requests over time rather than sending them in bursts.
Handling rate limit errors
Section titled “Handling rate limit errors”Use exponential backoff when you receive a 429 response:
import timeimport 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 responsePagination
Section titled “Pagination”All list endpoints use cursor-based pagination.
Query parameters
Section titled “Query parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 25 | Number of results per page (1-100) |
cursor | string | — | ID of the last item from the previous page |
Response metadata
Section titled “Response metadata”Every list response includes a meta object:
{ "data": [...], "meta": { "limit": 25, "has_more": true, "next_cursor": "mem_02H..." }}| Field | Type | Description |
|---|---|---|
limit | integer | The page size used for this request |
has_more | boolean | true if there are more results after this page |
next_cursor | string | Pass this as the cursor parameter to fetch the next page. Only present when has_more is true. |
Paginating through all results
Section titled “Paginating through all results”# First pagecurl "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.
Standard response format
Section titled “Standard response format”All successful API responses use this structure:
Single resource:
{ "data": { "id": "mem_01H...", ... }}List of resources:
{ "data": [...], "meta": { "limit": 25, "has_more": false }}Troubleshooting
Section titled “Troubleshooting”| Problem | Likely cause | Solution |
|---|---|---|
401 “Missing authorization token” | No Authorization header | Add -H "Authorization: Bearer sk_..." to your request |
401 “Invalid or expired API key” | Wrong key or key was deleted | Verify 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 needs | Check which scope the endpoint requires and create a key with that scope |
404 on a resource you know exists | Resource belongs to a different workspace | Verify you’re using the API key for the correct workspace |
422 “email is required” | Missing required field in POST body | Include 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 workspace | Look up the existing member instead of creating a duplicate |
422 “Member has active meeting requests” | Deactivating a member without transferring their meetings | Include transfer_to with another member’s email in the deactivate request |
422 “Member is already active” | Trying to reactivate someone who isn’t deactivated | Check the member’s deactivated_at field before calling reactivate |
429 “Too many requests” | Exceeded 120 requests/minute | Implement exponential backoff and reduce request frequency |
5xx Server error | Temporary server issue | Retry with exponential backoff. If it persists, contact support. |
Retry guidance
Section titled “Retry guidance”For transient errors, use exponential backoff with jitter:
| Status code | Should retry? | Strategy |
|---|---|---|
401, 403 | No | Fix your API key or scopes. Retrying won’t help. |
404 | No | The resource doesn’t exist. Check your request. |
422 | No | Fix the request data. The same request will fail again. |
429 | Yes | Wait with exponential backoff (1s, 2s, 4s, 8s…) |
5xx | Yes | Wait with exponential backoff. Cap at 5 retries. |