Generate & Edit Product Photos

Generate and edit product photos with a script that calls an AI image model.

Introduction

30 Second Summary

Getting a product photo usually means a camera, a table, and a lot of fiddling with lights. Writing one sentence and getting a usable picture back is a very different way to work.

In this project, you will send written prompts to an image model over HTTP and save every picture it sends back. You will also hand the model one of those pictures and ask it to change a single thing.

What You'll Build

You will open a folder of product images your own script created, flick between a before and after pair you edited on purpose, and point at exactly what the whole run cost.

By the end of this project, you'll have:

  • A script you run from your laptop that loops over five prompts and drops five finished images into an output folder, each file named after the prompt that made it.
  • A before and after pair where you fed an image back in with one instruction, so you can hold them side by side and see the single thing that changed.
  • A real cost figure from your OpenRouter activity page, checked against the $0.01 per image list price on the model page.
  • Secret Mission: Chain your script so every generated image is fed straight back in as a reference with an edit instruction, giving you a before and after pair for all five prompts.

Are there any prerequisites?

You need a laptop and an OpenRouter account with a card on file, because loading a small credit balance is what lets a paid model answer you.

At $0.01 per image, a twenty image session lands around twenty cents.

Before We Start

Every image in this project comes back from a model running on someone else's hardware. Your laptop never renders a single pixel of it.

That means the model needs to know who is asking and whose balance to charge. OpenRouter answers both questions with an API key and a small pre-paid credit balance.

In this step, get ready to:
  • Create an OpenRouter account and add a small credit balance.
  • Create an API key and store it somewhere safe.
  • Check that curl is ready in your terminal.
Sign up for OpenRouter and add credits

OpenRouter is a single HTTP endpoint sitting in front of hundreds of AI models. One account and one key get you to all of them.

  • Open the OpenRouter pricing page in your browser.
  • Click Get Started For Free.
  • Complete the sign-up flow, then sign in to your new account.

You land on the OpenRouter dashboard, signed in.

Why you need credits before the model answers

OpenRouter is pay as you go. Paid models stay silent until your account holds pre-paid credits, so a card has to go on file before your first request.

The minimum purchase is $5 per transaction. The muse-image model lists $0.01 per image, so $5 covers hundreds of runs of this project.

A platform fee is added on top at checkout. Expect the card charge to come out slightly above the credit amount you bought.

  • Open the Credits page from your account menu.
  • Start a credit purchase for the $5 minimum.
  • Enter your card details and confirm the purchase.

Your balance on the Credits page now reads $5.00 instead of nothing.

Purchase not going through?

  • Check that the amount you entered is at least $5. Anything smaller is rejected as below the minimum.
  • Some banks route card verification through a popup. Allow popups for the OpenRouter site and try again.
  • Prepaid and virtual cards are often declined by payment processors. Try a standard debit or credit card.

My OpenRouter credit purchase is failing, what should I check?

Create your API key

Your account has money in it now. An API key is what proves a request came from you and tells OpenRouter which balance to charge.

  • Open your account menu in the top right of the OpenRouter dashboard.
  • Go to your API keys settings.
  • Start creating a new key.
  • Name the key image-workshop.
  • Leave the credit limit blank so the key can spend from your full balance.
  • Confirm, then copy the key that appears.

You only see this key once

OpenRouter shows the full key at the moment you create it and never again. There is no way to read it back later.

Paste it into a password manager or a private note now. Never commit it to a repository, because OpenRouter scans public code for exposed keys and will email you when one turns up.

  • Close the key dialog.

Your keys settings page now lists image-workshop with the key value masked.

Closed the dialog before copying the key?

The key is gone for good, and that is by design. Delete the image-workshop entry from your keys settings page and create a fresh one.

Why can't I see my API key again after creating it?

Check that curl is ready in your terminal

Every request in this project leaves your machine as a plain HTTP call. curl is the tool that sends it.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or PowerShell (Windows), then press Enter to open it.

Before you run the next command, take a guess. Do you think curl is already sitting on your machine, or will you have to install it?

  • Find out by running this command:
curl --version

✔️ I see a version number

That first line names the curl version and the protocols it can speak. Nothing to install, your terminal is ready to talk to OpenRouter.

ⓧ Command not found

curl ships with most systems, but not all of them. Grab an official build and you are back on track.

  • Open the curl download page in your browser.
  • Download the package listed for your operating system.
  • Follow the installer's instructions.
  • Close your terminal and open it again so it picks up the new command.
  • Confirm the install worked by running this command:
curl --version

You should now see a version number instead of an error.

Still seeing command not found?

The installer may have put curl somewhere your terminal does not look. Reopening the terminal fixes this most of the time, and a full restart of your machine fixes the rest.

My terminal still can't find curl after installing it, how do I fix my PATH?

Your account is funded, your key is in hand, and your terminal can reach the internet. Next up, you send your first written prompt and open the picture that comes back.

Generate Your First Image

Your OpenRouter account is funded and your API key is sitting on the dashboard. So far that key has not moved a single byte.

The muse-image model lives behind a plain HTTP endpoint. Anything that can send a POST request can reach it, which makes the terminal you already have open a working image studio.

In this step you send one written prompt, catch what comes back, and open a picture that did not exist a minute ago.

In this step, get ready to:
  • Store your OpenRouter API key in your terminal session.
  • Send a text-to-image request to meta/muse-image with curl.
  • Decode the reply into an output.png file and open it.
Set up a folder for your generated images

Every picture you make in this project lands on disk as a file. Give them a home before the first one arrives.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your workshop folder and move into it by running these commands:
mkdir muse-workshop
cd muse-workshop
  • Confirm you are standing inside the new folder by running:
pwd

You should see a path that ends in muse-workshop. Every file in this step lands here.

Send your first text-to-image request

OpenRouter serves muse-image from a single Image API endpoint. Your key is the part of the request that says who to bill.

  • Store your key in this terminal session by running this command, with your-api-key-here swapped for the key from your dashboard:
export OPENROUTER_API_KEY=your-api-key-here

Why hold the key in a variable?

Your key now has one home in this terminal window. Every request from here on refers to it by name, so the key itself never gets pasted into a command you might share.

It also disappears the moment you close the window. Open a fresh terminal later and you will need to run this line again.

  • Send your first prompt to the model, saving the reply into response.json, by running this command:
curl -X POST https://openrouter.ai/api/v1/images \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -o response.json \
  -d '{
    "model": "meta/muse-image",
    "prompt": "A ceramic coffee mug on a pale linen tablecloth, soft morning light from the left, shallow depth of field"
  }'

What does this command do?

  • https://openrouter.ai/api/v1/images is the Image API endpoint. Every image request in this project goes to this one address.
  • Authorization: Bearer $OPENROUTER_API_KEY proves the request is yours and picks the balance to charge.
  • "model": "meta/muse-image" names the model. Swapping that one string is how you would try a different image model later.
  • "prompt" is the description the model renders. Specific wording about light, material, and framing pulls the result closer to what you pictured.
  • Check that a reply actually came back by running:
ls -lh response.json

You should see response.json listed at a size of at least a few hundred kilobytes. That bulk is your picture, currently stored as text.

File is tiny or full of error text?

  • A size under 1 KB means the API sent back an error instead of an image. Open response.json in your editor and read the message inside.
  • If the message mentions authentication, your key never made it into the request. Run the export command again in this same terminal window.
  • If the message mentions credits or billing, top up your balance on your OpenRouter dashboard and send the request again.

Why is my OpenRouter image request returning an error instead of an image?

Decode the response into a picture you can open

Your image came back as a long run of text inside JSON. Turning that text back into pixels takes one command.

  • Check whether you already have jq, the tool that reads values out of JSON files, by running this command:
jq --version

✔️ I see a version number

jq is ready to go. Carry on to the decode command below.

ⓧ Command not found

jq is not installed yet. Pick the command for your machine.

  • On macOS, install jq with Homebrew by running this command:
brew install jq
  • On Windows, install jq with winget by running this command instead:
winget install jqlang.jq
  • Confirm the install landed by running:
jq --version

You should now see a version number instead of an error.

  • Pull the image data out of the reply and write it to output.png by running the command for your machine:

macOS

jq -r '.data[0].b64_json' response.json | base64 --decode > output.png

Windows

$b64 = jq -r '.data[0].b64_json' response.json
[IO.File]::WriteAllBytes("$PWD\output.png", [Convert]::FromBase64String($b64))

Why did your image arrive as text?

JSON can only carry text, so the API encodes the raw image bytes into a long string of safe characters and hands you that. The b64_json field is where that string lives.

The command above does two jobs in a row. jq lifts the string out of the file, and the decoder turns it back into the exact bytes a PNG needs.

Before you open the file, make a call. How closely do you think the picture will match the light, the material, and the framing you described?

  • Open your new image on macOS by running this command:
open output.png
  • On Windows, open it by running this command instead:
start output.png

Your image viewer opens on a ceramic mug lit from the left, with the tablecloth behind it softly out of focus. Nobody photographed that mug.

Image will not open?

  • If your viewer says the file is damaged, check the size of output.png. A file of 0 bytes means jq found nothing to decode.
  • An empty result usually means response.json holds an error rather than image data. Open it and read the message, then send the request again.
  • Make sure you ran the decode command from inside your muse-workshop folder, where the reply was saved.

Why is my decoded PNG empty or unreadable?

One sentence, one picture, about a cent. Next up, you hand the model a reference image alongside a single instruction and watch it change only the part you asked about.

Edit an Image with a Reference

Last step you turned a sentence into a picture that did not exist before. This step keeps that picture and changes one thing about it.

Send a fresh prompt on its own and the model starts from scratch every time. Editing means handing the model the image you already have, then naming the single thing you want changed.

In this step, get ready to:
  • Encode your generated image so it can travel inside a request body.
  • Send one edit instruction with that image attached as a reference.
  • Put your original and your edit side by side and see what moved.
Encode your image so it can travel in JSON

A request body is JSON, which is plain text. Your PNG is raw bytes, so it needs a text form before it can ride along inside the request.

  • Return to the terminal window you used to send your first request.
  • Confirm you are in the folder that holds your first image by running this command:
ls output.png
  • Check that the terminal prints output.png back at you.
  • Turn that image into one long line of text by running this command:
REF_IMAGE=$(base64 < output.png | tr -d '\n')

What does this command do?

  • base64 < output.png reads your image as raw bytes and rewrites it using only letters, numbers and a couple of symbols.
  • tr -d '\n' strips out the line breaks so you end up with one unbroken string.
  • REF_IMAGE= parks that string in a shell variable you can drop into the request.
  • Check how much text your image turned into by running this command:
echo ${#REF_IMAGE}

You should see a number in the hundreds of thousands. That is your picture, written out as text.

Seeing 0 come back?

A zero means output.png was not in the folder you are sitting in. Run ls to see which files are actually there.

My terminal cannot find the image I saved earlier

Write the request that carries your reference image

Your first request only had a model and a prompt. OpenRouter's image API has a second field for handing over pictures you already have.

  • Write the request body into a file by running this command:
cat > payload.json <<EOF
{
  "model": "meta/muse-image",
  "prompt": "Change the background to a rainy city street at night.",
  "input_references": [
    { "type": "image_url", "image_url": { "url": "data:image/png;base64,$REF_IMAGE" } }
  ]
}
EOF

What's in this request?

  • model points at meta/muse-image, the same model that made your first picture.
  • prompt carries exactly one instruction, so there is no doubt about which change the model acted on.
  • input_references is the field that hands over your existing picture, wrapped as a data:image/png;base64, URL.
  • This second call costs about $0.01 per image, the same as your first one.
  • Read the start of the file you just wrote by running this command:
head -c 240 payload.json

You should see your model, your prompt, and the first slice of a very long data:image/png;base64, string.

Does the URL stop right after base64?

That means REF_IMAGE was empty when the file was written. Opening a new terminal window clears the variable, so it is blank in the window you are typing in now.

Run the encode command again in this same window, then write payload.json once more.

Send it and compare the two images

Before you send this, make a call. Will the model keep the subject you generated and swap only the background, or start over from scratch?

  • Swap your-api-key-here for the API key you used in your first request.
  • Send the edit request by running this command:
curl -s -X POST "https://openrouter.ai/api/v1/images" \
  -H "Authorization: Bearer your-api-key-here" \
  -H "Content-Type: application/json" \
  -d @payload.json > edited.json
  • Turn the response back into a picture by running this command:
sed 's/.*"b64_json":"//; s/".*//' edited.json | base64 -d > output_edited.png

What does this command do?

  • sed pulls the value of b64_json out of the response and throws the rest of the JSON away.
  • base64 -d turns that text back into raw image bytes.
  • > output_edited.png writes those bytes into a new file, so your original stays untouched.
  • Open the folder holding your images in Finder (macOS) or File Explorer (Windows).
  • Double-click output.png to open the original.
  • Double-click output_edited.png to open the edit.
  • Drag the two windows next to each other so both are visible at once.

The subject you generated is still recognisably the same. Behind it, the scene you named in your prompt has replaced whatever was there before.

Not seeing the edit you asked for?

A brand new picture, with none of your original in it, means the reference never reached the model. Check that input_references is spelled exactly that way in payload.json.

If output_edited.png will not open at all, read what actually came back by running:

cat edited.json

Help me read this OpenRouter image response

Change one thing at a time

The picture that comes back is the only report you get on what the model did. Ask for one change and you know exactly which instruction produced the difference.

You now have a before and an after that you steered on purpose. Next up, you'll move this call into a short script that runs five prompts in a row.

Batch Generate Five Images

You have two images on disk now. One you generated from scratch, and one you edited from a reference by putting them side by side to see what changed.

Both of those took a hand typed command. Retyping that for every new idea gets old fast, and it leaves you with files whose names tell you nothing about the prompt that made them.

In this step you move the call into a script called generate_images.sh, hand it a list of five prompts, and let it fill an output folder while you watch.

In this step, get ready to:
  • Turn your one-off curl call into a script that loops over five prompts.
  • Save each generated image into an output folder, named after the prompt that made it.
  • Run the script and confirm five image files landed on disk.
Set up your output folder and API key

Your script needs a folder to write into and a way to reach your API key without the key ever being typed into a file.

  • Return to the terminal you used earlier, in the same folder where you saved output.png.
  • Create the output folder and an empty script file by running these commands:
mkdir output
touch generate_images.sh
ls

What do these commands do?

  • mkdir output creates the folder every generated image will land in.
  • touch generate_images.sh creates an empty file for the script you are about to write.
  • ls lists the folder so you can see both of them appear.
  • Look for output, generate_images.sh, output.png and output_edited.png in the list.

Don't see your two images in the list?

  • Your terminal is in a different folder from the one you saved into earlier. Move to that folder first, then run the two commands again.
  • If mkdir says the folder already exists, you are fine. The folder is there and you can carry on.
  • How do I find which folder my terminal is currently in?
  • Make your API key available to the script by running this command, replacing the placeholder with the key you created earlier:
export OPENROUTER_API_KEY=your-api-key-here

Why the key stays out of the script

The script reads the key from your shell instead of holding a copy. That means you can share generate_images.sh with anyone without handing over your credentials.

The trade off is that this only lasts for the current terminal session. Open a new terminal window and you run the export again.

  • Confirm the key is set without printing it by running this command:
[ -n "$OPENROUTER_API_KEY" ] && echo "Key is set"
  • Look for Key is set in the output.

Nothing printed?

  • The export did not take. Run it again in this same terminal window, and check you replaced your-api-key-here with your actual key.
  • If your key has spaces or unusual characters, wrap it in double quotes after the equals sign.
  • Why isn't my exported environment variable showing up?
Write the loop that runs through five prompts

A loop turns one call into five. Start with just the prompt list and the loop, so you can watch it walk through all five before any money moves.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • Click File in the top menu bar.
  • Click Open Folder.
  • Select the folder where you saved output.png, then click Open.
  • Click generate_images.sh in the Explorer sidebar to open the empty file.

Using a different editor

Any plain text editor works here. Open generate_images.sh from the folder where you saved output.png and the rest of this step is identical.

  • Copy this code into the empty file:
#!/bin/bash

# The five prompts this run works through, one image each
prompts=(
  "a ceramic coffee mug on a wooden table"
  "a pair of running shoes on a white studio backdrop"
  "a leather backpack leaning against a brick wall"
  "a steel water bottle on a mossy rock"
  "a wool scarf folded on a linen sheet"
)

# Handle the prompts one at a time
for prompt in "${prompts[@]}"; do
  echo "Generating: $prompt"
done

What does this code do?

  • prompts=( ... ) holds your five prompts as a list. Change any of them later and the script picks up the new wording on the next run.
  • for prompt in "${prompts[@]}" walks the list one entry at a time, putting the current entry in prompt.
  • echo prints what the script is working on, so a long run never looks frozen.
  • Save generate_images.sh.
  • Return to your terminal and try the loop by running this command:
bash generate_images.sh
  • Look for five Generating: lines, one per prompt.

Seeing a syntax error instead of five lines?

  • Check that every prompt sits on its own line inside the brackets, and that the closing ) is on its own line.
  • Check that the last line of the file is done, which closes the loop.
  • Why is my bash for loop throwing a syntax error?
  • In generate_images.sh, find this line:
  echo "Generating: $prompt"
  • Copy the following code directly below it:
  # Turn the prompt into a filename by swapping spaces for underscores
  filename=$(echo "$prompt" | tr ' ' '_')

  # Build the JSON body that carries this prompt to the model
  body="{\"model\": \"meta/muse-image\", \"modalities\": [\"image\", \"text\"], \"messages\": [{\"role\": \"user\", \"content\": \"$prompt\"}]}"

  # Send the request and keep the raw response
  curl -s https://openrouter.ai/api/v1/chat/completions \
    -H "Authorization: Bearer $OPENROUTER_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$body" > "output/$filename.json"

What does this code do?

  • filename=$(echo "$prompt" | tr ' ' '_') swaps the spaces in the prompt for underscores, so the prompt itself becomes the file name.
  • body is the JSON the model receives. modalities is what asks for a picture back rather than just words.
  • The curl call is the same one you ran by hand earlier, with the response saved into your output folder instead of printed to the screen.
  • Five prompts means five requests. The model page lists $0.01 per image, so this run costs about five cents.
  • Save generate_images.sh.
  • Return to your terminal and run the script again by running this command:
bash generate_images.sh

Each Generating: line now pauses for a few seconds while OpenRouter works.

  • See what landed by running this command:
ls output
  • Look for five .json files, each named after one of your prompts.

Files are there but tiny?

  • Open one of the .json files in your editor. A response carrying an error field means the request was rejected rather than the file being written wrong.
  • A rejected request usually means the key is not set in this terminal window, or your credit balance ran out.
  • Help me read this OpenRouter error response.
Save each response as an image file

Your folder holds raw responses right now, not pictures. The image is sitting inside each response as a long block of base64 text.

  • In generate_images.sh, find this line:
    -d "$body" > "output/$filename.json"
  • Copy the following code directly below it:

  # Pull the base64 image out of the response and save it as a PNG
  python3 -c "
import sys, json, base64
data = json.load(open(sys.argv[1]))
url = data['choices'][0]['message']['images'][0]['image_url']['url']
open(sys.argv[2], 'wb').write(base64.b64decode(url.split(',', 1)[1]))
" "output/$filename.json" "output/$filename.png"

What does this code do?

  • json.load reads the response file the previous chunk saved.
  • data['choices'][0]['message']['images'][0]['image_url']['url'] is where the model puts the picture it made. The value is a long data URL, not a link you can click.
  • base64.b64decode turns that text back into real image bytes, which then get written to a .png file beside the response.
  • Running the script again sends five fresh requests, so you will have paid for ten images by the end of this step.
  • Save generate_images.sh.

Before you run this, how many files do you expect to find in output once the script finishes? Remember the script now writes two things per prompt.

  • Run the finished script by running this command:
bash generate_images.sh
  • See what the run produced by running this command:
ls output

You should see ten files: five .json responses and five .png images, each pair named after the prompt that made it.

No PNG files appeared?

  • If the terminal reports a key error, the response did not contain an image. Open the matching .json file and check it holds a real response rather than an error field.
  • If the terminal reports that python3 was not found, install Python and run the script again.
  • Check the indentation of the pasted block matches the lines above it, so it sits inside the loop.
  • Why is my script failing to decode the image out of the response?
  • Open the output folder in Finder (macOS) or File Explorer (Windows).
  • Double click one of the .png files to open it.

The picture matches the prompt in its own filename. One command produced all five, and every file tells you exactly what it was asked for.

✔️ Awesome, I've got everything!

Great work. Double check you've saved generate_images.sh before you move on.

ⓧ I'd like to double check the full code

Scroll up and confirm your file contains these three blocks, in this order:

  • The shebang, the prompts list of five prompts, and the for loop with its echo line.
  • The filename line, the body line, and the curl call, all indented inside the loop.
  • The python3 block that decodes the image, also inside the loop, with done as the final line of the file.

Five images from one command, and every one of them cost you something. Next up, you'll open your activity page and find out exactly how much.

Check Your Actual Cost

Five prompts went in and five image files came out, all from a single script run. The building part is done.

What you still don't know is what that run cost you. Every request drew from the credit balance you topped up at the start, and so far you've taken the price on trust.

OpenRouter keeps a record of every request you send and what it charged for it. In this step you read that record and hold it up against the price the model page advertises.

In this step, get ready to:
  • Open your OpenRouter activity page and find the requests from your run.
  • Read the actual spend those image requests cost you.
  • Compare that spend against the $0.01 per image list price on the muse-image model page.
Find your run on the activity page

Your images arrived, so the money left your balance. The activity page is where that charge is written down.

Before you open it, what do you think five generated images cost you? Commit to a number, then go and check it.

  • Open a new browser tab.
  • Go to https://openrouter.ai/activity.
  • Set the time period filter to **1 Day**.
  • Group the results by Model.
  • Read the Spend figure on the muse-image row.

You should see a spend of a few cents against a request count that matches the calls you sent. Your prediction was probably in the right neighbourhood.

What the activity page tracks

The page shows three metrics: Spend, Tokens, and Requests. Spend is the money that actually left your credit balance.

The time period filter offers 1 Hour, 1 Day, 1 Month, and 1 Year. Grouping by Model splits that spend per model, which is what isolates your image requests.

Don't see your run listed?

Widen the time period filter. A 1 Hour window hides a run you made earlier in the day.

Check the grouping is set to Model. Grouped by API Key, your spend is filed under the key name instead of the model name.

Still stuck? Help me find my image requests on the OpenRouter activity page.

Compare your spend against the list price

A number on a dashboard only means something next to the number you were promised. The model page carries the advertised rate.

The model page lists $0.01 per image. Five images from your script run works out to $0.05 at that rate.

  • Count every image request you've sent today: five from the batch script, one from your first curl call, and one from the edit call.
  • Multiply that count by $0.01 to get the list price total.
  • Hold that total next to the Spend figure on your activity page.

Seven images at $0.01 each comes to $0.07, and your Spend figure should sit right around there. The advertised rate and your bill agree.

Why your total is higher than five images

The 1 Day filter covers everything you sent today, not just the batch run. Your first single generation and your reference image edit are both in there.

This is the habit worth keeping. Read the spend per model against a known unit price, and a runaway loop shows up as a number that no longer divides cleanly.

You've closed the loop: a prompt goes out, an image comes back, and you can point at exactly what it cost. There's also an optional Secret Mission waiting if you want to push this further.

Secret mission

Batch One Edit Across Your Whole Image Set

Your five images each came back with their own look. A catalogue needs them to share one. Turn the single reference-image edit you ran earlier into a loop that applies the same instruction to every file in your output folder, then compare the before and after pairs and see what the extra pass cost.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. Everything you built runs from your laptop and OpenRouter only charges when you send a request, so nothing is ticking over in the background.

Resources you used:

  • Your OpenRouter API key.
  • Your batch script, generate_images.sh.
  • Your output/ folder holding the five images from your batch run.
  • Your first generated image, output.png, and its edited version, output_edited.png.

Keep everything running

No action needed. Choose this if you're still actively building on your image workshop.

  • Leave your API key active so the script keeps working.
  • Leave any remaining credit on your account for your next run.
  • Keep generate_images.sh and your output/ folder as your portfolio piece.

What stays behind either way

Your OpenRouter account stays open, along with any credit you have not spent. Credit only draws down when you send a request, so an idle account costs nothing.

Pause - I'll come back to this later

Nothing runs in the background here, so pausing is about keeping your files tidy and your spending at zero until you return.

  • Keep generate_images.sh where it is so you can rerun the batch later.
  • Move output.png and output_edited.png into your output/ folder so the whole project sits in one place.
  • Leave your credit balance untouched, since it only draws down when you send a request.

Delete - I don't want to use this again

Remove all project resources and start fresh. Your key goes first, then the files on your laptop.

  • Sign in to OpenRouter.
  • Open the keys page on your dashboard, the same page where you created your key and saw it listed.
  • Delete the key you used in this project, so it can no longer spend your credit.
  • In your terminal, move into the folder that holds generate_images.sh by running this command:
cd [[PROJECT_FOLDER="path to your project folder"]]
  • Delete the images and the script on macOS or Linux by running these commands:
rm -rf output
rm output.png output_edited.png generate_images.sh
  • Delete the images and the script on Windows by running these commands:
rmdir /s /q output
del output.png output_edited.png generate_images.sh
  • Confirm the files are gone by running:
ls

You should no longer see output, output.png, output_edited.png, or generate_images.sh in the output.

What about the leftover credit?

Any credit you added stays on your account after the key is deleted. With no active key, nothing can spend it.

Nice Work!

Nice Work

Nice work! You've just built a working image workshop that generates pictures from text, edits them with a reference image, and runs the whole thing in a loop from your own laptop.

You've learned how to:

  • Call an image model over an HTTP API with your own OpenRouter key, and save the picture that comes back as a file you can open.
  • Turn generation into editing by attaching a reference image and one specific instruction, then proving the change by putting the before and after side by side.
  • Move a one-off request into a batch script that loops over five prompts and fills an output folder, then read the activity page to see what the run actually cost.
  • Secret Mission: Take on an optional challenge that stretches your image workshop further.

Ready to quiz yourself?