Error Handling

Understanding API error codes and troubleshooting

When a request to the DoCreate API fails, the response includes an appropriate HTTP status code and a JSON body with details about the error.

Error Response Format

All error responses follow a consistent JSON structure:

{
  "error": "A human-readable description of the error",
  "code": "ERROR_CODE"
}
FieldTypeDescription
errorstringA human-readable message describing what went wrong.
codestringA machine-readable error code for programmatic handling.

HTTP Status Codes

400 Bad Request

The request body is missing, malformed, or contains invalid parameters.

{
  "error": "The 'html' field is required and must be a non-empty string.",
  "code": "INVALID_REQUEST"
}

Common causes:

  • Missing the required html field in the request body
  • Sending an empty request body
  • Invalid JSON syntax in the request body
  • The data field is not a valid JSON object
  • Request body exceeds the 5 MB size limit

401 Unauthorized

The request is missing a valid API key or the provided key is invalid.

{
  "error": "Invalid or missing API key. Provide a valid key in the Authorization header.",
  "code": "UNAUTHORIZED"
}

Common causes:

  • Missing the Authorization header entirely
  • Using an incorrect Bearer token format (must be Bearer YOUR_API_KEY)
  • The API key has been revoked or deleted
  • Typographical error in the API key

403 Forbidden

The API key is valid but does not have permission to perform the requested action.

{
  "error": "Your current plan does not include access to this feature.",
  "code": "FORBIDDEN"
}

Common causes:

  • The associated account subscription has expired
  • The API key does not have the required scope
  • Attempting to access a feature not included in your current plan

429 Too Many Requests

You have exceeded the rate limit for your plan. See Rate Limits for details on limits per plan.

{
  "error": "Rate limit exceeded. Please wait before making another request.",
  "code": "RATE_LIMITED"
}

Common causes:

  • Exceeding your monthly PDF generation quota
  • Sending too many concurrent requests

The response includes headers indicating when you can retry:

Retry-After: 60
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1740000000

500 Internal Server Error

An unexpected error occurred on the server. These errors are logged automatically and investigated by the DoCreate team.

{
  "error": "An internal server error occurred. Please try again later.",
  "code": "INTERNAL_ERROR"
}

Common causes:

  • Temporary service disruption
  • PDF rendering engine failure due to extremely complex HTML
  • External resource loading timeout (e.g., images or fonts referenced in your HTML)

Error Handling Best Practices

Implement Retry Logic

For transient errors (429 and 500), implement exponential backoff:

async function generatePdfWithRetry(payload, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch('https://api.docreate.io/api/pdf/external', {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(payload),
    });

    if (response.ok) {
      return Buffer.from(await response.arrayBuffer());
    }

    const error = await response.json();

    // Do not retry client errors (except rate limiting)
    if (response.status >= 400 && response.status < 500 && response.status !== 429) {
      throw new Error(`API error ${response.status}: ${error.error}`);
    }

    // Wait before retrying (exponential backoff)
    const retryAfter = response.headers.get('Retry-After');
    const delay = retryAfter
      ? parseInt(retryAfter, 10) * 1000
      : Math.pow(2, attempt) * 1000;

    await new Promise((resolve) => setTimeout(resolve, delay));
  }

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

Validate Input Before Sending

Reduce 400 errors by validating your request data before making API calls:

function validatePdfRequest(payload) {
  if (!payload.html || typeof payload.html !== 'string' || payload.html.trim() === '') {
    throw new Error('The "html" field is required and must be a non-empty string.');
  }

  if (payload.css && typeof payload.css !== 'string') {
    throw new Error('The "css" field must be a string.');
  }

  if (payload.data && typeof payload.data !== 'object') {
    throw new Error('The "data" field must be a JSON object.');
  }

  const bodySize = new TextEncoder().encode(JSON.stringify(payload)).length;
  if (bodySize > 5 * 1024 * 1024) {
    throw new Error('Request body exceeds the 5 MB limit.');
  }
}

Log Errors for Debugging

Always log the full error response for debugging purposes:

if (!response.ok) {
  const error = await response.json();
  console.error('DoCreate API error:', {
    status: response.status,
    code: error.code,
    message: error.error,
    requestId: response.headers.get('X-Request-Id'),
  });
}

When contacting support, include the X-Request-Id header value from the error response. This helps the team locate and diagnose the issue quickly.