<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sherwood</title>
    <description>The latest articles on DEV Community by Sherwood (@sherwoodtools).</description>
    <link>https://dev.to/sherwoodtools</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4164560%2F7a2b9dbe-71f2-48fa-a158-3a020514036f.png</url>
      <title>DEV Community: Sherwood</title>
      <link>https://dev.to/sherwoodtools</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sherwoodtools"/>
    <language>en</language>
    <item>
      <title>Bulk-generate images from a list of prompts with one API call (FLUX, no API key)</title>
      <dc:creator>Sherwood</dc:creator>
      <pubDate>Mon, 19 Oct 2026 17:44:52 +0000</pubDate>
      <link>https://dev.to/sherwoodtools/bulk-generate-images-from-a-list-of-prompts-with-one-api-call-flux-no-api-key-2np</link>
      <guid>https://dev.to/sherwoodtools/bulk-generate-images-from-a-list-of-prompts-with-one-api-call-flux-no-api-key-2np</guid>
      <description>&lt;p&gt;Generating one image is easy: open any web playground, type a prompt, download the file. Generating two hundred images from inside a pipeline is a different job. The typical cases: product visuals for a catalog where each row of a spreadsheet needs its own picture, illustrations for a batch of blog posts or slides, placeholder art for a game or an app, creative variations for ads where you want four takes per concept.&lt;/p&gt;

&lt;p&gt;What you want there is boring: a list of prompts in, a list of image URLs out, the size and the seed for each so you can reproduce it, and a clear signal for the ones that failed. This post compares the usual ways to batch-generate images, then shows how to do it with one API call from curl, Python, JavaScript, n8n and an AI agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why batch image generation gets annoying
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Self-hosting FLUX or Stable Diffusion.&lt;/strong&gt; Full control, no per-image fee, any LoRA you like. In exchange, you need a GPU with enough VRAM, a queue, a way to serve the files, and someone to keep it running. It pays off at high, steady volume, and not before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calling an inference API (fal, Replicate, Together) directly.&lt;/strong&gt; Close to the metal and fast, but now your pipeline owns an API key, a retry policy, per-image polling, and a place to store the output files. Multiply that by every workflow that needs images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The big-model APIs (OpenAI, Google).&lt;/strong&gt; Great models, but you still sign up for a key, and the bill is per account, not per run. For a no-code tool or an agent, "go create an account and wire a secret" is where the automation dies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Looping over a single-image endpoint.&lt;/strong&gt; Most hosted generators take one prompt per call. Two hundred prompts means two hundred calls, your own concurrency control, and your own bookkeeping of which output belongs to which prompt.&lt;/p&gt;

&lt;p&gt;The pattern that removes all of this: one call that takes the whole list, runs the model, stores the files, and returns one record per image with its URL. That is what an Apify Actor does, and the rest of this post uses one I maintain: &lt;a href="https://apify.com/sherwood/image-generator-batch" rel="noopener noreferrer"&gt;Bulk AI Image Generator&lt;/a&gt; (FLUX.2 Klein 9B by default, FLUX Schnell in fast mode).&lt;/p&gt;

&lt;h2&gt;
  
  
  A batch image generator behind one API call
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: get a token
&lt;/h3&gt;

&lt;p&gt;Create a free Apify account and copy your API token from Settings → Integrations. There is no image-model key to manage: the model runs inside the Actor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: the input
&lt;/h3&gt;

&lt;p&gt;The input is a JSON object. Everything is optional; the default input generates two sample images so you can see the output shape before sending your own prompts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"A ceramic coffee mug on a wooden table, soft morning light, product photography"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Minimal flat illustration of a paper plane flying over pastel mountains"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imageSize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"square_hd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imagesPerPrompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"outputFormat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"png"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;prompts&lt;/code&gt; — the list. A single string in &lt;code&gt;prompt&lt;/code&gt;, or the aliases &lt;code&gt;texts&lt;/code&gt; and &lt;code&gt;queries&lt;/code&gt;, work too; everything is merged and deduplicated.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;imageSize&lt;/code&gt; — &lt;code&gt;square_hd&lt;/code&gt; (1024×1024), &lt;code&gt;landscape_4_3&lt;/code&gt;, &lt;code&gt;landscape_16_9&lt;/code&gt;, &lt;code&gt;portrait_4_3&lt;/code&gt;, &lt;code&gt;portrait_16_9&lt;/code&gt;, &lt;code&gt;square&lt;/code&gt;; or exact &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; in pixels (256 to 2048).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quality&lt;/code&gt; — &lt;code&gt;standard&lt;/code&gt; is FLUX.2 Klein 9B; &lt;code&gt;fast&lt;/code&gt; is FLUX Schnell, cheaper and quicker, for drafts and placeholders.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;imagesPerPrompt&lt;/code&gt; — up to 4 variations per prompt, each with its own seed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;seed&lt;/code&gt; — set it to reproduce a run; every result carries the seed it used either way.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxPrompts&lt;/code&gt; — hard cap per run (default 100, up to 1,000), so a malformed input cannot surprise you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: call it with curl
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.apify.com/v2/actors/sherwood~image-generator-batch/run-sync-get-dataset-items?token=&lt;/span&gt;&lt;span class="nv"&gt;$APIFY_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"prompts": ["A ceramic coffee mug on a wooden table, product photography"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The call blocks until the run finishes and returns the dataset items directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Python
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_APIFY_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sherwood/image-generator-batch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A ceramic coffee mug on a wooden table, product photography&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Minimal flat illustration of a paper plane over pastel mountains&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;imageSize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;landscape_16_9&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outputUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FAILED:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApifyClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apify-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_APIFY_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sherwood/image-generator-batch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A ceramic coffee mug on a wooden table, product photography&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;imagesPerPrompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputUrl&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What comes back
&lt;/h2&gt;

&lt;p&gt;One dataset item per generated image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A ceramic coffee mug on a wooden table, product photography"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"outputUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.apify.com/v2/key-value-stores/abc123/records/image-0000-0.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"seed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1545164682&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fal-ai/flux-2/klein/9b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1666&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;index&lt;/code&gt; is the position of the prompt in your input and &lt;code&gt;variant&lt;/code&gt; the variation number, so matching outputs back to your own rows is a sort, not a guess. &lt;code&gt;seed&lt;/code&gt; makes any image reproducible: send the same prompt, size and seed, get the same image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failed prompts come back as items, and are not charged
&lt;/h3&gt;

&lt;p&gt;A prompt the safety checker refuses, or an upstream error, produces an item with &lt;code&gt;"status": "error"&lt;/code&gt; and the reason in &lt;code&gt;error&lt;/code&gt;. It costs nothing, and nothing is silently dropped: you always get as many answers as questions. The safety checker is always on; this Actor is not a way around model content rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it from n8n
&lt;/h2&gt;

&lt;p&gt;The wiring is three nodes around the official Apify node (install "Apify" from the community nodes if you don't have it). Read a &lt;code&gt;prompt&lt;/code&gt; column from a Google Sheet; a Code node collects the non-empty prompts into one list; the Apify node runs &lt;code&gt;sherwood~image-generator-batch&lt;/code&gt; with that list as &lt;code&gt;prompts&lt;/code&gt;; a last Code node sorts the results by &lt;code&gt;index&lt;/code&gt; to put each image next to its prompt, and a Google Sheets node writes &lt;code&gt;image_url&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;seed&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt; back to each row. Because the whole sheet goes in one Actor call, the workflow runs once per batch, not once per row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it from an AI agent (MCP)
&lt;/h2&gt;

&lt;p&gt;Add the &lt;a href="https://mcp.apify.com" rel="noopener noreferrer"&gt;Apify MCP server&lt;/a&gt; to your agent and it can call the Actor like any tool: the input above, as arguments; the dataset items, as the result. Nothing in the input is required, so a one-line instruction ("generate an image for each of these ten product names") maps cleanly onto one call.&lt;/p&gt;

&lt;p&gt;The Actor is also eligible for agentic payments (x402, Skyfire, MPP): an agent can run it and pay per request without an Apify account at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing at the time of writing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image generated&lt;/strong&gt; — $0.01 per image delivered, 1024×1024 included. Larger sizes add $0.01 per started extra megapixel.&lt;/li&gt;
&lt;li&gt;Failed or safety-flagged prompts: free.&lt;/li&gt;
&lt;li&gt;No subscription, no minimum. Apify's free plan is enough to try it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two hundred catalog images cost about $2. Set a maximum cost per run in Apify and the Actor stops cleanly at the limit, reporting what remains instead of failing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Output files live in the run's key-value store and follow your Apify account's data retention; copy them out if you need them long-term.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;imagesPerPrompt&lt;/code&gt; tops out at 4, sizes at 2048 px per side.&lt;/li&gt;
&lt;li&gt;The model is FLUX.2 Klein 9B (or Schnell). If your brief needs a specific aesthetic from another model, this is not that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When I wouldn't use it
&lt;/h2&gt;

&lt;p&gt;For one image now and then, a free web playground is simpler. For a style that depends on a particular model or a custom LoRA, self-hosting or a direct inference API gives you the control this Actor deliberately hides. And at very high, steady volume — tens of thousands of images a month, every month — a rented GPU beats any per-image price. Everywhere between those: a list in, a list out, one call.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>javascript</category>
      <category>python</category>
    </item>
    <item>
      <title>How to upscale images in bulk with one API call (2x or 4x, no GPU)</title>
      <dc:creator>Sherwood</dc:creator>
      <pubDate>Mon, 05 Oct 2026 18:26:27 +0000</pubDate>
      <link>https://dev.to/sherwoodtools/how-to-upscale-images-in-bulk-with-one-api-call-2x-or-4x-no-gpu-5e5n</link>
      <guid>https://dev.to/sherwoodtools/how-to-upscale-images-in-bulk-with-one-api-call-2x-or-4x-no-gpu-5e5n</guid>
      <description>&lt;p&gt;Upscaling one image is easy: open a desktop app, drop the file, save the result. Upscaling a few hundred images from inside a pipeline is a different job. The typical cases: a supplier feed full of 600×600 product photos when the marketplace wants larger ones, AI-generated images that are too small for print, avatars and thumbnails pulled by a scraper that look soft at full width.&lt;/p&gt;

&lt;p&gt;What you want there is boring: a list of URLs in, a list of larger files out, the new width and height for each, and a clear signal for the ones that failed. This post compares the usual ways to increase image resolution in bulk, then shows how to do it with one API call from curl, Python, JavaScript, n8n and an AI agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bulk upscaling gets annoying
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Self-hosting a super-resolution model.&lt;/strong&gt; Open models such as Real-ESRGAN give you full control and no per-image fee. In exchange, you need a GPU, a queue, a way to move files in and out, and someone to keep it running. It pays off at high, steady volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Desktop apps like Upscayl.&lt;/strong&gt; Free, open source and good at the job, folders included. They run on the machine of whoever clicks the button, with that machine's GPU, which makes them a poor fit for a server-side pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hosted model APIs.&lt;/strong&gt; Providers such as Replicate or fal remove the GPU. You still sign up, manage a provider key and write the glue: one request per image, a concurrency limit, retries, storage for the results, matching each output to its input, and a plan for when image 37 of 200 fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SaaS upscalers.&lt;/strong&gt; Polished web apps, usually sold as a subscription or credit packs, and designed around someone uploading files in a browser.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;GPU on your side&lt;/th&gt;
&lt;th&gt;What you manage&lt;/th&gt;
&lt;th&gt;A list of URLs in one call&lt;/th&gt;
&lt;th&gt;Billing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Self-hosted model&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Model, queue, storage, uptime&lt;/td&gt;
&lt;td&gt;If you build it&lt;/td&gt;
&lt;td&gt;Your infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desktop app (Upscayl)&lt;/td&gt;
&lt;td&gt;Yes, local&lt;/td&gt;
&lt;td&gt;Running it by hand&lt;/td&gt;
&lt;td&gt;Folders, from the app&lt;/td&gt;
&lt;td&gt;Free, open source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosted model API&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Provider key, per-image loop, retries&lt;/td&gt;
&lt;td&gt;Usually one image per request&lt;/td&gt;
&lt;td&gt;Pay per use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SaaS upscaler&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Account, uploads&lt;/td&gt;
&lt;td&gt;Depends on the product&lt;/td&gt;
&lt;td&gt;Usually subscription or credits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apify actor (below)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Apify token&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Per image delivered, failures free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A batch image upscaler behind one API call
&lt;/h2&gt;

&lt;p&gt;What the first four rows leave open is hosted convenience (no GPU, no model-provider key) without writing the per-image glue yourself. That's the case the actor below covers. It's Image Upscaler API on the Apify Store (&lt;code&gt;sherwood/image-upscaler-batch&lt;/code&gt;): you send a list of image URLs, it runs each one through SeedVR2, an AI super-resolution model, and writes one result per image to a dataset.&lt;/p&gt;

&lt;p&gt;Disclosure: I maintain the Apify actor used below.&lt;/p&gt;

&lt;p&gt;The only credential you handle is your Apify API token, and there's no subscription to the actor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: get a token
&lt;/h3&gt;

&lt;p&gt;Create an Apify account, copy your API token from &lt;strong&gt;Settings &amp;gt; API &amp;amp; Integrations&lt;/strong&gt; in the Apify Console, and export it as &lt;code&gt;APIFY_TOKEN&lt;/code&gt;; every example below reads it from there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: the input
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imageUrls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/products/chair-600.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/products/lamp-600.png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"outputFormat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxOutputMegapixels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;imageUrls&lt;/code&gt;: public JPG, PNG or WebP URLs. The aliases &lt;code&gt;imageUrl&lt;/code&gt;, &lt;code&gt;urls&lt;/code&gt;, &lt;code&gt;images&lt;/code&gt; and &lt;code&gt;startUrls&lt;/code&gt; are merged into it and duplicates are removed, which helps when the list comes from another tool.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scale&lt;/code&gt;: &lt;code&gt;"2"&lt;/code&gt; doubles width and height, &lt;code&gt;"4"&lt;/code&gt; quadruples them. It's a string in the input schema, and Apify validates API input against the schema, so send &lt;code&gt;"4"&lt;/code&gt;, not &lt;code&gt;4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;outputFormat&lt;/code&gt;: &lt;code&gt;jpg&lt;/code&gt; (default), &lt;code&gt;png&lt;/code&gt; (keeps transparency) or &lt;code&gt;webp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxOutputMegapixels&lt;/code&gt;: a cap on output size, default 16. An image whose upscaled size would exceed it is upscaled to the cap instead. For reference, 4 MP is about 2000×2000.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxImages&lt;/code&gt;: stop after this many images, default 100.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every field is optional: an empty input upscales two sample images, a quick way to see the output shape.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: call it with curl
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.apify.com/v2/actors/sherwood~image-upscaler-batch/run-sync-get-dataset-items?maxTotalChargeUsd=1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$APIFY_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "imageUrls": [
      "https://example.com/products/chair-600.jpg",
      "https://example.com/products/lamp-600.png"
    ],
    "scale": "2"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;run-sync-get-dataset-items&lt;/code&gt; starts the run, waits for it, and returns the dataset items as a JSON array. The &lt;code&gt;maxTotalChargeUsd&lt;/code&gt; query parameter caps what the run can cost: when the cap is reached, the actor stops cleanly and the remaining images are reported, not processed.&lt;/p&gt;

&lt;p&gt;One constraint: synchronous endpoints wait at most 300 seconds, then return HTTP 408 while the run carries on. Processing typically takes 5 to 25 seconds per image, 4 images in parallel, so keep synchronous calls to a few dozen images. For bigger lists, use a client library: it waits as long as the run takes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Python
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pip install apify-client&lt;/code&gt;. This targets version 3 of the client, where &lt;code&gt;call()&lt;/code&gt; returns a typed &lt;code&gt;Run&lt;/code&gt; object. Older examples that read &lt;code&gt;run["defaultDatasetId"]&lt;/code&gt; were written for the 1.x and 2.x clients, which returned dicts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APIFY_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sherwood/image-upscaler-batch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;imageUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/products/chair-600.jpg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/products/lamp-600.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scale&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outputFormat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SUCCEEDED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The actor run did not finish successfully&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_dataset_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;list_items&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inputUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outputUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;width&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;height&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;FAILED &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inputUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sorting by &lt;code&gt;index&lt;/code&gt; puts the results back in input order. To cap the cost from Python, pass &lt;code&gt;max_total_charge_usd=Decimal("2")&lt;/code&gt; (from the &lt;code&gt;decimal&lt;/code&gt; module) to &lt;code&gt;call()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: JavaScript
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npm install apify-client&lt;/code&gt;, then run this as an ES module (&lt;code&gt;node upscale.mjs&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApifyClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apify-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIFY_TOKEN&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sherwood/image-upscaler-batch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;imageUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/products/chair-600.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outputFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxTotalChargeUsd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUCCEEDED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Run ended with status &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FAILED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What comes back
&lt;/h2&gt;

&lt;p&gt;One item per image, like this example from the README:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/product-small.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"outputUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.apify.com/v2/key-value-stores/abc123/records/upscaled-0000.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"megapixels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fal-ai/seedvr/upscale/image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;index&lt;/code&gt; is the image's position in your input list, so matching results to inputs never depends on order. &lt;code&gt;outputUrl&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;megapixels&lt;/code&gt; describe the upscaled file; &lt;code&gt;model&lt;/code&gt; and &lt;code&gt;durationMs&lt;/code&gt; tell you what ran and how long it took.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failed images come back as items, and are not charged
&lt;/h3&gt;

&lt;p&gt;An unreachable URL, a file that isn't an image, or an input over 25 MB doesn't break the batch. That image comes back with &lt;code&gt;"status": "error"&lt;/code&gt; and the reason in &lt;code&gt;error&lt;/code&gt;, and it is never charged. In practice: filter on &lt;code&gt;status&lt;/code&gt;, log the failures, and re-run just those URLs if the cause was temporary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it from n8n
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;With the Apify node.&lt;/strong&gt; Install the Apify community node (&lt;code&gt;@apify/n8n-nodes-apify&lt;/code&gt;; on n8n Cloud it's listed among the verified community nodes) and add your token as an Apify API credential. Then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An Apify node with the &lt;strong&gt;Run Actor&lt;/strong&gt; operation: actor &lt;code&gt;sherwood/image-upscaler-batch&lt;/code&gt;, the JSON above as custom input, &lt;strong&gt;Wait for finish&lt;/strong&gt; on.&lt;/li&gt;
&lt;li&gt;A second Apify node that reads the dataset items, with Dataset ID set to the &lt;code&gt;defaultDatasetId&lt;/code&gt; field returned by the first node.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;With the HTTP Request node.&lt;/strong&gt; POST to the run-sync URL from the curl example, with a Header Auth credential (name &lt;code&gt;Authorization&lt;/code&gt;, value &lt;code&gt;Bearer &amp;lt;your token&amp;gt;&lt;/code&gt;) and the input as the JSON body. n8n turns the returned array into one item per image, so the next node runs once per upscaled file.&lt;/p&gt;

&lt;p&gt;If your URLs arrive as separate n8n items, put an Aggregate node in front; otherwise n8n sends one request per item instead of one list. If you set the node's Timeout option, give it at least 300,000 ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it from an AI agent (MCP)
&lt;/h2&gt;

&lt;p&gt;Apify's MCP server can expose a single actor as a tool. Add it to your MCP client with a &lt;code&gt;tools&lt;/code&gt; filter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"image-upscaler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.apify.com?tools=sherwood/image-upscaler-batch"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On first connection, your browser opens so you can sign in to Apify. To skip the sign-in, send your token in an &lt;code&gt;Authorization: Bearer&lt;/code&gt; header instead. With &lt;code&gt;tools=&lt;/code&gt; set, the agent only gets this actor, as a tool named &lt;code&gt;sherwood--image-upscaler-batch&lt;/code&gt; with the input schema above, plus helpers the server adds on its own (&lt;code&gt;get-actor-run&lt;/code&gt;, &lt;code&gt;get-dataset-items&lt;/code&gt;, &lt;code&gt;get-key-value-store-record&lt;/code&gt;, &lt;code&gt;abort-actor-run&lt;/code&gt;). Calling the actor tool returns the run metadata and a dataset ID; the agent then calls &lt;code&gt;get-dataset-items&lt;/code&gt; to read the URLs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing at the time of writing
&lt;/h2&gt;

&lt;p&gt;From the actor's README:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image upscaled&lt;/strong&gt;: $0.012 per image delivered, output up to 4 MP included.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extra megapixel&lt;/strong&gt;: $0.002 per started megapixel above 4 MP (a 4x upscale of a 1000×1000 photo is 16 MP, so 12 extra).&lt;/li&gt;
&lt;li&gt;Failed, unreachable or non-image URLs: free.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Worked through: a 2x upscale of a 1000×1000 photo gives 2000×2000, which is 4 MP and costs $0.012. The 4x version costs $0.012 + 12 × $0.002 = $0.036. The example item above, 2048×2048 at 4.19 MP, starts a fifth megapixel, so it costs $0.014. &lt;code&gt;maxOutputMegapixels&lt;/code&gt; bounds the extra-megapixel part, and a maximum cost per run bounds the total.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Inputs must be reachable over public http(s) URLs, up to 25 MB each. Local files need to go somewhere reachable first, for example a pre-signed URL from your bucket.&lt;/li&gt;
&lt;li&gt;Output files live in the run's key-value store and follow your account's data retention, so copy the ones you want to keep.&lt;/li&gt;
&lt;li&gt;Processing is typically 5 to 25 seconds per image, with 4 images in parallel.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxImages&lt;/code&gt; defaults to 100, and the input schema allows up to 1,000 per run.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When I wouldn't use it
&lt;/h2&gt;

&lt;p&gt;At high, steady volume, a self-hosted GPU can be cheaper per image, and it's the only option when images can't leave your network. If you already have an account with a model provider and the per-image glue written, the actor adds little: the &lt;code&gt;model&lt;/code&gt; field shows the SeedVR2 endpoint it calls. For ten images and a laptop with a decent GPU, Upscayl does the job.&lt;/p&gt;

&lt;p&gt;The actor is for the case in between: lists of URLs inside a pipeline or an agent, where you want a result or an error per image without running anything yourself. Actor page: &lt;a href="https://apify.com/sherwood/image-upscaler-batch" rel="noopener noreferrer"&gt;apify.com/sherwood/image-upscaler-batch&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>ai</category>
      <category>python</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
