Pilio API Documentation
Use permanent API keys to create asynchronous image processing, PDF watermark removal, and GPT Image 2 tasks.
Basics
Pilio API async task flow
Create file -> Upload file -> Create task -> Poll results -> Download file
- 1
Create file
POST/files/batch-createReceive file_id and upload URL
- 2
Upload file
PUTupload_urlSend binary to the presigned URL
- 3
Create task
POST/v1/images/...Pass file_id and receive task_id
- 4
Poll results
GETstatus_url / result_urlWait for Succeeded or Failed
- 5
Download file
GETdownload_urlFetch the temporary result URL
Recommended async API flow: prepare a file ID, upload the file, create the task, poll status and result, then download the output file.
All creation endpoints return IDs immediately. After completion, read the file list from result_url and use download_url for the final download.
Authentication
Send your API key with every request. You can use either header form.
Authorization: Bearer pilio_sk_your_key_here
# or
X-API-Key: pilio_sk_your_key_here- Base URL
https://pilio.ai- Content-Type
application/json
5-minute quickstart
This flow creates an upload file, uploads the binary to a presigned URL, creates an async task, then polls for the result.
# 1) Create upload file records. This example explicitly chooses simple upload.
# Read data.items[0].id and data.items[0].upload_url.
curl -X POST "https://pilio.ai/v1/files/batch-create" -H "Authorization: Bearer pilio_sk_your_key_here" -H "Content-Type: application/json" -d '{"files":[{"nanoid":"V1StGXR8_Z5jdHi6B-myT","name":"reference.png","type":"png","size":1024}],"upload_mode":"simple","storage_intent":"temporary"}'
# 2) Upload the file to the presigned URL. Do not send your Pilio API key here.
# After a simple upload_url upload succeeds, go to step 3. Do not call /v1/files/:id/complete.
curl -X PUT "<upload_url>" -H "Content-Type: image/png" --data-binary @reference.png
# 3) Create an image watermark removal task with the file ID
curl -X POST "https://pilio.ai/v1/images/remove-watermark" -H "Authorization: Bearer pilio_sk_your_key_here" -H "Content-Type: application/json" -d '{"image_file_id":"<file_id>","mode":"auto"}'
# 4) Poll task status until status is Succeeded or Failed
curl -H "Authorization: Bearer pilio_sk_your_key_here" "https://pilio.ai/v1/tasks/<task_id>/status"
# 5) Fetch the result and download data.files[0].download_url
curl -H "Authorization: Bearer pilio_sk_your_key_here" "https://pilio.ai/v1/tasks/<task_id>/result"Response envelope
Every Pilio API response uses the same outer envelope. Check both the HTTP status and code.
{
"code": 200,
"message": "success",
"data": { ... }
}File upload and download
Image editing and PDF watermark removal endpoints receive file IDs, not raw multipart file bodies. Create upload records first, upload to the returned URL, then pass the file ID to a task endpoint.
upload_mode=simpleexplicitly chooses one-file upload. Whenupload_urlis returned, PUT the file binary to that URL and do not call complete.upload_mode=multipartexplicitly chooses multipart upload. Whenupload_urlsis returned, PUT each part in order, collect eachETag, then call complete.upload_mode=autolets the server choose by file size. Client code should follow the returned field instead of guessing the upload path.- Do not send the Pilio API key to presigned upload or download URLs.
Main flow boundaries
batch-createonly creates file records and upload URLs. It does not upload file content.- Returned
upload_url: run onePUT, then create the task withitems[].id. Do not call/v1/files/:id/complete. - Returned
upload_urls: after all partPUTrequests succeed, call/v1/files/:id/complete, then create the task withitems[].id. - Task creation accepts file IDs such as
image_file_id,image_file_ids, orpdf_file_id. Do not passnanoid,upload_url,upload_urls, orupload_id. - If upload URLs expire or multipart state is lost, call
batch-createagain to get fresh upload URLs.
/v1/files/batch-create
Create file upload records and return either a simple upload_url or multipart upload_urls based on upload_mode and file size.
Request example
{
"files": [
{
"nanoid": "V1StGXR8_Z5jdHi6B-myT",
"name": "reference.png",
"type": "png",
"size": 1024
}
],
"upload_mode": "simple",
"storage_intent": "temporary"
}Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
files | SourceFile[] | yes | Files to create upload credentials for; at least one item. |
files[].nanoid | string | yes | Client-generated 21-character nanoid for matching returned file records to local files. |
files[].name | string | yes | Original file name, up to 300 characters; extension must match type. |
files[].type | enum | yes | File extension type, such as jpg, jpeg, png, webp, or pdf. |
files[].size | integer | no | File size in bytes; auto mode uses it to choose upload_url or upload_urls. |
upload_mode | enum | no | auto, simple, or multipart. simple returns upload_url; multipart returns upload_urls; auto lets the server choose by size. |
storage_intent | enum | no | Developer API uploads currently only support temporary; use temporary for image editing and PDF watermark removal inputs. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
total | integer | yes | Number of file records created. |
items | File[] | yes | File records; pass the returned id as image_file_id, image_file_ids, or pdf_file_id. |
items[].id | string | yes | File ID. |
items[].nanoid | string | yes | The nanoid from your request, useful for client-side matching. |
items[].status | FileStatus | yes | Initially Pending; finish the presigned PUT upload before creating a task with this file ID. |
items[].upload_url | string | yes for simple upload | Presigned URL for a single PUT upload. Do not call complete when this field is returned. |
items[].upload_urls | UploadPart[] | yes for multipart upload | Presigned URLs for multipart PUT uploads. Upload every part, then call complete. |
items[].upload_id | string | yes for multipart upload | Object storage multipart upload ID, mainly for troubleshooting; complete only needs parts. |
/v1/files/:id/complete
Complete a multipart upload (optional). Call this only when batch-create returns upload_urls; do not call it for simple upload_url uploads.
Request example
{
"parts": [
{ "part_number": 1, "etag": ""etag-from-upload-response"" }
]
}Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
id | string(path) | yes for multipart upload | File ID returned as items[].id by batch-create; do not call this endpoint for simple upload_url uploads. |
parts | CompletedPart[] | yes for multipart upload | Successfully uploaded parts; part numbers must be continuous and start at 1. |
parts[].part_number | integer | yes for multipart upload | Part number, starting at 1. |
parts[].etag | string | yes for multipart upload | ETag response header returned by object storage after each part upload. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
data | null/object | no | Success means the multipart object has been merged; simple upload_url uploads do not need this endpoint. |
Task polling
Creation endpoints return immediately with a task ID. Poll status_url until the task reaches Succeeded or Failed, then read result files from result_url.
{
"code": 200,
"message": "success",
"data": {
"task_id": "2048...",
"status": "Processing",
"status_url": "/v1/tasks/2048.../status",
"result_url": "/v1/tasks/2048.../result",
"created_at": "2026-04-25T12:00:00Z"
}
}/v1/tasks/:id/status
Query task status. :id is the task_id returned by a creation endpoint.
Request example
No request body.Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
id | string(path) | yes | Task ID. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
task_id | string | yes | Task ID. |
status | TaskStatus | yes | Pending, Processing, Succeeded, or Failed. |
error_type | string | no | Failure category, usually present only when the task failed. |
error_message | string | no | Failure message, usually present only when the task failed. |
created_at | string(date-time) | no | Task creation time. |
updated_at | string(date-time) | no | Last update time. |
result_url | string | no | URL for reading task results. |
/v1/tasks/:id/result
Query task results. Successful tasks return downloadable files.
Request example
No request body.Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
id | string(path) | yes | Task ID. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
task_id | string | yes | Task ID. |
status | TaskStatus | yes | If the task has not succeeded yet, files may be empty. |
error_type | string | no | Failure category. |
error_message | string | no | Failure message. |
files | ResultFile[] | no | One or more output files. |
zip_file | ResultFile | no | May be present for batched output. |
Shared schemas
TaskStatus
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
Pending | TaskStatus | yes | Created and waiting for worker scheduling. |
Processing | TaskStatus | yes | The task is running; keep polling status_url. |
Succeeded | TaskStatus | yes | The task finished successfully; call result_url for output files. |
Failed | TaskStatus | yes | The task failed; read error_type and error_message. |
ResultFile
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
id | string | yes | File ID. |
name | string | no | File name. |
type | string | no | File type. |
size | integer | no | File size in bytes. |
download_url | string | no | Temporary download URL, issued for successful results. |
Errors
Errors keep the same envelope shape. Use the numeric code and structured data for program logic instead of parsing the human message.
{
"code": 1402,
"message": "Invalid token",
"data": null
}Resources
OpenAPI file
A public OpenAPI JSON file is available for Postman, Apifox, Insomnia, Bruno, code generators, and AI-assisted integration.
Download /developers/pilio-openapi.jsonSDK
Pilio now provides an official JavaScript / TypeScript SDK and CLI. For other languages, keep using the OpenAPI JSON as the source of truth and generate a thin client that wraps authentication, response envelopes, polling, and downloads.
Install & usage
pilioai/pilio-js contains the typed @pilio/sdk package and the @pilio/cli command-line tool for Node.js, automation scripts, and CI workflows.
pnpm add @pilio/sdk
pnpm dlx @pilio/cli --helpCLI quick commands
# Run the CLI without global install
pnpm dlx @pilio/cli --help
# Use PILIO_API_KEY from the process environment
pnpm dlx @pilio/cli gpt-image-2 --prompt "A cinematic product photo" --aspect-ratio 3:2
pnpm dlx @pilio/cli nano-banana-2 --prompt "A clean product poster" --aspect-ratio 1:1 --resolution 1K
pnpm dlx @pilio/cli task wait <task_id>SDK upload and task example
import { readFile } from "node:fs/promises";
import { PilioClient } from "@pilio/sdk";
const client = new PilioClient({
apiKey: process.env.PILIO_API_KEY!,
});
const image = await readFile("portrait.png");
const file = await client.files.upload({
name: "portrait.png",
type: "png",
data: new Blob([image]),
size: image.byteLength,
});
const task = await client.images.removeBackground({
image_file_id: file.id!,
});
const result = await client.tasks.wait(task.task_id);
console.log(result.files);Agent Skills
Pilio Agent Skills provide small, ready-to-use skill packs for AI agents. They drive Developer API workflows with natural language and delegate execution to @pilio/cli.
Install skills
pilioai/skills provides GPT Image 2, Nano Banana 2, background removal, and other skill packs for AI agents to call Pilio Developer API with natural language.
pnpm dlx skills add pilioai/skills --skill gpt-image-2
pnpm dlx skills add pilioai/skills --skill nano-banana-2
pnpm dlx skills add pilioai/skills --skill remove-backgroundTool APIs
GPT Image 2
/v1/images/gpt-image-2
Create a GPT Image 2 image task. Send only a prompt for text-to-image, or include image_file_ids for reference-image editing or composition.
Request example
{
"prompt": "A cinematic product photo of an orange perfume bottle",
"aspect_ratio": "3:2",
"output_count": 2,
"resolution": "2K"
}Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
prompt | string | yes | Generation or editing prompt, up to 32000 characters. |
negative_prompt | string | no | Negative prompt, up to 32000 characters. |
image_file_ids | string[] | no | Reference image file IDs. Omit for text-to-image; pass 1-16 items for reference-image editing or composition. Supports jpg, jpeg, png, and webp. |
aspect_ratio | enum | yes without references; no with references | 1:1, 3:2, 2:3, 3:4, 4:3, 4:5, 5:4, 16:9, 9:16, 21:9, or auto. |
output_count | integer | no | Number of output images: 1, 2, or 4. |
resolution | enum | no | 1K, 2K, or 4K. Prefer this for normal output-size control. |
quality | enum | no | Advanced optional tier: auto, low, medium, or high. Defaults to auto. It affects detail, compute effort, and generation time, not resolution. |
preprocess_mode | enum | no | off or auto. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
task_id | string | yes | Async task ID. |
status | TaskStatus | yes | Usually Processing when the task is created. |
status_url | string | yes | URL for polling task status. |
result_url | string | yes | URL for reading task results. |
created_at | string(date-time) | no | Task creation time. |
Nano Banana 2
/v1/images/nano-banana-2
Create a Nano Banana 2 image task. Send only a prompt for text-to-image, or include image_file_ids for reference-image editing or composition.
Request example
{
"prompt": "Turn these references into a clean product poster",
"image_file_ids": ["123456789", "123456790"],
"aspect_ratio": "16:9",
"resolution": "1K"
}Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
prompt | string | yes | Generation or editing prompt, up to 32000 characters. |
negative_prompt | string | no | Negative prompt, up to 32000 characters. |
image_file_ids | string[] | no | Reference image file IDs. Omit for text-to-image; pass 1-14 items for reference-image editing or composition. Supports jpg, jpeg, png, and webp. |
aspect_ratio | enum | yes without references; no with references | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9, 1:4, 4:1, 1:8, or 8:1. |
output_count | integer | no | Number of output images: 1, 2, or 4. |
resolution | enum | no | 0.5K, 1K, 2K, or 4K. |
preprocess_mode | enum | no | off or auto. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
task_id | string | yes | Async task ID. |
status | TaskStatus | yes | Usually Processing when the task is created. |
status_url | string | yes | URL for polling task status. |
result_url | string | yes | URL for reading task results. |
created_at | string(date-time) | no | Task creation time. |
Image watermark removal
/v1/images/remove-watermark
Create an image watermark removal task with automatic detection or manual boxes.
Request example
{
"image_file_id": "123456789",
"mode": "manual",
"boxes": [{ "x1": 10, "y1": 15, "x2": 60, "y2": 70 }]
}Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
image_file_id | string | yes | File ID of the image to process. |
mode | enum | no | auto detects watermark areas automatically; manual uses explicit boxes. Default: auto. |
boxes | Box[] | yes when mode is manual | Manual watermark regions, 1-5 boxes. Coordinates are percentages of image width and height. |
boxes[].x1 | integer | yes when mode is manual | Top-left X, 0-100. |
boxes[].y1 | integer | yes when mode is manual | Top-left Y, 0-100. |
boxes[].x2 | integer | yes when mode is manual | Bottom-right X, 0-100. |
boxes[].y2 | integer | yes when mode is manual | Bottom-right Y, 0-100. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
task_id | string | yes | Async task ID. |
status | TaskStatus | yes | Usually Processing when the task is created. |
status_url | string | yes | URL for polling task status. |
result_url | string | yes | URL for reading task results. |
created_at | string(date-time) | no | Task creation time. |
Background removal
/v1/images/remove-background
Create an image background removal task that returns a transparent-background image.
Request example
{
"image_file_id": "123456789",
"industry_type": "portrait",
"quality_type": "original",
"trim_transparent_background": true
}Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
image_file_id | string | yes | File ID of the image to cut out; supports jpg, jpeg, png, webp, and heic. |
industry_type | enum | no | portrait, product, general, and compatible aliases. Default: portrait. |
quality_type | enum | no | original, fast, and compatible aliases. Default: original. |
trim_transparent_background | boolean | no | Trim extra transparent canvas around the cutout. Default: false. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
task_id | string | yes | Async task ID. |
status | TaskStatus | yes | Usually Processing when the task is created. |
status_url | string | yes | URL for polling task status. |
result_url | string | yes | URL for reading task results. |
created_at | string(date-time) | no | Task creation time. |
Image upscale
/v1/images/upscale
Create an image upscaling task to improve resolution and clarity.
Request example
{
"image_file_id": "123456789",
"method": "ai_super_resolution",
"preset": "hd",
"type": "2X",
"enhance_face": true,
"enhance_quality": true
}Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
image_file_id | string | yes | File ID of the image to upscale; supports heic, jpg, png, and webp. |
method | enum | no | ai_super_resolution, super_resolution, or enhance. Default: ai_super_resolution. |
preset | enum | no | auto or hd. Default: auto. |
type | string | no | Upscale factor. Default: 2X. |
enhance_face | boolean | no | Enhance face details. |
enhance_quality | boolean | no | Enhance overall image quality. |
enhance_text | boolean | no | Enhance text clarity. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
task_id | string | yes | Async task ID. |
status | TaskStatus | yes | Usually Processing when the task is created. |
status_url | string | yes | URL for polling task status. |
result_url | string | yes | URL for reading task results. |
created_at | string(date-time) | no | Task creation time. |
PDF watermark removal
/v1/pdfs/remove-watermark
Create a PDF watermark removal task with structured or AI reconstruction mode.
Request example
{
"pdf_file_id": "123456789",
"mode": "editable"
}Request fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
pdf_file_id | string | yes | File ID of the PDF to process. |
mode | enum | no | editable for structured watermark removal; ai for page-by-page image reconstruction. Default: editable. |
Response data fields
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
task_id | string | yes | Async task ID. |
status | TaskStatus | yes | Usually Processing when the task is created. |
status_url | string | yes | URL for polling task status. |
result_url | string | yes | URL for reading task results. |
created_at | string(date-time) | no | Task creation time. |
Limits and billing
Upload limits
| Field | Type | Required | Notes / allowed values |
|---|---|---|---|
Files per batch | integer | fixed limit | Up to 500 files. |
Single file size | bytes | fixed limit | Up to 2 GB. |
Multipart threshold | bytes | fixed rule | With upload_mode=auto, files larger than 5 MB usually receive upload_urls; simple explicitly asks for one upload_url. |
Upload URL lifetime | duration | fixed rule | upload_url / upload_urls are valid for about 2 hours; call batch-create again after expiration. |
Download URL lifetime | duration | fixed rule | download_url is valid for about 24 hours; query result_url again after expiration. |
- Large customer accounts may have a custom API multiplier. Actual billing is based on the server-side multiplier snapshot captured when the task is created.
- Image watermark removal default API price: 2 credits per image.
- Background removal default API price: 10 credits per image.
- Image upscale default API price: 6 credits per image.
- PDF watermark removal editable mode default API price: 4 credits per file.
- PDF watermark removal AI mode default API price: 2 credits per page.
- GPT Image 2 watermark-free mode default API price: 5.34 credits per output image.
- No separate API usage ledger is created; credit records remain the source of truth.
- Web and API tasks share the same in-flight pool:
Pending + Processing <= 20. - The first version only supports asynchronous APIs.
- See the full tool pricing table.