Upscayl AI UpscalerUpscayl AI
Developer documentation

Image Upscaler API documentation

Create, monitor, and safely retry asynchronous 2x and 4x image upscaling tasks with the Upscayl REST API.

API price

0.7 credit

per successfully upscaled image. Failed tasks are automatically refunded.

Asynchronous

Submit quickly and process results in the background.

Safe retries

Idempotency prevents duplicate tasks and duplicate charges.

Simple billing

The same 0.7 credit price for 2x and 4x standard upscaling.

API overview

Your server sends authenticated REST requests to Upscayl. Creating an upscale is asynchronous: the create endpoint returns a task immediately, and the result endpoint reports its latest state.

MethodEndpointPurpose
POST
/api/v1/upscalesCreate one upscale task.
GET
/api/v1/upscales/{id}Retrieve status, result, and billing state.

Polling is supported

Use the returned urls.get URL until the task reaches a terminal state.

Customer webhooks are not available yet

Do not send a callback URL. A separate signed webhook contract will be published before this option is enabled.

Quick start

Create a key, submit one image, then start polling after 3 seconds and gradually increase the interval up to 10 seconds.

  1. 1Create an API key
  2. 2Submit an image URL
  3. 3Download the result

Authentication

API access is activated after your first successful credit purchase. Add your secret key to the Authorization header and keep it on your server—never put it in browser or mobile application code.

Authorization: Bearer up_live_your_api_key
API keys are shown once when created. Store them in a server-side secret manager, rotate them if exposed, and revoke keys you no longer use.
POST
/api/v1/upscales

Create an upscale

The image must be available through a public HTTPS URL. A 202 Accepted response means the task was accepted, not that processing has finished.

cURL
curl --request POST \
  --url https://www.upscayl.app/api/v1/upscales \
  --header "Authorization: Bearer $UPSCAYL_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: order-1842-image-01" \
  --data '{
    "image_url": "https://example.com/photo.jpg",
    "scale": 2,
    "face_enhance": false,
    "output_format": "png"
  }'

Required headers

HeaderRequiredDescription
AuthorizationYesBearer API key.
Content-TypeYesapplication/json
Idempotency-KeyYes8–128 characters. Reuse it when retrying the same logical image request.

JSON body

FieldTypeDescription
image_urlstringRequired public HTTPS URL ending in .jpg, .jpeg, .png, or .webp.
scaleinteger2 or 4. Defaults to 2.
face_enhancebooleanImprove faces. Defaults to false.
output_formatstringpng. Other formats are coming later.
202 Accepted
{
  "id": "upscale-7d2f...",
  "object": "upscale",
  "status": "processing",
  "urls": {
    "get": "https://www.upscayl.app/api/v1/upscales/upscale-7d2f..."
  },
  "usage": {
    "credits_reserved": 0.7,
    "credits_charged": 0
  }
}

Save both id and urls.get. They are the source of truth if the client disconnects after submission.

GET
/api/v1/upscales/{id}

Check task status

Start after 3 seconds, use gradual backoff, and stop when status becomes succeeded or failed. GET requests may be safely retried after temporary network or server errors.

cURL
curl \
  --url https://www.upscayl.app/api/v1/upscales/UPSCALING_TASK_ID \
  --header "Authorization: Bearer $UPSCAYL_API_KEY"
Processing
{
  "id": "upscale-7d2f...",
  "object": "upscale",
  "status": "processing",
  "urls": {
    "get": "https://www.upscayl.app/api/v1/upscales/upscale-7d2f..."
  },
  "usage": {
    "credits_reserved": 0.7,
    "credits_charged": 0
  }
}
Successful result
{
  "id": "upscale-7d2f...",
  "object": "upscale",
  "status": "succeeded",
  "urls": {
    "get": "https://www.upscayl.app/api/v1/upscales/upscale-7d2f..."
  },
  "result": {
    "url": "https://cdn.upscayl.app/hd-photos/result.png",
    "format": "png"
  },
  "usage": {
    "credits_charged": 0.7
  }
}
Failed and refunded
{
  "id": "upscale-7d2f...",
  "object": "upscale",
  "status": "failed",
  "error": {
    "code": "image_processing_failed",
    "message": "The image could not be processed. No credits were charged."
  },
  "usage": {
    "credits_charged": 0,
    "refund_status": "refunded"
  }
}
Python
import os
import time
import requests

api_key = os.environ["UPSCAYL_API_KEY"]
headers = {
    "Authorization": f"Bearer {api_key}",
    "Idempotency-Key": "order-1842-image-01",
}

response = requests.post(
    "https://www.upscayl.app/api/v1/upscales",
    headers=headers,
    json={"image_url": "https://example.com/photo.jpg", "scale": 2},
    timeout=(10, 60),
)
response.raise_for_status()
task = response.json()

deadline = time.monotonic() + 300
interval = 3
while time.monotonic() < deadline:
    response = requests.get(
        task["urls"]["get"],
        headers={"Authorization": headers["Authorization"]},
        timeout=(10, 30),
    )
    response.raise_for_status()
    result = response.json()
    if result["status"] == "succeeded":
        print(result["result"]["url"])
        break
    if result["status"] == "failed":
        raise RuntimeError(result["error"]["message"])
    time.sleep(interval)
    interval = min(10, interval + 1)
else:
    raise TimeoutError(f"Task {task['id']} did not finish within 5 minutes")
Not yet available

Customer webhooks

The public API currently uses polling for result delivery. Upscayl does not send task events to customer callback URLs yet. Continue using urls.get; do not expose a webhook receiver solely for this integration.

When customer webhooks become available, they will be documented as a separate outbound contract covering event payloads, signature verification, duplicate delivery, acknowledgement time, retries, and polling fallback.

Retries and idempotency

Use a unique Idempotency-Key for each logical image request.

If POST times out or returns a temporary server error, retry with the same key and identical parameters.

The same key and parameters return the original task without another charge.

The same key with different parameters returns 409 idempotency_conflict.

To run a genuinely new task after a terminal failure, use a new key.

Billing

0.7 credit is reserved when a task is accepted.

A successful result charges 0.7 credit.

A confirmed failed task returns the reserved 0.7 credit.

A failed response can briefly report refund_status as pending while the refund is finalized.

Repeating the same Idempotency-Key returns the original task without another charge.

View credit plans

Limits

Input fileUp to 20 MB
FormatsJPEG, PNG, WebP
2x input sizeUp to 1.5 MP (0.8 MP with face enhance)
4x input sizeUp to 0.75 MP (0.5 MP with face enhance)
Create requests60 per minute
Active tasks20 at a time
Daily tasks2,500 per UTC day
Recommended client concurrency5–10 tasks

Errors

Errors use normal HTTP status codes and include a stable code plus a request ID for support.

Error response
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_scale",
    "message": "scale must be 2 or 4.",
    "request_id": "req_01abc..."
  }
}
400Invalid parameters, JSON, idempotency key, or image URL
401Missing, invalid, or revoked API key
402Not enough credits
403Paid API access is required or the API key lacks permission
404Task not found or not owned by this account
409Idempotency key was reused with different parameters
429Rate, concurrency, or daily limit reached
503Service temporarily unavailable
504An upstream request timed out

Rate-limit response headers

X-RateLimit-Limit is the create-request limit, X-RateLimit-Remaining is the remaining count in the current window, and X-RateLimit-Reset is the Unix reset time. Honor Retry-After on 429 responses.

Result retention

Result URLs are intended for delivery, not as your permanent asset archive. Download and store every successful result you need to keep. Upscayl does not currently promise a minimum public URL retention period in the API contract.