Cheapest text-to-image API for an MVP: sticker price vs what you get billed

Published per-image rates and real invoices disagree. How to measure your own cost per render in Node, which billing shapes exist, and when a specialist is genuinely cheaper.

For an MVP the useful question isn’t which text-to-image API advertises the lowest number. It’s which one tells you what a render actually cost, per call, while you’re still building — because published per-image rates and real charges diverge more often than anyone admits. Infrai returns infrai.cost_usd on every image response, which makes that gap measurable in about four minutes instead of at the end of the month.

We went looking for the gap on 2026-07-26 and found one immediately, on our own platform. Here’s the whole thing, including the part that doesn’t flatter us.

Read the catalogue, then don’t trust it for budgeting

export INFRAI_API_KEY="your_infrai_api_key"

curl -sS "https://api.infrai.cc/v1/ai/models?capability=image&available=true" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}"
{
  "object": "list",
  "capability": "image",
  "count": 5,
  "data": [
    { "id": "gpt-image-1.5", "owned_by": "azure_foundry", "unit": "per_token",
      "token_rates": { "input_usd_per_mtok": 8.0, "cached_input_usd_per_mtok": 2.0, "output_usd_per_mtok": 32.0 } },
    { "id": "gpt-image-2", "owned_by": "azure_foundry", "unit": "per_token",
      "token_rates": { "input_usd_per_mtok": 8.0, "cached_input_usd_per_mtok": 2.0, "output_usd_per_mtok": 30.0 } },
    { "id": "wan-t2i", "owned_by": "wanxiang", "price_usd": 0.02, "unit": "per_image" },
    { "id": "wanx-v1", "owned_by": "wanxiang" },
    { "id": "wanx2.1-t2i-turbo", "owned_by": "wanxiang", "price_usd": 0.014, "unit": "per_image" }
  ]
}

Three things jump out of that list, and only one of them is a price.

First, two different billing shapes live side by side. The wanxiang models bill per image, so a render is a fixed line item. The gpt-image-* models bill per token — 8 dollars per million in, 30 to 32 out — which means the cost of one picture depends on how long your prompt is and what the model emits, and you genuinely cannot know it before you call. For an MVP with a fixed burn rate that difference matters more than the digits.

Second, wanx-v1 carries no price at all. A model with no rate in the catalogue is not a model you should be routing production traffic to.

Third — and this is the honest bit — the listed $0.014 per image for wanx2.1-t2i-turbo was not what we were charged.

Measure the real rate

Point INFRAI_BASE_URL at https://api.infrai.cc and run this. It renders one image and prints what the platform says it cost:

const KEY = process.env.INFRAI_API_KEY;
const BASE = process.env.INFRAI_BASE_URL;
if (!KEY || !BASE) throw new Error("set INFRAI_API_KEY and INFRAI_BASE_URL");

const res = await fetch(`${BASE}/v1/images/generations`, {
  method: "POST",
  headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "wanx2.1-t2i-turbo",
    prompt: "a plain ceramic coffee mug on a white studio background, product photo",
    n: 1,
    size: "1024x1024",
  }),
});
if (!res.ok) throw new Error(`generation failed: ${res.status} ${await res.text()}`);

const json = await res.json();
console.log(json.data[0].url);
console.log(`billed $${json.infrai.cost_usd} on ${json.infrai.model} via ${json.infrai.vendor}`);
{
  "created": 1785042737,
  "data": [{ "url": "https://dashscope-5859.oss-cn-wulanchabu-acdr-1.aliyuncs.com/…" }],
  "infrai": {
    "cost_usd": 0.04,
    "vendor": "wanxiang",
    "region": "china",
    "model": "wanx2.1-t2i-turbo",
    "markup_pct": 0,
    "cache": false
  }
}

Four cents, not 1.4. Verified 2026-07-26, twice, at two different output sizes — the charge didn’t move with resolution either. The catalogue figure is a floor, the invoice is the truth, and the general lesson transfers to every image vendor you’ll evaluate: budget from a metered response, never from a marketing page. Image rates trend downward and vendors run promotions, so re-measure before you commit to a number in a board deck.

The URL that comes back is a signed, expiring link on the vendor’s own bucket. Download it and put it somewhere you control, or your product’s images will 403 in about a day.

What an MVP actually spends

Here’s the arithmetic that decides whether image generation is affordable for you, and it’s not the per-image rate.

AssumptionValueMonthly cost at $0.04/image
200 SKUs, one render each200 images$8
Regeneration rate of 3× (prompt tuning)600 images$24
50 users generating 10 images/day15,000 images$600
Same, but with a 2× cheaper vendor15,000 images$300

Look at the last two rows. Once real users are generating, halving the rate saves real money — but before that, the entire MVP costs less than a team lunch and optimising it is procrastination. The variable that dominates every line here is the regeneration rate, which is a prompt-quality problem, not a pricing problem.

Guard the budget rather than the rate. This checks the balance before a bulk render kicks off:

import os
import sys
import requests

key = os.environ["INFRAI_API_KEY"]
res = requests.get(
    "https://api.infrai.cc/v1/account/balance",
    headers={"Authorization": f"Bearer {key}"},
    timeout=20,
)
res.raise_for_status()
data = res.json()["data"]

planned_images = 600
estimated = planned_images * 0.04

print(f"balance ${data['balance_usd']:.2f}, runway {data['runway_days']} days")
if data["balance_usd"] < estimated:
    sys.exit(f"need ${estimated:.2f} for {planned_images} images; top up first")
print(f"proceeding: ${estimated:.2f} of headroom required")

And confirm afterwards, per capability, instead of reconstructing it from logs:

curl -sS "https://api.infrai.cc/v1/account/usage" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}"
{
  "ok": true,
  "data": {
    "period": "30d",
    "total_cost": 12.06568973,
    "total_calls": 19610,
    "breakdown": [
      { "key": "ai.chat", "cost": 7.91272499, "calls": 1244 },
      { "key": "ai.image", "cost": 0.9703, "calls": 24 },
      { "key": "storage.object.put", "cost": 0.459, "calls": 4590 }
    ]
  }
}

Divide cost by calls on the ai.image row and you have your true effective rate over the whole month, promotions and failures included. That’s the number to put in the model.

When you should buy elsewhere

Being blunt: if image generation is the entire product and you’re price-sensitive at volume, a specialist will beat a general platform on rate. fal serve FLUX.1 [schnell] at a per-megapixel rate that’s hard to match, Stability sell credits against a documented per-render table, and Ideogram is the one to reach for when the picture has to contain readable words. All three publish live pricing; check them rather than a comparison table, including this one.

OpenAI direct is the other honest answer — one vendor, one SDK, gpt-image quality, and the per-token billing shape you saw above. Azure OpenAI hosts the same family when you need a contracted region, which the routing here won’t give you: our renders came back "region": "china", and that’s a routing property rather than a residency guarantee.

Other limitations worth knowing before you pick this route. The async batch endpoint runs chat requests, so image rows submitted to POST /v1/ai/batch/submit come back failed — you fan out image calls yourself with a concurrency limit. There’s no negative prompt, seed or style-preset surface on the OpenAI-compatible path, so reproducibility is weaker than a specialist’s API. And wanx-v1 has no published rate at all.

What tips it back is the second question. An MVP that generates images also has to store them, retry the vendor timeout, email the customer a link and attribute the spend to a tenant — and on one key those are four more calls on a client you already wrote, not four more vendor accounts. For a two-person team shipping in six weeks, that’s usually worth more than two cents an image.

References

Browse more ai developer guides