Error Handling
Understanding and handling API errors from SyntaxDoc
Learn how to handle errors from the SyntaxDoc API.
Error Response Format
All errors return a JSON response with the following structure:
{
"statusCode": 400,
"code": "BAD_REQUEST",
"message": "Human-readable error message",
"hint": "Optional hint for fixing the error"
}Or the simpler format:
{
"success": false,
"message": "Human-readable error message"
}HTTP Status Codes
| Status | Description |
|---|---|
200 | Success - PDF returned or JSON response |
400 | Bad Request - Invalid or missing parameters |
401 | Unauthorized - Invalid or missing API key |
403 | Forbidden - Access denied to resource |
404 | Not Found - PDF or resource doesn't exist |
429 | Too Many Requests - Rate/quota limit exceeded |
500 | Internal Server Error - Try again later |
Error Codes Reference
Authentication Errors
UNAUTHORIZED
Status: 401
{
"success": false,
"message": "API key is required. Please provide it in X-API-Key header or Authorization header."
}Causes:
- No API key provided
- Invalid API key format
- API key has been revoked or deleted
Solution: Ensure you're sending the API key correctly:
# Header format
X-API-Key: your_api_key_here
# Or Bearer token format
Authorization: Bearer your_api_key_hereInvalid API Key
{
"success": false,
"message": "Invalid or expired API key."
}Causes:
- API key doesn't exist
- API key has expired
- API key belongs to a deleted user
Solution: Generate a new API key from the dashboard.
Request Errors
BAD_REQUEST
Status: 400
No Files Uploaded:
{
"statusCode": 400,
"code": "BAD_REQUEST",
"message": "No files uploaded. Please upload at least one HTML file with key \"files\".",
"hint": "In Postman, ensure you select an actual file from your file system in the form-data body."
}No HTML File Found:
{
"statusCode": 400,
"code": "BAD_REQUEST",
"message": "No HTML file found in the request. Expected a file named \"index.html\" or any .html file.",
"receivedFiles": [{"name": "document.txt", "mimetype": "text/plain"}],
"hint": "Make sure to select an HTML file from your file system."
}No PDF Files (Merge):
{
"statusCode": 400,
"code": "BAD_REQUEST",
"message": "No PDF files provided for merging"
}Quota & Limit Errors
QUOTA_EXCEEDED
Status: 429
{
"statusCode": 429,
"code": "QUOTA_EXCEEDED",
"message": "You have reached your conversion limit for this billing period"
}Causes:
- Monthly conversion limit reached for your plan
Solution:
- Wait for the next billing period
- Upgrade to a higher plan
- Check usage:
GET /api/billing/usage
HOSTED_PDF_LIMIT_REACHED
Status: 429
{
"statusCode": 429,
"code": "HOSTED_PDF_LIMIT_REACHED",
"message": "Hosted PDF limit reached. Delete existing PDFs or upgrade your plan."
}Causes:
- Reached the maximum number of hosted PDFs for your plan
Limits by Plan:
| Plan | Hosted PDF Limit |
|---|---|
| Hobby | 10 |
| Pro | 500 |
| Enterprise | 10,000 |
Solution:
- Delete unused hosted PDFs:
DELETE /api/hosted-pdfs/:pdfId - Upgrade your plan:
POST /api/billing/subscription/change-plan
Resource Errors
NOT_FOUND
Status: 404
{
"statusCode": 404,
"code": "NOT_FOUND",
"message": "File not found or expired"
}Causes:
- PDF ID doesn't exist
- PDF has expired (exceeded retention period)
- PDF was deleted
- Invalid file ID format
Retention by Plan:
| Plan | Retention Period |
|---|---|
| Hobby | 1 day |
| Pro | 7 days |
| Enterprise | 30 days |
FORBIDDEN
Status: 403
{
"statusCode": 403,
"code": "FORBIDDEN",
"message": "Access denied"
}Causes:
- Trying to access another user's PDF
- PDF sharing is not enabled
- Insufficient permissions
Server Errors
INTERNAL_SERVER_ERROR
Status: 500
{
"statusCode": 500,
"code": "INTERNAL_SERVER_ERROR",
"message": "PDF generation failed"
}Causes:
- Server-side processing error
- HTML rendering failure
- Resource timeout
Solution:
- Check your HTML for JavaScript errors
- Ensure external resources (fonts, images) are accessible
- Retry the request
- If persistent, contact support
Handling Errors in Code
async function generatePdf(htmlFile) {
const formData = new FormData();
formData.append('files', htmlFile);
formData.append('options', JSON.stringify({ fileName: 'document' }));
try {
const response = await fetch('https://api.syntaxdoc.com/pdf/generate', {
method: 'POST',
headers: { 'X-API-Key': 'YOUR_API_KEY' },
body: formData
});
// Check if response is an error
if (!response.ok) {
const error = await response.json();
switch (response.status) {
case 401:
throw new Error('Invalid API key. Check your credentials.');
case 429:
if (error.code === 'HOSTED_PDF_LIMIT_REACHED') {
throw new Error('Hosted PDF limit reached. Delete some PDFs first.');
}
throw new Error('Conversion quota exceeded. Upgrade your plan.');
case 400:
throw new Error(`Bad request: ${error.message}`);
default:
throw new Error(error.message || 'Unknown error occurred');
}
}
// Check content type for success response
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
// Hosted PDF response
return await response.json();
} else {
// Direct PDF binary
return await response.blob();
}
} catch (error) {
console.error('PDF generation failed:', error.message);
throw error;
}
}import requests
import json
class SyntaxDocError(Exception):
def __init__(self, code, message, status_code):
self.code = code
self.message = message
self.status_code = status_code
super().__init__(f"{code}: {message}")
def generate_pdf(html_file_path: str, api_key: str):
url = 'https://api.syntaxdoc.com/pdf/generate'
with open(html_file_path, 'rb') as f:
files = [('files', ('index.html', f, 'text/html'))]
data = {'options': json.dumps({'fileName': 'document'})}
headers = {'X-API-Key': api_key}
response = requests.post(url, headers=headers, files=files, data=data)
if not response.ok:
try:
error = response.json()
code = error.get('code', 'UNKNOWN_ERROR')
message = error.get('message', 'Unknown error')
except:
code = 'UNKNOWN_ERROR'
message = response.text
raise SyntaxDocError(code, message, response.status_code)
# Check if JSON response (hosted PDF) or binary
if 'application/json' in response.headers.get('content-type', ''):
return response.json()
else:
return response.content
# Usage
try:
result = generate_pdf('document.html', 'YOUR_API_KEY')
if isinstance(result, dict):
print(f"Hosted PDF URL: {result['url']}")
else:
with open('output.pdf', 'wb') as f:
f.write(result)
except SyntaxDocError as e:
if e.status_code == 429:
print(f"Quota exceeded: {e.message}")
elif e.status_code == 401:
print("Invalid API key")
else:
print(f"Error: {e.message}")Retry Strategy
For transient errors (500), implement exponential backoff:
async function generatePdfWithRetry(htmlFile, maxRetries = 3) {
const formData = new FormData();
formData.append('files', htmlFile);
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch('https://api.syntaxdoc.com/pdf/generate', {
method: 'POST',
headers: { 'X-API-Key': 'YOUR_API_KEY' },
body: formData
});
if (response.ok) {
return await response.blob();
}
// Only retry on server errors
if (response.status >= 500) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
console.log(`Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
// Non-retryable error
const error = await response.json();
throw new Error(error.message);
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
throw new Error('Max retries exceeded');
}import time
import requests
def generate_pdf_with_retry(html_file_path: str, api_key: str, max_retries: int = 3):
url = 'https://api.syntaxdoc.com/pdf/generate'
for attempt in range(max_retries):
with open(html_file_path, 'rb') as f:
files = [('files', ('index.html', f, 'text/html'))]
headers = {'X-API-Key': api_key}
response = requests.post(url, headers=headers, files=files)
if response.ok:
return response.content
# Only retry on server errors
if response.status_code >= 500:
delay = (2 ** attempt) # 1s, 2s, 4s
print(f"Retrying in {delay}s...")
time.sleep(delay)
continue
# Non-retryable error
error = response.json()
raise Exception(error.get('message', 'Unknown error'))
raise Exception('Max retries exceeded')Debugging Tips
1. Check Request Format
Make sure you're using form-data (not JSON body):
# Correct ✅
curl -X POST https://api.syntaxdoc.com/pdf/generate \
-H "X-API-Key: YOUR_KEY" \
-F "files=@index.html" \
-F 'options={"fileName":"test"}'
# Incorrect ❌
curl -X POST https://api.syntaxdoc.com/pdf/generate \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Test</h1>"}'2. Verify API Key
Test your API key:
curl https://api.syntaxdoc.com/api/keys \
-H "Authorization: Bearer YOUR_AUTH_TOKEN"3. Check Quota
Check your remaining conversions:
curl https://api.syntaxdoc.com/api/billing/subscription \
-H "Authorization: Bearer YOUR_AUTH_TOKEN"4. Validate HTML
Ensure your HTML is valid before sending:
- All tags are properly closed
- External resources are accessible
- No JavaScript runtime errors
Need help? Check your usage dashboard or contact support with your request ID for faster debugging.