Stability AI 18,251 's Developer Platform bills in credits, where "1 credit = $0.01," with 25 free credits to start. Stable Image Ultra, built on Stable Diffusion 3.5 18,251 Large, costs 8 credits ($0.08); SD 3.5 Large 6.5, Large Turbo 4, Medium 3.5 and Flash 2.5; Stable Image Core, tuned for speed and cost, 3 ($0.03); and legacy SDXL 1.0 1,113 from 0.9. The same credits buy task endpoints that OpenAI 86 leaves to prompts: inpaint, erase, background removal, search-and-replace (5 credits each), sketch-guided generation (5), a 4K upscaler (60) and image-to-3D (Meshy and Other 3D Generators).
What sets Stability apart is that it also publishes open weights: you can prototype on the API and move a model to your own hardware (Running Open Models Locally), subject to its license.
<!doctype html>
<style>
body { margin: 0; padding: 8px; background: #fafaf7; font: 11px system-ui, sans-serif; color: #263238; }
svg { width: 100%; max-width: 600px; display: block; }
</style>
<script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js"></script>
<p><label><input type="radio" name="v" value="models" checked> generation models</label> <label><input type="radio" name="v" value="tasks"> task endpoints</label>
· with the 25 free credits you get <b id="free"></b></p>
<svg viewBox="0 0 600 250" font-size="11"></svg>
<script>
const DATA = {
models: [['Stable Image Ultra (SD 3.5 Large)', 8], ['SD 3.5 Large', 6.5], ['SD 3.5 Large Turbo', 4], ['SD 3.5 Medium', 3.5],
['Stable Image Core', 3], ['SD 3.5 Flash', 2.5], ['SDXL 1.0 (legacy)', 0.9]],
tasks: [['4K upscaler', 60], ['inpaint', 5], ['erase', 5], ['remove background', 5], ['search-and-replace', 5], ['sketch-guided', 5]],
};
const svg = d3.select('svg'), gx = svg.append('g').attr('transform', 'translate(0,230)');
function draw(view) {
const rows = DATA[view];
const y = d3.scaleBand(rows.map(r => r[0]), [10, 230]).padding(0.25);
const x = d3.scaleLinear([0, d3.max(rows, r => r[1])], [200, 530]);
gx.transition().call(d3.axisBottom(x).ticks(5));
const row = svg.selectAll('g.r').data(rows, r => r[0]).join(enter => {
const g = enter.append('g').attr('class', 'r');
g.append('rect').attr('x', 200).attr('fill', view === 'models' ? '#5b3f99' : '#e09a10');
g.append('text').attr('class', 'n').attr('x', 194).attr('text-anchor', 'end').attr('dy', '0.35em');
g.append('text').attr('class', 'v').attr('dy', '0.35em');
return g;
}).attr('transform', r => `translate(0,${y(r[0])})`);
row.select('rect').attr('height', y.bandwidth()).attr('width', 0).transition().attr('width', r => x(r[1]) - 200);
row.select('.n').attr('y', y.bandwidth() / 2).text(r => r[0]);
row.select('.v').attr('y', y.bandwidth() / 2).attr('x', r => x(r[1]) + 5).text(r => `${r[1]} credits = $${(r[1] / 100).toFixed(3)}`);
document.getElementById('free').textContent = view === 'models'
? `${Math.floor(25 / 3)} Core or ${Math.floor(25 / 8)} Ultra images` : `${Math.floor(25 / 5)} inpaint jobs`;
}
document.querySelectorAll('input[name=v]').forEach(r => r.onchange = () => draw(r.value));
draw('models');
</script>