← Blog
Article

Image Upscale API Tutorial with Node.js and Python

Step by step with the Image Upscale API: turn an image into a data URI, submit a job, check status, then download the PNG. Includes Node.js and Python code and the full error code list.

This guide shows how to use the Image Upscale API from start to finish, until the result is saved on disk: turn an image into a data URI, submit a job, check its status, then download the result. Code samples are available in Node.js and Python.

What You'll Need

  • An API key from dashboard.apiindonesia.id. New accounts get a bonus of 3,000 credits, enough to try the 2x tier.
  • A PNG, JPEG or WebP image of at most 1 MB and at most 1 MP (for example 1000×1000 or 1200×800).

The Short Version

Step Endpoint Cost
1. Submit a job POST /api/v1/image/upscale/{2x|4x|8x} 250 / 1,000 / 4,000 credits
2. Check status GET /api/v1/image/jobs/{job_id} Free
3. Download the result GET /api/v1/image/jobs/{job_id}/file Free

Base URL: https://use.apiindonesia.id. Every request uses the x-api-key header.

Upscaling runs asynchronously. The submit call answers right away with 202 Accepted and a job_id, then you check the status every few seconds. In our test, a 2x job for an 800×600 photo finished in about 6 seconds.

Step 1: Turn the Image Into a Data URI

The API only accepts images as a data URI (data:image/jpeg;base64,...). Plain http URLs are rejected on purpose, so you don't need to host the image anywhere.

Full Example: Node.js (18+)

import { readFile, writeFile } from 'node:fs/promises'

const API = 'https://use.apiindonesia.id/api/v1/image'
const headers = { 'x-api-key': process.env.API_INDONESIA_KEY }
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))

async function upscale(path, tier = '2x') {
  // 1. Data URI
  const bytes = await readFile(path)
  const mime = path.endsWith('.png') ? 'image/png' : path.endsWith('.webp') ? 'image/webp' : 'image/jpeg'
  const image = `data:${mime};base64,${bytes.toString('base64')}`

  // 2. Submit the job
  const submit = await fetch(`${API}/upscale/${tier}`, {
    method: 'POST',
    headers: { ...headers, 'Content-Type': 'application/json' },
    body: JSON.stringify({ image }),
  })
  const job = await submit.json()
  if (submit.status !== 202) throw new Error(`${job.error.code}: ${job.error.message}`)

  // 3. Poll the status until it finishes
  let status
  do {
    await sleep(3000)
    status = (await (await fetch(`${API}/jobs/${job.data.job_id}`, { headers })).json()).data
  } while (status.status === 'pending')

  if (status.status === 'failed') throw new Error(`${status.error.code}: ${status.error.message}`)

  // 4. Download the result (PNG), within 1 hour
  const file = await fetch(status.download_url, { headers })
  await writeFile(`upscaled-${tier}.png`, Buffer.from(await file.arrayBuffer()))
  return status.result // { width, height }
}

console.log(await upscale('produk.jpg', '2x'))

Full Example: Python

import base64, os, time, requests

API = "https://use.apiindonesia.id/api/v1/image"
HEADERS = {"x-api-key": os.environ["API_INDONESIA_KEY"]}

def upscale(path, tier="2x"):
    mime = "image/png" if path.endswith(".png") else "image/webp" if path.endswith(".webp") else "image/jpeg"
    with open(path, "rb") as f:
        image = f"data:{mime};base64,{base64.b64encode(f.read()).decode()}"

    submit = requests.post(f"{API}/upscale/{tier}", json={"image": image}, headers=HEADERS)
    body = submit.json()
    if submit.status_code != 202:
        raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")

    job_id = body["data"]["job_id"]
    while True:
        time.sleep(3)
        status = requests.get(f"{API}/jobs/{job_id}", headers=HEADERS).json()["data"]
        if status["status"] != "pending":
            break

    if status["status"] == "failed":
        raise RuntimeError(f"{status['error']['code']}: {status['error']['message']}")

    result = requests.get(status["download_url"], headers=HEADERS)
    with open(f"upscaled-{tier}.png", "wb") as f:
        f.write(result.content)
    return status["result"]

print(upscale("produk.jpg", "2x"))

Example Status Response

{
  "success": true,
  "data": {
    "id": "2fa711a4-deca-42df-90c2-fde8e528de17",
    "status": "succeeded",
    "tier": "2x",
    "credits_charged": 250,
    "source": { "width": 800, "height": 600 },
    "result": { "width": 1600, "height": 1200 },
    "download_url": "https://use.apiindonesia.id/api/v1/image/jobs/2fa711a4-deca-42df-90c2-fde8e528de17/file",
    "download_expires_at": "2026-10-23T09:34:55.583Z",
    "error": null
  }
}

Error Codes You Need to Handle

Status Code Meaning
400 INVALID_IMAGE Not a valid PNG/JPEG/WebP data URI
400 IMAGE_TOO_LARGE More than 1 MB or more than 1 MP
402 INSUFFICIENT_CREDITS Not enough credit balance
403 TIER_REQUIRES_TOPUP The 4x/8x tiers require an account that has topped up before
403 BONUS_UPSCALE_LIMIT The 4 jobs included with the signup bonus have been used
410 RESULT_EXPIRED More than 1 hour has passed, submit the image again
502 UPSTREAM_UNAVAILABLE The processor is having trouble, no charge is made

Rejected requests don't deduct credits, and jobs that fail after being accepted are refunded in full.

Tips

  • Compress to JPEG or WebP first if your PNG is over 1 MB. For upscaling, JPEG at quality 90 is good enough.
  • Save the result to your own storage. Results are only available for 1 hour and we don't keep a copy.
  • Batch: send one request per image and store its job_id. Checking the status is free.

FAQ

Why doesn't the API accept image URLs? So our servers can't be pointed at fetching arbitrary URLs. Images are sent as a data URI, which also means you don't need to host the image anywhere.

How long does one upscale job take? Usually a few seconds. In our test, an 800×600 photo on the 2x tier finished in about 6 seconds. Check the status every 3 seconds as given by poll_after_seconds.

What happens if I download too late? After 1 hour, the download endpoint answers 410 RESULT_EXPIRED and you need to submit the image again.

What format comes back? PNG, at the size of the chosen tier scale: 2x, 4x or 8x the input size.

Start Now

Get an API key at dashboard.apiindonesia.id/register, take a look at the Image Upscale API product page, and read the use case in Image Upscale API for Marketplace Product Photos.

Get Started

Start Integrating
Today

Sign up for free and get a 3,000 credit bonus (Rp3,000) once your email is verified, grab an API key, and call your first endpoint right away. No credit card, no tedious approval process.

Dot wave visualization