Understand API error responses, status codes, error codes, and retry strategies.

❌ Error Handling

The Sayify.pro API uses standard HTTP status codes and structured JSON error bodies to tell you exactly what went wrong.


📦 Error Response Format

Most errors return a JSON object with error and code fields:

{
  "error": "name is required",
  "code": "NAME_REQUIRED"
}

Validation errors may include more descriptive messages:

{
  "error": "Slug \"my-form\" is already in use",
  "code": "SLUG_CONFLICT"
}

Some DRF-standard errors return a detail field instead:

{
  "detail": "Authentication credentials were not provided."
}

For field-level validation, the response includes per-field messages:

{
  "name": ["This field is required."],
  "slug": ["A link with this slug already exists in your workspace."]
}

📊 Status Codes

✅ Success Codes

Code Meaning
200 OK Request succeeded.
201 Created Resource successfully created.
200 OK Resource successfully deleted (returns confirmation message).

⚠️ Client Error Codes

Code Meaning Common Cause
400 Bad Request Invalid request body. Missing required fields, invalid data types, slug conflict.
401 Unauthorized Authentication failed. Missing or invalid API key.
403 Forbidden Insufficient permissions. Key missing required scope, plan limit reached.
404 Not Found Resource doesn't exist. Invalid UUID, wrong endpoint, missing trailing slash.
405 Method Not Allowed Wrong HTTP method. Using GET on a POST-only endpoint.
429 Too Many Requests Rate limit exceeded. Slow down and retry with backoff.

🔴 Server Error Codes

Code Meaning
500 Internal Server Error Something went wrong on our end. Contact support if persistent.
503 Service Unavailable Temporary maintenance or overload. Retry after a short wait.

🏷️ Error Codes

Named error codes help you programmatically handle specific failure scenarios:

Error Code HTTP Status Description
NAME_REQUIRED 400 The name field is missing from the request body.
SLUG_CONFLICT 400 The provided slug already exists in this workspace. Omit it to auto-generate.
CREATE_FAILED 400 Unexpected error during resource creation — check request body.
LINK_LIMIT_REACHED 403 Workspace has hit its plan's link quota — upgrade to create more.
INSUFFICIENT_SCOPE 403 API key lacks the required scope (e.g. links:read, links:write).

🔄 Retry Strategy

For 429 and 5xx errors, use exponential backoff:

Python

import time
import requests

def api_request(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code == 200:
            return response.json()

        if response.status_code in (429, 500, 503):
            wait_time = (2 ** attempt)  # 1s, 2s, 4s
            time.sleep(wait_time)
            continue

        # Client error — don't retry
        response.raise_for_status()

    raise Exception("Max retries exceeded")

JavaScript

async function apiRequest(url, headers, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, { headers });

    if (response.ok) return response.json();

    if ([429, 500, 503].includes(response.status)) {
      const waitMs = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
      await new Promise(r => setTimeout(r, waitMs));
      continue;
    }

    throw new Error(`API Error: ${response.status} ${response.statusText}`);
  }

  throw new Error("Max retries exceeded");
}

[!TIP]
When rate-limited (429), the response includes a Retry-After header indicating how many seconds to wait before retrying.


💡 Tips

  • Always check the code field — it's more reliable than parsing error messages.
  • Don't retry 400 or 401 errors — these indicate a problem with your request, not a transient failure.
  • Log delivery IDs — for webhook errors, log X-Sayify-Delivery-Id for debugging.
  • Contact support — if you consistently get 500 errors, create a support ticket.

🎓 What's Next?

Was this page helpful?
Report an issue →