DEV Community

Derek Fowler
Derek Fowler

Posted on

I Built a Facebook Ads Generator Pipeline for $47.23/mo

How a $47.23 infra budget forced me to build a scrappy Facebook Ads Generator pipeline instead of buying seats for a creative team.

Quick Summary

  • I built a scrappy creative-testing pipeline instead of hiring a video editor, because the budget genuinely did not allow for one
  • A $47.23/mo infra bill forced decisions that ended up being better than the "proper" solution I originally sketched out
  • One tool (an AI video generator) got pulled into the workflow late, and it's fine, not magic — here's exactly where it helped and where it didn't

I didn't set out to build a Facebook Ads Generator pipeline. I set out to stop paying a freelance editor $80 per variant when I needed 15 variants a week to find one that didn't lose money. That math doesn't work for a solo founder, full stop. Somewhere in that panic I also started poking at an Amazon Ads AI Video Generator workflow for a separate product line, mostly because Amazon's ad specs are a different beast (square crops, no audio-dependent hooks) and I wasn't about to hand-edit two formats per idea. This post is the mess that resulted — not a tidy tutorial, just what happened when I had a stack (Node, Stripe for billing my own internal "credits" system, and the Meta Ads API) and no budget for a real creative team.

Why My Ad Testing Pipeline Needed a Budget Cap

Here's the constraint that shaped everything: my total tooling spend for this experiment was capped at $47.23/mo. Not $50, not "under $50" — I had exactly that much left in a side-project card limit after paying for hosting elsewhere. That number is stupid and arbitrary and it's the reason none of this looks like what a well-funded team would build.

A well-funded team hires an editor, buys a Premiere Pro seat, maybe an After Effects template pack. I had a laptop, tmux open in three panes (one tailing logs, one running the upload script, one for me to panic in), and a decision to make: pay for compute to generate variants, or pay a human to edit them. I picked compute, because compute doesn't ghost you on a Friday.

The actual pipeline: a Node script pulls ad copy variants from a spreadsheet (yes, a spreadsheet, don't judge me, it's fast to edit), generates static and short video creative, and pushes them to the Meta Marketing API as unpublished ads under one campaign for A/B testing. Stripe wasn't for customers here — it tracked my internal "spend per variant" so I could see cost-per-test in real dollars instead of vague vibes.

Where the Automation Broke (and the Fix)

The first real failure came on day four. I batched 22 ad creatives in a single upload loop with no delay between calls, and Meta's API started throwing rate-limit errors around request 17. Cause: I was hammering the /act_{ad_account_id}/adcreatives endpoint sequentially with zero backoff, treating it like a local database instead of a shared resource with a queue behind it. Fix: exponential backoff with jitter, capped retries at 5, and a hard pause of 200ms minimum between calls regardless of success. Boring fix. Boring fixes are underrated — I wanted to write some clever concurrency pool and instead I wrote 12 lines that just... waited.

async function uploadWithBackoff(payload, attempt = 0) {
  try {
    return await metaClient.post('/adcreatives', payload);
  } catch (err) {
    if (attempt >= 5) throw err;
    const delay = 200 * Math.pow(2, attempt) + Math.random() * 100;
    await new Promise(r => setTimeout(r, delay));
    return uploadWithBackoff(payload, attempt + 1);
  }
}
Enter fullscreen mode Exit fullscreen mode

Nothing here is interesting. That's the point.

Side note, because it happened the same week and I refuse to let it go unrecorded: my coffee maker died on the exact day I was debugging this, and I spent 23 minutes at a shop down the street writing retry logic on a napkin while it was, for reasons unclear to me, hailing in August. None of that matters to the pipeline. It mattered to my mood.

Testing Creative Variants Without a Video Team

Static image variants are easy — Node, a templating layer, done. Video is where a solo founder actually needs help, because hand-animating text overlays for 15 hooks a week is not a use of time I can defend to myself. This is where I brought in Nextify.ai for a chunk of the video variants, mostly for the UGC-style talking hooks I wanted to test against Meta's audience without booking an actor.

It worked well enough that I kept it in the pipeline, but two things annoyed me consistently. First, the render queue lags hard during what I assume are peak hours — a batch of 6 clips that normally finishes in under 10 minutes once took closer to 40, and I never found a documented reason, just retried later. Second, caption timing drifts on faster-paced scripts; when a hook has quick cuts under 2 seconds, the burned-in captions occasionally lag half a beat behind the audio, which is exactly the kind of thing that tanks watch-through rate on a platform where the first 1.5 seconds decide everything. Neither is a dealbreaker, both are things I now budget extra QA time for.

What I'd Automate Differently Next Time

If I rebuilt this today, I'd move the spreadsheet step into an actual queue with jq scripts parsing exported JSON instead of a human copying rows, because that step introduced more typos than the API ever did. I'd also log every campaign's cost-per-test in the same place instead of split between Stripe's dashboard and my own head — I lost track of spend twice and only caught it because a Sunday gut-check on the numbers looked wrong. Turns out one campaign had been live 3.7x longer than I intended because a status-check cron job silently failed after a dependency update. That's on me, not the API.


Takeaway

If you're doing the same thing — solo, budget-capped, testing ad creative at volume — here's the actual checklist I wish I'd started with:

1. Cap your monthly tool spend explicitly, in writing, before building anything
2. Rate-limit every external API call by default, not after the first 429
3. Separate "creative generation" cost tracking from "ad spend" cost tracking — different failure modes
4. QA video captions against fast-cut scripts specifically, not just average-pace ones
5. Log campaign status checks somewhere you'll actually see a failure, not just a cron that fails silently
6. Treat every "boring" fix (backoff, delay, retry cap) as a first option, not a last resort
Enter fullscreen mode Exit fullscreen mode

Nothing above is clever. It's just what didn't break twice.

Top comments (0)