REST API reference
Upscale images from your own code. Authenticate with an API key, post one file, get the enlarged PNG back on the same request, and read the cost from the response headers.
Last updated
The API runs the same upscale engine as the browser tool, over the same core module, so the limits are the same wherever you call from. One request does one image and answers with the enlarged PNG directly: no job id to poll, no download URL to follow, no copy of your upload stored anywhere.
The base URL in these examples is https://imageupscaler.tools.
Authentication
Create a key in the dashboard under API keys. The key is shown exactly once, at creation. Only a SHA-256 hash of it is stored, so nobody can recover a lost key for you and you will need to mint a new one.
Send it as a bearer token:
Authorization: Bearer sk_your_key_here
An x-api-key: sk_your_key_here header works too. A missing, unknown, or revoked
key returns 401. A suspended account returns 403.
Upscale an image
POST /api/v1/upscale
Content-Type: multipart/form-data
| Field | Required | Description |
|---|---|---|
image |
yes | The image to upscale. JPG, PNG, or WebP. file is accepted as an alias. |
factor |
yes | 2 or 4. The output is the input dimensions times this factor, exactly. |
model |
yes | fast, quality, or anime. |
curl -X POST https://imageupscaler.tools/api/v1/upscale \
-H "Authorization: Bearer $API_KEY" \
-F "[email protected]" \
-F "factor=2" \
-F "model=fast" \
-o upscaled.png
The response body is the PNG itself, with Content-Type: image/png and a
Content-Disposition filename of upscaled-<width>x<height>.png. The numbers you
need to account for a call ride in headers, so a client does not have to decode
the image to learn them:
curl -X POST https://imageupscaler.tools/api/v1/upscale \
-H "x-api-key: $API_KEY" \
-F "[email protected]" \
-F "factor=4" \
-F "model=anime" \
-D headers.txt \
-o upscaled.png
grep -i '^x-upscale-' headers.txt
A 4x anime call prints:
X-Upscale-Model: anime
X-Upscale-Factor: 4
X-Upscale-In-Dims: 600x400
X-Upscale-Out-Dims: 2400x1600
X-Upscale-Ms: 8123
X-Upscale-Free: false
X-Upscale-Credits: 2
X-Upscale-Remaining: 98
X-Upscale-Free says whether the call came out of the daily allowance instead of
credits. X-Upscale-Credits is what it cost. X-Upscale-Remaining is what is
left, in free upscales today or monthly credits. Every plan has a monthly credit
cap, so the header always carries a number.
Errors
Every error is JSON with an error sentence you can show a user and a code you
can branch on.
| Status | code |
Meaning |
|---|---|---|
400 |
invalid_request, missing_image, invalid_parameter |
The request was not multipart, image was missing, or factor/model was outside the allowlist. |
401 |
unauthorized |
No key, an unknown key, or a revoked key. |
403 |
suspended |
The account is suspended. |
413 |
input_too_large, plan_input_cap, plan_output_cap |
The file, the input, or the projected output is over a cap. The sentence names the exact limit. |
415 |
unsupported_image, animated_image |
The bytes are not JPG, PNG, or WebP, or they are an animation the worker would flatten. |
429 |
rate_limited, out_of_credits, free_daily_limit |
Too many requests, no credits left, or the day's free allowance is used up. |
502 |
worker_bad_output |
An internal error: the enlargement did not match the requested size, so it was refused rather than returned. |
503 |
metering_unavailable, worker_unavailable |
A dependency is down. Try again shortly. |
Two things are worth being explicit about, because they are the point of the error design:
- The caps are checked from the input header before inference. A request that cannot be delivered, such as a 4x whose output would exceed your plan's output cap, is refused in milliseconds and costs nothing.
- A
429is never charged. The per-key rate limit runs before any credit is reserved, and a credit or free-allowance refusal is decided before the work starts and refunded if the work cannot finish. Nothing that came back429moved your balance.
Rate limit
Each API key may make 60 requests in any 60-second window. Over that, the
request is refused with 429 and code rate_limited before it is charged:
{
"error": "Rate limit exceeded: 60 requests per 60 seconds per API key. Try again in 37 seconds.",
"code": "rate_limited"
}
Every response carries the state so a client can slow down before it is refused rather than only learning the limit from the refusal:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 37
X-RateLimit-Reset is the number of seconds until the window resets, which is
also the Retry-After value sent with a 429.
Credits and limits
Upscaling is free within the daily free allowance, and metered in credits on a paid plan. The schedule is fixed by the factor: a 2x costs 1 credit and a 4x costs 2 credits. A 4x is twice the work of a 2x, so it is twice the price.
- Re-sending the same image at the same factor costs nothing. The charge is keyed by the image content and the factor, so a repeat call is a free re-download and the balance does not move.
- A
429costs nothing. It is a refusal, not a receipt. - The free tier is 3 upscales a day per IP with no signup. A FREE-plan key shares that per-IP allowance.
Limits that decide the response, all enforced from the input header:
| Limit | Free | Paid |
|---|---|---|
| Input pixels | 2 MP | 25 MP |
| Output pixels | 8 MP | 64 MP |
A single upload is 50 MB at most, which the engine applies the same way on every plan.
See /pricing for the current plans and credit packs. The browser tool at / has the same limits and the same rules, so a call that works there works here.