Cron Scripts vs Managed Sync: The Hidden Cost of Rolling Your Own

Cron Scripts vs Managed Sync: The Hidden Cost of Rolling Your Own

A cron job and 50 lines of Node looks free. Here is what building and running your own billing data pipeline really costs, and when managed sync is cheaper.

Ilshaad Kheerdali·Sep 22, 2026·13 min read

Every developer who has needed billing data in their own database has had the same thought, usually within about ninety seconds of looking at a pricing page: I could write this myself in an afternoon.

You are right. You could. The afternoon version genuinely works, and that is the problem, because the afternoon is not the cost. The cost is the eighteen months afterwards, and it lands in places that never appear in the build estimate: somewhere to run the thing, someone to notice when it stops, and a slow drip of maintenance that never quite ends.

This post puts real numbers on that. Not to argue that you cannot build it, but so you can compare the two options honestly before you commit.

The Script That Works on Day One

Here is the version everyone writes. It is not a strawman, it is genuinely what a competent developer produces in an afternoon, and it does work.

// sync-stripe.js
import Stripe from 'stripe';
import { Client } from 'pg';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const db = new Client({ connectionString: process.env.DATABASE_URL });

await db.connect();

const customers = await stripe.customers.list({ limit: 100 });

for (const c of customers.data) {
  await db.query(
    `INSERT INTO stripe_customers (id, email, name, created)
     VALUES ($1, $2, $3, to_timestamp($4))
     ON CONFLICT (id) DO UPDATE SET email = $2, name = $3`,
    [c.id, c.email, c.name, c.created]
  );
}

await db.end();

One crontab line and it is a pipeline:

0 2 * * * /usr/bin/node /srv/sync/sync-stripe.js

Run it, and rows appear in Postgres. The demo is convincing. If your Stripe account has 80 customers and you only ever need customers, you can genuinely stop here, and you should.

What the Script Quietly Does Not Handle

The script above is correct for exactly one shape of data: a single small table, fetched whole, with no failures. Every assumption in that sentence breaks at some point.

Pagination. limit: 100 is the maximum page size, not the maximum table size. Past 100 customers you are silently syncing a fraction of your data, and nothing errors. You need has_more and cursor loops on every list call.

Rate limits. Stripe's published limits are 100 requests per second in live mode, but the constraint that actually bites is the read allocation, which scales with your transaction volume and has a floor of 10,000 reads a month. Xero is stricter, with a per-tenant daily call cap. Hit either and your script does not slow down gracefully, it throws. You need backoff, retry with jitter, and a way to resume a half-finished run.

Incremental fetching. Re-reading every invoice every night stops being viable somewhere in the low tens of thousands of rows. So you add a watermark, and then you discover the genuinely hard part: updated_at semantics differ per provider, some objects do not expose one at all, and a naive "since last run" bookmark loses data permanently the first time a run fails. Fixed lookback windows are more robust than bookmarks, which is why we default to them.

Deletions and amendments. An upsert never removes anything. Delete a customer in Stripe, void an invoice in Xero, amend last quarter's entry in QuickBooks, and your table keeps the stale row forever. Accounting systems do this constantly, and it is the failure mode most likely to put a wrong number in front of your finance team.

Schema drift. Providers add fields. Your INSERT has a fixed column list, so new fields are dropped silently, and the day you want one you are writing a migration and a backfill.

Type handling. Stripe returns amounts as integers in the smallest currency unit and timestamps as Unix seconds. Get the conversion wrong once and every revenue figure downstream is out by a factor of 100. Nested objects need a considered jsonb policy rather than a JSON.stringify reflex.

Secrets and connections. process.env on a box you SSH into is fine until it is not. Long-lived API keys need rotating, OAuth providers like QuickBooks and Xero need refresh-token handling with the refresh persisted somewhere durable, and a script that reconnects to Postgres on every run will exhaust a pooled connection limit faster than you expect.

None of these is hard. That is exactly the trap. Each one is a two-hour job, and there are nine of them, and you find them one at a time over six months, each announced by a number that looks slightly wrong.

Somewhere to Run It: The Infrastructure Nobody Budgets

This is the cost that gets left out of every build-versus-buy estimate, because crontab -e feels free. It is not free, it is just unpriced. Your script has to live somewhere, and every option has a real bill and a real failure mode.

Your laptop. Free, and it works until you close the lid. Not a serious option, but worth naming because a surprising number of pipelines start here and quietly stay here.

A small VPS. The honest baseline. A DigitalOcean basic droplet starts at $4 a month for 512 MiB and 1 vCPU, which is ample for a nightly sync. The bill is not really $4 though, it is $4 plus a machine you now own: OS patches, Node version upgrades, disk filling with logs, and a box that is invisible to your team until it is the reason the numbers are stale.

GitHub Actions on a schedule trigger. Genuinely appealing, and the most common free choice. Read the documentation carefully first. GitHub states that the shortest interval is once every 5 minutes, that "the schedule event can be delayed during periods of high loads", that "if the load is sufficiently high enough, some queued jobs may be dropped", and that in a public repository scheduled workflows are automatically disabled after 60 days of no repository activity. A scheduler that may silently drop your run, and may switch itself off, is a poor foundation for the table your MRR dashboard reads from. Private repositories on the Free plan include 2,000 minutes a month, after which Linux runners bill at $0.006 a minute.

Serverless functions on a scheduler. Cheap per invocation and pleasantly hands-off, until a full backfill runs past the execution timeout. Then you are writing checkpointing and chunking, which is a genuine engineering project rather than a cron line.

Whichever you pick, you still need the part that is not the schedule: somewhere to see whether last night's run succeeded, and something that tells you when it did not. A cron job that fails is completely silent by default. The realistic failure is not a dramatic outage, it is a script that has been dying at 2am for three weeks while everyone kept reading the dashboard it feeds. We covered why these pipelines break in production in more detail.

Run history and alerting is the piece people defer indefinitely, and it is the piece that decides whether you can trust the data.

What Building a Data Pipeline Actually Costs

Put hours against it. These are for a developer who knows the stack, building one provider properly, with no padding for the parts they have not met yet.

PieceHours
First working script, one table4 to 8
Pagination and initial backfill3 to 6
Rate-limit backoff, retries, resumable runs4 to 8
Upserts, deletions, amendment handling4 to 8
Incremental windows4 to 8
Secrets, OAuth refresh, multi-table config3 to 6
Deploy target and scheduling3 to 8
Run history and failure alerting4 to 8
Total29 to 60

IT Jobs Watch puts the UK median backend developer contract day rate at £525 for the six months to 20 September 2026, which is roughly £70 an hour. That makes the build £2,000 to £4,200, or about $2,700 to $5,700.

If you are a founder writing it yourself, substitute your own number, but do not substitute zero. Those 29 to 60 hours are a week to a week and a half of full-time work not spent on the product, and that is the expensive part, not the cash.

Then add the running cost: $4 to $20 a month for compute, plus whatever your monitoring costs once you stop pretending you will check the logs by hand.

And that is one provider. Adding QuickBooks or Xero afterwards is not a copy-paste, because OAuth, tenant handling and pagination semantics are all different.

The Maintenance Line That Never Ends

The build is finite. The maintenance is not, and this is the number that decides the argument.

Every month there is something. An API version deprecation with a migration window. A new field finance wants, which means a migration plus a backfill. A refresh token that expired while someone was on holiday. A rate limit you started hitting because the business grew. A Node or dependency upgrade on that droplet. A run that failed at 2am and needs replaying by hand.

Call it 2 to 4 hours a month once things have settled down, which is conservative. At £70 an hour that is £140 to £280 a month, every month, indefinitely. It is invisible in any budget because it is never a line item, it is just a Tuesday morning that went somewhere.

Over two years, the build plus that maintenance comes to somewhere between £5,400 and £10,900, ignoring compute.

When Rolling Your Own Is the Right Call

It genuinely is sometimes, and the cases are specific:

  • You need one small table and nothing else, ever. Eighty customers, one nightly pull. The script at the top of this post is the correct answer, and buying anything would be silly.
  • Your transformation is the actual product. If the point is bespoke enrichment, or joining three sources into a proprietary shape mid-flight, you were going to write code regardless.
  • You already run a pipeline platform. If Airflow or Dagster is up with alerting and an on-call rota, one more DAG is a genuinely small marginal cost. The infrastructure section above is already paid for.
  • Compliance forbids third-party credential access. A hard constraint is a hard constraint.

Notice what those have in common: either the work is trivially small, or the expensive infrastructure already exists. Outside those, you are paying to rebuild something undifferentiated.

What Managed Sync Costs Instead

Codeless Sync exists to be the other side of that comparison, so here is the pricing plainly.

Starter is $19 a month and Pro is $29, both with daily, weekly and monthly schedules, and 10 and 30 configurations respectively. Business is $99 and adds twelve-hourly scheduling. There is a free tier with two manual syncs a day while you evaluate it.

The comparison over two years:

Build itCodeless Sync Pro
Up-front£2,000 to £4,200£0
Compute$4 to $20 a monthIncluded
Maintenance£140 to £280 a month£0
Alerting and run historyYou build itIncluded
Adding a second providerMost of another buildA configuration
Two-year total£5,400 to £10,900about £550

Pagination, backoff, upserts, deletion handling, lookback windows, OAuth refresh, run history and failure alerting all sit on our side of the line. Destination tables are created for you, and the schedule is a dropdown rather than infrastructure. If you want the cron syntax behind those presets, we wrote that up separately.

The quick start takes about five minutes, which is a useful benchmark against the afternoon.

Frequently Asked Questions

Is it cheaper to build your own data pipeline or use a managed sync tool?

For a single small table you pull occasionally, building it is cheaper, and a 20-line script does the job. Past that, managed sync wins quickly. A properly built pipeline for one provider runs 29 to 60 developer hours up front and 2 to 4 hours a month to maintain, which at UK median contract rates is £2,000 to £4,200 to build and £140 to £280 a month to keep. Managed sync starts at $19 a month with no build.

Can I just use a GitHub Actions cron job for free?

You can, with caveats worth knowing first. GitHub documents that scheduled workflows may be delayed under high load, that queued jobs may be dropped entirely when load is high enough, and that scheduled workflows in public repositories are disabled automatically after 60 days without repository activity. Private repositories on the Free plan get 2,000 minutes a month. It is fine for something you can afford to miss, and a poor choice for a table your reporting depends on.

What usually breaks first in a homemade sync script?

Pagination, and it breaks silently. A list call with limit: 100 returns the first page whether you have 80 records or 80,000, with no error raised. The second is deletion handling: upserts never remove rows, so voided invoices and deleted customers linger indefinitely. Both surface as numbers that are slightly wrong rather than as a failure, which is why they survive so long.

How long does it really take to build a Stripe to Postgres sync?

The first working version takes an afternoon. A version you would trust your revenue reporting to takes 29 to 60 hours, because pagination, rate-limit backoff, incremental windows, deletions, schema drift, secrets handling, deployment and alerting are each small jobs that you meet one at a time over several months.

Does this change if I need more than one provider?

It gets worse, not better. A second provider is not a copy-paste: QuickBooks and Xero use OAuth with refresh tokens rather than a static key, Xero adds per-tenant handling and a stricter daily call cap, and pagination semantics differ across all four. Expect most of another build per provider. With managed sync it is a new configuration.

The Honest Comparison

The choice is not "write 50 lines" versus "pay $19 a month". It is "own a small piece of infrastructure indefinitely" versus "pay $19 a month".

If that infrastructure is load-bearing for your product, own it. If it is a table your dashboard reads so you can look at MRR without opening four tabs, the maintenance line is the whole argument, and it is the one nobody prices until they are already paying it.

Worth doing the arithmetic before the afternoon, rather than eighteen months into it.

Questions or feedback? Feel free to reach out. If you found this helpful, you can try Codeless Sync for free.