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"
}
| Field | Type | Description |
|---|---|---|
error | string | A human-readable message describing what went wrong. |
code | string | A 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
htmlfield in the request body - Sending an empty request body
- Invalid JSON syntax in the request body
- The
datafield 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
Authorizationheader 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.