Batch Processing
Batch processing allows you to convert multiple URLs in a single API request. Each URL is converted asynchronously, and you can track progress via polling, webhook callbacks, or email notifications. Batch processing is available for both url-to-pdf and url-to-screenshot endpoints.
X-API-Key: sk_live_...). Public keys are restricted to single-URL synchronous requests.
How It Works
- Send a request with an array of URLs in the
urlparameter to either/v1/convert/url-to-pdfor/v1/convert/url-to-screenshot. - The API validates the batch against your plan's limits and returns HTTP 202 with a
batch_id. - Each URL is converted in the background. The entire batch count is pre-checked against your remaining monthly conversion quota before any processing begins.
- Track progress via
GET /v1/convert/batch/{batch_id}, or receive a webhook callback or email notification on completion. - Download the results via presigned URLs in the batch status response.
Output Modes
Individual Mode (Default)
Each URL produces a separate file. Each file gets its own presigned download URL in the batch status response.
Request:
curl -X POST https://api.enconvert.com/v1/convert/url-to-pdf \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": [
"https://example.com/page-1",
"https://example.com/page-2",
"https://example.com/page-3"
]
}'
Response (HTTP 202 Accepted):
{
"status": "processing",
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"url_count": 3,
"output_format": "individual"
}
async_mode is automatically set to true regardless of whether you explicitly include it in the request.
ZIP Bundle Mode
Set output_format to true to receive all converted files bundled into a single ZIP archive. Requires a plan with ZIP output access.
Request:
curl -X POST https://api.enconvert.com/v1/convert/url-to-pdf \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": [
"https://example.com/page-1",
"https://example.com/page-2",
"https://example.com/page-3"
],
"output_format": true,
"output_filename": "monthly-reports"
}'
Response (HTTP 202 Accepted):
{
"status": "processing",
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"url_count": 3,
"output_format": "zip"
}
In ZIP mode, all URLs are processed sequentially, and the successful results are bundled into a single ZIP archive named {output_filename}_{timestamp}.zip (or batch_{timestamp}.zip if no custom name is provided).
Batch Parameters
| Parameter | Type | Default | Description | Plan Gating |
|---|---|---|---|---|
url |
string[] |
(required) | Array of URLs to convert. | -- |
output_format |
boolean |
false |
Set to true to bundle all results into a ZIP archive. Requires multiple URLs. |
Requires ZIP output access |
output_filename |
string |
Auto-generated | Custom filename for the output. In ZIP mode, this names the ZIP archive. | -- |
async_mode |
boolean |
true (implicit) |
Always true for batch. Automatically enabled when multiple URLs are provided. |
Requires async access |
notification_email |
string |
Project owner email | Email address to notify on completion. If omitted, defaults to the project owner's email. | -- |
callback_url |
string |
null |
Webhook URL to receive a POST on completion. | Requires webhook access |
direct_download |
-- | -- | Not supported for batch. Returns 400 error if set with multiple URLs. | -- |
Browser & Rendering Parameters
These settings apply to every URL in the batch:
| Parameter | Type | Default | Description |
|---|---|---|---|
viewport_width |
integer |
1920 |
Browser viewport width in pixels. |
viewport_height |
integer |
1080 |
Browser viewport height in pixels. |
single_page |
boolean |
true |
Render as a single continuous page (url-to-pdf only). |
load_media |
boolean |
true |
Wait for images and media to load. |
enable_scroll |
boolean |
true |
Scroll pages to trigger lazy-loaded content. |
handle_sticky_header |
boolean |
true |
Detect and handle sticky/fixed headers. |
handle_cookies |
boolean |
true |
Auto-dismiss cookie consent banners. |
wait_for_images |
boolean |
true |
Wait for all images to finish loading. |
Authentication & Custom Requests
These apply to every URL in the batch. Require a plan with basic auth access.
| Parameter | Type | Default | Description |
|---|---|---|---|
auth |
object |
null |
HTTP Basic Auth credentials: {"username": "...", "password": "..."}. |
cookies |
array |
null |
Array of cookie objects injected before each page load. Max 50. |
headers |
object |
null |
Custom HTTP headers sent with every request. Max 20. |
PDF Options (url-to-pdf only)
Pass a pdf_options object to control PDF output formatting for every page in the batch:
| Parameter | Type | Default | Description |
|---|---|---|---|
page_size |
string |
"A4" |
Named page size. |
orientation |
string |
"portrait" |
"portrait" or "landscape". |
margins |
object |
{"top": 10, "bottom": 10, "left": 10, "right": 10} |
Margins in mm. |
grayscale |
boolean |
false |
Grayscale output via Ghostscript. |
Batch Status Polling
Use the batch status endpoint to check progress and retrieve download URLs.
GET /v1/convert/batch/{batch_id}
X-API-Key: sk_live_your_private_key
Replace {batch_id} with the batch_id returned from the initial request.
Processing State
While conversions are still running:
{
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"total": 3,
"completed": 1,
"failed": 0,
"in_progress": 2,
"output_mode": "individual",
"zip_download_url": null,
"items": [
{
"source_url": "https://example.com/page-1",
"status": "Success",
"download_url": "https://spaces.example.com/...",
"output_file_size": 184320,
"duration": "3.12"
},
{
"source_url": "https://example.com/page-2",
"status": "In Progress",
"download_url": null,
"output_file_size": null,
"duration": null
},
{
"source_url": "https://example.com/page-3",
"status": "In Progress",
"download_url": null,
"output_file_size": null,
"duration": null
}
]
}
Completed State
When all URLs have been converted successfully:
{
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"total": 3,
"completed": 3,
"failed": 0,
"in_progress": 0,
"output_mode": "individual",
"zip_download_url": null,
"items": [
{
"source_url": "https://example.com/page-1",
"status": "Success",
"download_url": "https://spaces.example.com/...",
"output_file_size": 184320,
"duration": "3.12"
},
{
"source_url": "https://example.com/page-2",
"status": "Success",
"download_url": "https://spaces.example.com/...",
"output_file_size": 210944,
"duration": "4.55"
},
{
"source_url": "https://example.com/page-3",
"status": "Success",
"download_url": "https://spaces.example.com/...",
"output_file_size": 97280,
"duration": "2.87"
}
]
}
Partial State
When all URLs have finished but some failed:
{
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "partial",
"total": 3,
"completed": 2,
"failed": 1,
"in_progress": 0,
"output_mode": "individual",
"zip_download_url": null,
"items": [
{
"source_url": "https://example.com/page-1",
"status": "Success",
"download_url": "https://spaces.example.com/...",
"output_file_size": 184320,
"duration": "3.12"
},
{
"source_url": "https://example.com/page-2",
"status": "Success",
"download_url": "https://spaces.example.com/...",
"output_file_size": 210944,
"duration": "4.55"
},
{
"source_url": "https://invalid-url.example",
"status": "Failed",
"download_url": null,
"output_file_size": null,
"duration": "0.87"
}
]
}
ZIP Mode Completed
In ZIP mode, a single zip_download_url is provided for the entire archive:
{
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"total": 3,
"completed": 3,
"failed": 0,
"in_progress": 0,
"output_mode": "zip",
"zip_download_url": "https://spaces.example.com/...",
"items": [
{
"source_url": "https://example.com/page-1",
"status": "Success",
"download_url": "https://spaces.example.com/...",
"output_file_size": 184320,
"duration": "3.12"
},
{
"source_url": "https://example.com/page-2",
"status": "Success",
"download_url": "https://spaces.example.com/...",
"output_file_size": 210944,
"duration": "4.55"
},
{
"source_url": "https://example.com/page-3",
"status": "Success",
"download_url": "https://spaces.example.com/...",
"output_file_size": 97280,
"duration": "2.87"
}
]
}
Batch Status Values
| Status | Meaning |
|---|---|
processing |
At least one URL is still being converted. |
completed |
All URLs converted successfully. |
partial |
All URLs finished, but some failed. |
failed |
All URLs failed. |
Webhook Callbacks
Provide a callback_url in the request to receive an automatic POST notification on completion. The webhook is sent with Content-Type: application/json and a 30-second timeout. No retries are attempted on delivery failure.
Individual Mode Callback
In individual mode, a separate webhook POST is sent for each URL as it completes:
{
"job_id": "12345",
"status": "success",
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"gcs_uri": "env/files/{project_id}/url-to-pdf/page1_20260405_123456789.pdf",
"filename": "page1_20260405_123456789.pdf",
"file_size": 184320
}
ZIP Mode Callback
In ZIP mode, a single webhook POST is sent when the entire batch completes:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "success",
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"gcs_uri": "env/files/{project_id}/url-to-pdf/batch_20260405_123456789.zip",
"filename": "batch_20260405_123456789.zip",
"file_size": 456789,
"total_tasks": 3,
"successful_tasks": 2,
"failed_tasks": 1,
"tasks": [
{"url": "https://example.com/page-1", "status": "success", "filename": "page1.pdf"},
{"url": "https://example.com/page-2", "status": "success", "filename": "page2.pdf"},
{"url": "https://invalid.example", "status": "failed", "error": "Page load timeout"}
]
}
Email Notifications
A completion email is sent to notification_email when the batch finishes. If no notification_email is provided, the email is sent to the project owner's email by default.
The email includes:
- Job status (success/failed) with a colored banner
- Batch ID
- For batch jobs: a table listing each URL, its status, and output filename
- A link to download results from the dashboard
Subscription Plan Gating
| Feature | Free | Starter | Pro | Enterprise |
|---|---|---|---|---|
| Batch processing | No | Yes | Yes | Yes |
| Async mode | No | Yes | Yes | Yes |
| ZIP output bundling | No | No | Yes | Yes |
| Webhook callbacks | No | No | Yes | Yes |
| HTTP Basic Auth / Cookies / Headers | No | Yes | Yes | Yes |
| Batch size limit | 0 | Plan-based | Plan-based | Unlimited |
| Monthly conversions | 100 | Plan-based | Plan-based | Unlimited |
Code Examples
Python -- Individual Mode with Polling
import requests
import time
# Submit batch
response = requests.post(
"https://api.enconvert.com/v1/convert/url-to-pdf",
headers={"X-API-Key": "sk_live_your_private_key"},
json={
"url": [
"https://example.com/page-1",
"https://example.com/page-2",
"https://example.com/page-3"
]
}
)
data = response.json()
batch_id = data["batch_id"]
print(f"Batch started: {batch_id} ({data['url_count']} URLs)")
# Poll for completion
while True:
status = requests.get(
f"https://api.enconvert.com/v1/convert/batch/{batch_id}",
headers={"X-API-Key": "sk_live_your_private_key"}
).json()
print(f"Status: {status['status']} ({status['completed']}/{status['total']})")
if status["status"] in ("completed", "partial", "failed"):
for item in status["items"]:
if item["download_url"]:
print(f" {item['source_url']} -> {item['download_url']}")
break
time.sleep(5)
Python -- ZIP Mode with Webhook
import requests
response = requests.post(
"https://api.enconvert.com/v1/convert/url-to-pdf",
headers={"X-API-Key": "sk_live_your_private_key"},
json={
"url": [
"https://example.com/page-1",
"https://example.com/page-2",
"https://example.com/page-3"
],
"output_format": True,
"output_filename": "monthly-reports",
"callback_url": "https://your-server.com/webhook/enconvert",
"pdf_options": {
"page_size": "A4",
"orientation": "portrait"
}
}
)
data = response.json()
print(f"Batch started: {data['batch_id']}")
print(f"URLs: {data['url_count']}, Format: {data['output_format']}")
# Results will be delivered to your webhook URL
Node.js -- Batch Screenshots
const response = await fetch("https://api.enconvert.com/v1/convert/url-to-screenshot", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "sk_live_your_private_key"
},
body: JSON.stringify({
url: [
"https://example.com/page-1",
"https://example.com/page-2",
"https://example.com/page-3"
],
output_format: true,
output_filename: "screenshots-bundle"
})
});
const data = await response.json();
console.log(`Batch ${data.batch_id}: ${data.url_count} screenshots queued`);
Error Responses
| Status | Condition |
|---|---|
400 Bad Request |
url is empty or missing |
400 Bad Request |
output_format=true with a single URL (requires multiple URLs) |
400 Bad Request |
direct_download=true with multiple URLs (not supported) |
400 Bad Request |
direct_download=true with async_mode=true |
400 Bad Request |
Public key attempting multiple URLs |
402 Payment Required |
Batch would exceed remaining monthly conversion quota |
402 Payment Required |
Storage limit reached |
403 Forbidden |
Async processing not available on current plan |
403 Forbidden |
Batch processing not available (batch limit is 0) |
403 Forbidden |
Batch size exceeds plan's batch limit |
403 Forbidden |
ZIP output not available on current plan |
403 Forbidden |
Webhook callbacks not available on current plan |
404 Not Found |
Batch not found (wrong batch_id or wrong project) |
Limits
| Limit | Value |
|---|---|
| Batch size | Plan-dependent (Free: disabled) |
| Monthly conversions | Plan-dependent (entire batch pre-checked) |
| Webhook delivery timeout | 30 seconds (no retries) |
| Maximum cookies per request | 50 |
| Maximum custom headers per request | 20 |
| File retention | Plan-dependent |