DEV Community

Cover image for Building a Pay-Per-Use SaaS: Lessons from a Caption Tool
YEB
YEB

Posted on

Building a Pay-Per-Use SaaS: Lessons from a Caption Tool

The original frustration was simple: I was paying €10/month for a caption generator and using it three times. That is over €3 per video for a tool that does a single, stateless job — take a video file, produce a subtitle file, done.

So I built a pay-per-use alternative. What I did not expect was how much the billing model would reshape every technical decision from the ground up.

The Architecture Difference

A subscription SaaS and a pay-per-use SaaS look similar on the surface — users, auth, a product feature — but the payment and job model diverges significantly.

Subscription model:

  • User pays monthly → gets a "member" flag → unlimited (or quota-limited) access
  • Auth middleware checks membership status
  • Usage tracking is optional (for analytics, not billing)

Pay-per-use model:

  • User has a credit balance (or card on file)
  • Every job deducts a defined cost before processing starts
  • If balance is insufficient, the job is rejected before any compute runs
  • The credit deduction is atomic with the job record creation

That last point matters more than it sounds. If a user submits a job and payment succeeds but the job fails, you need a reliable refund path. If payment fails but the job runs anyway, you have a revenue leak. The deduction and job creation need to be in the same database transaction, or you need a compensating transaction pattern.

Job State Machine

Caption generation (or any media processing job) is inherently async. The user uploads, you enqueue, you process, you deliver. The job moves through states:

pending → queued → processing → completed
                              → failed
                              → refunded
Enter fullscreen mode Exit fullscreen mode

The refunded state is specific to pay-per-use. If the job hits failed, a background process evaluates whether the failure was on the user's side (bad file format, invalid input — no refund) or on the system's side (processing error, timeout — full refund).

This introduces a failure_reason enum on the job model, not just a boolean failed flag.

Credit System Design

A few options for implementing credits:

Option A — Integer credits (simplest)
Store credits as an integer (e.g., 100 credits = €10). Each job costs N credits. Top up in packs. This is easy to display, easy to deduct, and easy to audit.

Option B — Real currency balance
Store balance in cents (integer). Each job has a price in cents. Top up via Stripe with exact amount. More transparent to users, slightly more complex for pricing tiers.

Option C — Prepaid tokens
User buys a pack of "jobs" (e.g., 10 video captions for €5). Tokens decrement per use. Simple, but inflexible if you want variable pricing based on video length or quality settings.

I went with Option A. It is the most flexible for adding new tools with different costs without changing the billing infrastructure.

The Checkout Flow Problem

Subscription tools can afford friction in signup. The user is making a commitment — they will tolerate a few extra steps to set up billing.

Pay-per-use needs a faster on-ramp. If a user lands on the tool, uploads a file, and then hits a "please create an account and add payment info" wall, conversion dies there.

The flow I landed on:

  1. User uploads file (no auth required at this step — preview/analysis only)
  2. Show result preview + credit cost
  3. One-click purchase option (guest checkout with email) OR login if they have credits already
  4. Job runs immediately after payment confirms

Guest checkout with saved email means return users can top up without a full account if they choose. It reduces the commitment required for a first transaction.

What Subscription Billing Hides

Building pay-per-use forced me to confront costs I could ignore with a subscription model:

  • Processing time — a 30-minute video costs more to transcribe than a 2-minute one. Flat pricing hides this.
  • Failure rate — with a subscription, failed jobs are annoying but not a billing event. With pay-per-use, every failure is a potential support ticket about money.
  • Storage — output files need to be available for download for a reasonable window. With pay-per-use, storing files indefinitely makes no sense. I auto-delete outputs after 48 hours and tell users this upfront.

These are not complications — they are clarifications. The model forces you to understand your own cost structure.


Read the full backstory that started this build: https://yeb.to/captions-ai-charged-me-10-euro-a-month-for-3-videos-so-i-built-my-own-tool

Try the tool: https://captions.yeb.to

Top comments (0)