Batchwork API
Every tool on the site, and pipelines of up to 10 steps, over plain HTTP. Upload files, poll the job, download the result.
Base URL https://batchwork.nl/api/v1
Format multipart/form-data in, JSON status out, files as downloads
Keys Create one on your account page
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps":[{"tool":"image","action":"resize","params":{"width":1600}}]}' \
-F file=@photo1.jpg -F file=@photo2.jpg
# {"job_id": "…", "status": "queued", "status_url": "/api/v1/jobs/…"}
curl -H "Authorization: Bearer $BATCHWORK_KEY" https://batchwork.nl/api/v1/jobs/JOB_ID
curl -H "Authorization: Bearer $BATCHWORK_KEY" -o result.zip https://batchwork.nl/api/v1/jobs/JOB_ID/download
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {'steps': [{'tool': 'image', 'action': 'resize', 'params': {'width': 1600}}]}
files = [('file', open(n, 'rb')) for n in ('photo1.jpg', 'photo2.jpg')]
job = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=files).json()
while (s := requests.get(f"{API}/jobs/{job['job_id']}", headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
open('result.zip', 'wb').write(requests.get(f"{API}/jobs/{job['job_id']}/download", headers=H).content)
Authentication
Send your key in the Authorization header: Authorization: Bearer bw_…. The older X-API-Key header still works. Keys belong to your account; revoke one on the account page and it stops working at once.
Limits and credits
Only calls that start work count: POST /jobs and the legacy POST /process. Status, downloads, /me and /tools are free.
Every metered response carries X-RateLimit-Limit and X-RateLimit-Remaining. Jobs also follow the plan's file limits (size, files per job, daily server quota); a job over them answers 402 with need_credits.
Endpoints
GET /api/v1/toolsEvery tool, action, parameter and accepted file type.
GET /api/v1/meYour plan, credit balance and calls today.
POST /api/v1/jobsStart a job. Multipart fields: pipeline (JSON) and one or more file. Answers 202 with job_id.
GET /api/v1/jobs/<id>Status (queued, running, done, error), progress and per-file results.
GET /api/v1/jobs/<id>/downloadThe ZIP, or the single output file.
GET /api/v1/jobs/<id>/file/<name>One output file. Results are kept for 2 hours.
Pipelines
A pipeline is {name, steps: [{tool, action, params}], rename, output}. Steps run in order on every file; the output of one step is the input of the next. rename is an optional pattern with {name}, {n}, {n:03}, {ext}, {date}, {w} and {h}. output is zip or files. Up to 10 steps.
{"name": "Web images",
"steps": [
{"tool": "image", "action": "resize", "params": {"width": 1600}},
{"tool": "image", "action": "convert", "params": {"format": "webp", "quality": 82}},
{"tool": "image", "action": "strip_metadata", "params": {}}
],
"rename": "product-{n:03}.{ext}",
"output": "zip"}
Remove Background
Cut the subject out of a photo and get a transparent PNG
Remove background ai/remove_bg
heavy
Works best on a clear subject: a product, a person, a car. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "ai", "action": "remove_bg", "params": {"background": "transparent"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "ai", "action": "remove_bg", "params": {"background": "transparent"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Audio Tools
Convert, trim, normalize and merge audio files
Convert audio audio/convert
MP3 for compatibility, FLAC or WAV when you need every detail. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "audio", "action": "convert", "params": {"format": "mp3"}}]}' \
-F file=@input.mp3
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "audio", "action": "convert", "params": {"format": "mp3"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp3', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Trim audio audio/trim
Handy for ringtones, intros and removing silence at the end. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "audio", "action": "trim", "params": {"start": "0", "duration": "30"}}]}' \
-F file=@input.mp3
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "audio", "action": "trim", "params": {"start": "0", "duration": "30"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp3', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Normalize volume audio/normalize
Loudness is measured over the whole file, so it sounds natural. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "audio", "action": "normalize", "params": {"target": "-16"}}]}' \
-F file=@input.mp3
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "audio", "action": "normalize", "params": {"target": "-16"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp3', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Merge audio audio/merge
many files in, one out
Files can differ in format and sample rate; they are re-encoded together. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "audio", "action": "merge", "params": {"format": "mp3"}}]}' \
-F file=@input1.mp3 \
-F file=@input2.mp3
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "audio", "action": "merge", "params": {"format": "mp3"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input1.mp3', 'rb')), ('file', open('input2.mp3', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Bulk / Data
CSV and JSON transforms, ZIP pack and unpack
CSV to JSON bulk/csv_to_json
Comma, semicolon, tab and pipe delimiters are detected for you. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "bulk", "action": "csv_to_json", "params": {}}]}' \
-F file=@input.csv
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "bulk", "action": "csv_to_json", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.csv', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
JSON to CSV bulk/json_to_csv
Every key found in any object becomes a column. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "bulk", "action": "json_to_csv", "params": {}}]}' \
-F file=@input.csv
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "bulk", "action": "json_to_csv", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.csv', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Filter CSV rows bulk/csv_filter
Matching is case-insensitive. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "bulk", "action": "csv_filter", "params": {"column": "", "operator": "contains", "value": ""}}]}' \
-F file=@input.csv
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "bulk", "action": "csv_filter", "params": {"column": "", "operator": "contains", "value": ""}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.csv', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Select CSV columns bulk/csv_columns
Type the column names separated by commas. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "bulk", "action": "csv_columns", "params": {"columns": ""}}]}' \
-F file=@input.csv
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "bulk", "action": "csv_columns", "params": {"columns": ""}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.csv', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Create ZIP bulk/zip_pack
many files in, one out
Files keep their names; duplicates get a number. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "bulk", "action": "zip_pack", "params": {}}]}' \
-F file=@input1.csv \
-F file=@input2.csv
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "bulk", "action": "zip_pack", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input1.csv', 'rb')), ('file', open('input2.csv', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Extract ZIP bulk/zip_unpack
many files out
You get the files back as a clean ZIP with safe names. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "bulk", "action": "zip_unpack", "params": {}}]}' \
-F file=@input.csv
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "bulk", "action": "zip_unpack", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.csv', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Document Tools
Convert between Word, ODT, Markdown, HTML, EPUB, RTF, text and PDF
Convert document document/convert
Headings, lists, tables and images are kept where the target format supports them. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "document", "action": "convert", "params": {"format": "pdf"}}]}' \
-F file=@input.docx
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "document", "action": "convert", "params": {"format": "pdf"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.docx', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Image Tools
Resize, convert, compress, crop and watermark images, plus HEIC, SVG, favicon and PDF conversions
Resize image/resize
Set a maximum width and height and every image is scaled to fit, aspect ratio kept. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "resize", "params": {"width": "1600", "height": "0", "enlarge": "false"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "resize", "params": {"width": "1600", "height": "0", "enlarge": "false"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Convert format image/convert
Pick the format you need; transparency is kept where the format supports it. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "convert", "params": {"format": "webp", "quality": "85"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "convert", "params": {"format": "webp", "quality": "85"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Compress image/compress
Lower quality means smaller files; 70 to 80 is invisible on most photos. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "compress", "params": {"quality": "75"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "compress", "params": {"quality": "75"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Add watermark image/watermark
Type your text once and it lands in the same spot on every image. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "watermark", "params": {"text": "Batchwork", "position": "SouthEast", "size": "36", "color": "#ffffff", "opacity": "40"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "watermark", "params": {"text": "Batchwork", "position": "SouthEast", "size": "36", "color": "#ffffff", "opacity": "40"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Crop image/crop
Give a width and height; centred crops work well for square social posts. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "crop", "params": {"crop_w": "1080", "crop_h": "1080", "crop_x": "0", "crop_y": "0", "center": "true"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "crop", "params": {"crop_w": "1080", "crop_h": "1080", "crop_x": "0", "crop_y": "0", "center": "true"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Rotate image/rotate
Positive numbers rotate clockwise. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "rotate", "params": {"degrees": "90"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "rotate", "params": {"degrees": "90"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Effects image/effects
One effect per step; chain steps in a pipeline for more. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "effects", "params": {"effect": "grayscale", "amount": "5"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "effects", "params": {"effect": "grayscale", "amount": "5"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Strip metadata image/strip_metadata
The pixels stay the same; only hidden data is removed. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "strip_metadata", "params": {}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "strip_metadata", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
HEIC to JPG image/heic_to_jpg
Drop HEIC or HEIF files and get standard JPGs back. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "heic_to_jpg", "params": {"quality": "90"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "heic_to_jpg", "params": {"quality": "90"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
SVG to PNG image/svg_to_png
SVGs are rendered, not scaled, so the PNG is crisp at any size. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "svg_to_png", "params": {"width": "1024", "background": "transparent"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "svg_to_png", "params": {"width": "1024", "background": "transparent"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Favicon pack image/favicon
many files out
Start from a square image of at least 512 px for the sharpest icons. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "favicon", "params": {"background": "transparent"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "favicon", "params": {"background": "transparent"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Image to PDF image/to_pdf
Each image becomes its own PDF; use PDF tools to combine many images into one file. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "image", "action": "to_pdf", "params": {}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "image", "action": "to_pdf", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
OCR / Text Extract
Extract text from images and scanned PDFs
Office Tools
Word, Excel and PowerPoint to PDF, Excel to CSV and CSV to Excel
Word to PDF office/word_to_pdf
Your document is rendered by LibreOffice on our server and deleted within two hours. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "office", "action": "word_to_pdf", "params": {}}]}' \
-F file=@input.docx
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "office", "action": "word_to_pdf", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.docx', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Excel to PDF office/excel_to_pdf
Set the print area in Excel first if you only want part of a sheet. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "office", "action": "excel_to_pdf", "params": {}}]}' \
-F file=@input.docx
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "office", "action": "excel_to_pdf", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.docx', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
PowerPoint to PDF office/powerpoint_to_pdf
Handy for sharing slides with people who do not have PowerPoint. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "office", "action": "powerpoint_to_pdf", "params": {}}]}' \
-F file=@input.docx
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "office", "action": "powerpoint_to_pdf", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.docx', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Excel to CSV office/excel_to_csv
Formulas are exported as their last calculated values. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "office", "action": "excel_to_csv", "params": {"sheet": "1", "delimiter": "comma"}}]}' \
-F file=@input.docx
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "office", "action": "excel_to_csv", "params": {"sheet": "1", "delimiter": "comma"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.docx', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
CSV to Excel office/csv_to_excel
Leading zeros (postcodes, phone numbers) stay as text. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "office", "action": "csv_to_excel", "params": {"detect_numbers": "true", "bold_header": "true"}}]}' \
-F file=@input.docx
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "office", "action": "csv_to_excel", "params": {"detect_numbers": "true", "bold_header": "true"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.docx', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
PDF Tools
Merge, split, compress, convert, protect and number PDF files
Merge PDFs pdf/merge
many files in, one out
Drop your PDFs, drag them into order and download one file. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "merge", "params": {}}]}' \
-F file=@input1.jpg \
-F file=@input2.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "merge", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input1.jpg', 'rb')), ('file', open('input2.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Split PDF pdf/split
many files out
Leave ranges blank to get one PDF per page. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "split", "params": {"ranges": ""}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "split", "params": {"ranges": ""}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Compress PDF pdf/compress
"screen" is smallest, "printer" keeps the most detail. If compression would make the file bigger you get the original back. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "compress", "params": {"level": "ebook"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "compress", "params": {"level": "ebook"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Watermark PDF pdf/watermark
The watermark is centred on each page, whatever its size. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "watermark", "params": {"text": "CONFIDENTIAL", "size": "60", "opacity": "25", "color": "#808080"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "watermark", "params": {"text": "CONFIDENTIAL", "size": "60", "opacity": "25", "color": "#808080"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Remove password pdf/unlock
You need the current password; this does not crack PDFs. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "unlock", "params": {"password": ""}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "unlock", "params": {"password": ""}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
PDF to JPG pdf/to_jpg
many files out
150 DPI suits screens; 300 DPI suits print. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "to_jpg", "params": {"dpi": "150", "quality": "85"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "to_jpg", "params": {"dpi": "150", "quality": "85"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
PDF to PNG pdf/to_png
many files out
PNG keeps text and line art perfectly sharp. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "to_png", "params": {"dpi": "150"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "to_png", "params": {"dpi": "150"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
PDF to Word pdf/to_word
Works on PDFs with real text. Scanned PDFs need OCR first. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "to_word", "params": {}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "to_word", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
PDF to text pdf/to_text
For scanned PDFs use OCR instead; they have no text layer. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "to_text", "params": {"layout": "true"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "to_text", "params": {"layout": "true"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Images to PDF pdf/from_images
many files in, one out
Images go in upload order. "auto" makes each page exactly the size of its image. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "from_images", "params": {"page_size": "auto", "margin": "0"}}]}' \
-F file=@input1.jpg \
-F file=@input2.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "from_images", "params": {"page_size": "auto", "margin": "0"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input1.jpg', 'rb')), ('file', open('input2.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Delete pages pdf/delete_pages
Use ranges like 5-7 and open ranges like 10- for "10 to the end". Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "delete_pages", "params": {"pages": "1"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "delete_pages", "params": {"pages": "1"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Reorder pages pdf/reorder_pages
List the page numbers in the order you want them. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "reorder_pages", "params": {"order": "", "reverse": "false", "drop_rest": "false"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "reorder_pages", "params": {"order": "", "reverse": "false", "drop_rest": "false"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Rotate pages pdf/rotate_pages
Rotation is clockwise and stored in the file, so it sticks everywhere. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "rotate_pages", "params": {"degrees": "90", "pages": ""}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "rotate_pages", "params": {"degrees": "90", "pages": ""}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Protect with password pdf/protect
Pick a password you can share separately; nobody can recover it for you. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "protect", "params": {"password": "", "allow_print": "true", "allow_copy": "false"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "protect", "params": {"password": "", "allow_print": "true", "allow_copy": "false"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Add page numbers pdf/page_numbers
Choose where the number sits and how it reads. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "pdf", "action": "page_numbers", "params": {"position": "bottom-center", "template": "{n}", "start": "1", "size": "10", "skip_first": "false"}}]}' \
-F file=@input.jpg
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "pdf", "action": "page_numbers", "params": {"position": "bottom-center", "template": "{n}", "start": "1", "size": "10", "skip_first": "false"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.jpg', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Utilities
QR codes and batch renaming
QR code generator utility/qr_code
Static codes: the link is inside the image, so it works forever. Upload a .txt file to make one code per file in a batch. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "utility", "action": "qr_code", "params": {"text": "https://batchwork.nl", "format": "png", "size": "512", "error_correction": "M", "color": "#000000", "background": "#ffffff"}}]}'
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "utility", "action": "qr_code", "params": {"text": "https://batchwork.nl", "format": "png", "size": "512", "error_correction": "M", "color": "#000000", "background": "#ffffff"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)})
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Rename files utility/rename
Use {name}, {n}, {n:03}, {ext}, {date} and {w}x{h} (images) in the pattern. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "utility", "action": "rename", "params": {"pattern": "{name}_{n:03}.{ext}"}}]}' \
-F file=@input.txt
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "utility", "action": "rename", "params": {"pattern": "{name}_{n:03}.{ext}"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.txt', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Video Tools
Compress, convert, trim and resize videos, make GIFs and pull out the audio
Compress video video/compress
heavy
28 is a good default; go up for smaller files, down for better quality. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "video", "action": "compress", "params": {"crf": "28", "max_height": "keep"}}]}' \
-F file=@input.mp4
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "video", "action": "compress", "params": {"crf": "28", "max_height": "keep"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp4', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Convert video video/convert
heavy
MP4 plays almost everywhere; WebM is best for websites. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "video", "action": "convert", "params": {"format": "mp4"}}]}' \
-F file=@input.mp4
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "video", "action": "convert", "params": {"format": "mp4"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp4', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Resize video video/resize
heavy
The picture is scaled to fit and padded with black bars if the shape differs. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "video", "action": "resize", "params": {"width": "1280", "height": "720"}}]}' \
-F file=@input.mp4
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "video", "action": "resize", "params": {"width": "1280", "height": "720"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp4', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Trim video video/trim
heavy
Cuts snap to the nearest keyframe, so the start may shift by a fraction of a second. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "video", "action": "trim", "params": {"start": "0", "duration": "10"}}]}' \
-F file=@input.mp4
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "video", "action": "trim", "params": {"start": "0", "duration": "10"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp4', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
Video to GIF video/to_gif
heavy
Keep GIFs short and narrow; they grow fast. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "video", "action": "to_gif", "params": {"fps": "10", "width": "480", "start": "0", "duration": "5"}}]}' \
-F file=@input.mp4
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "video", "action": "to_gif", "params": {"fps": "10", "width": "480", "start": "0", "duration": "5"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp4', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
MP4 to MP3 video/mp4_to_mp3
heavy
Only the sound is kept; the video track is dropped. Try it on the site .
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "video", "action": "mp4_to_mp3", "params": {"bitrate": "192k"}}]}' \
-F file=@input.mp4
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "video", "action": "mp4_to_mp3", "params": {"bitrate": "192k"}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp4', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)
MOV to MP4 video/mov_to_mp4
heavy
Output is H.264 video with AAC audio, the most compatible MP4 there is. Try it on the site .
No parameters.
curl
Python
Copy
curl -X POST https://batchwork.nl/api/v1/jobs \
-H "Authorization: Bearer $BATCHWORK_KEY" \
-F 'pipeline={"steps": [{"tool": "video", "action": "mov_to_mp4", "params": {}}]}' \
-F file=@input.mp4
import json, os, time, requests
API = 'https://batchwork.nl/api/v1'
H = {'Authorization': f"Bearer {os.environ['BATCHWORK_KEY']}"}
pipeline = {"steps": [{"tool": "video", "action": "mov_to_mp4", "params": {}}]}
r = requests.post(f'{API}/jobs', headers=H, data={'pipeline': json.dumps(pipeline)}, files=[('file', open('input.mp4', 'rb'))])
job = r.json()['job_id']
while (s := requests.get(f'{API}/jobs/{job}', headers=H).json())['status'] in ('queued', 'running'):
time.sleep(1)
out = requests.get(f'{API}/jobs/{job}/download', headers=H)
open('result', 'wb').write(out.content)