Stripe Can Now Stream Billing Data Into Your Own Postgres in Seconds. The Warehouse Pipeline Still Runs on a 6-Hour Lag.
Stripe's new Postgres destination syncs billing data in seconds. One catch: payment status updates can still lag 10 minutes during preview.
Every team running a webhook-based dunning stack has lived through some version of the same incident: a handler throws, a queue backs up, a retry silently stops firing, and a subscriber who should have gotten a payment-failed notice keeps using the product on an invoice that never actually got paid. We've written before about how that specific failure mode — a bug in your own event-handling code, not a decision your customer made — ends up misfiled as ordinary churn with nobody the wiser. On October 15, Stripe shipped something that doesn't fix that bug for you. It does remove a large part of the reason most teams had to write that fragile code themselves.
What actually shipped on October 15
Data Pipeline is Stripe's no-code export of your billing data — customers, subscriptions, invoices, charges, disputes, and every other resource exposed through a list endpoint in the public API. Until this release, it had one job: feed a data warehouse. You'd connect Snowflake, BigQuery, Redshift, or Databricks, accept a data share, and Stripe would keep a batch copy current on a recurring schedule. The new destination is Postgres itself — your own self-hosted database, or a managed one on Supabase, Neon, AWS, or Databricks Lakebase — and instead of a batch, it's a continuous stream. Stripe backfills your existing data once, then every subsequent change in your Stripe account shows up as a change in your Postgres tables, landing within seconds according to Stripe's own documentation.
The mechanics are specific enough to reason about rather than take on faith. Stripe writes into a dedicated schema it controls, connecting through a scoped role that only has access to that schema — your database password never leaves your infrastructure, and the credential Stripe uses is short-lived rather than a standing secret. Stripe also runs an ongoing audit against the synced data, reconciling drift on its own rather than leaving that job to you. It's currently in public preview and free to turn on.
| How you get live Stripe data into your app | Latency | Scope | What you build |
|---|---|---|---|
| Webhook + your own cache table | Minutes to hours, depending on queue depth and retry backoff | Whatever events you subscribe to | Handler, retry logic, idempotency checks, a cache schema, reconciliation jobs |
| CustomerSession active_entitlements (Aug 2026) | None — live read at request time | One customer, at the moment you ask | A CustomerSession call and a front-end component |
| Data Pipeline, warehouse destination | 3-hour cadence; up to 6 hours P50 from event to query-ready | Your entire dataset, in a separate warehouse | Nothing — Stripe maintains the share |
| Data Pipeline, real-time Postgres (Oct 2026) | A few seconds for most tables; up to 10 min on specific fields in preview | Your entire dataset, in your own operational database | Nothing to ingest — you still write the queries or triggers |
Read across that table and the real-time Postgres row is the only one that combines your whole dataset with near-instant freshness in a database you can join against your own application tables directly. Our piece on CustomerSession's embedded entitlements covers the second row in depth — it's genuinely instant, but it only answers one customer's question at a time, at render time, which is the right tool for "can this specific user see this button" and the wrong tool for "which of my 4,000 accounts went past_due in the last hour." That second question needs the fourth row.
Why "a few seconds" and "within hours" are different products, not degrees of the same thing
Stripe's own freshness documentation is unusually precise about what a warehouse sync actually promises, and it's worth sitting with the definitions before assuming "regularly refreshed" means "close to live." A 3-hour cadence means data is shared at fixed points — 00:00, 03:00, 06:00 UTC, and so on — not continuously. A record created at 00:01 UTC waits for the 03:00 batch, then takes Stripe's typical delivery time on top of that before it's actually queryable, which Stripe's own worked example puts at roughly 6 hours from the moment the event happened. That's the P50 — half the time, it's slower than that, not faster.
Postgres is the only Data Pipeline destination with sub-minute sync as of October 2026. Stripe's announcement lists real-time sync for the warehouse destinations as "coming soon," with no date given. Source: Stripe, "Sync real-time Stripe data to Postgres with Stripe Data Pipeline" (Oct 15, 2026).
That gap matters for anything you'd call an operational decision rather than a reporting one. Our earlier piece on the BigQuery destination is about cohort math and churn analytics — questions you can afford to answer this afternoon with this morning's data. A live dunning banner, an in-app flag that a renewal just failed, a real-time segment for "accounts past_due right now" feeding a save offer — none of those work on a 6-hour-old snapshot. That's the gap this release is actually built to close, and it's a different gap than the one the warehouse destinations were ever meant to close.
The part worth reading the fine print on
Two limitations matter enough to design around rather than discover in production. First, Data Pipeline only syncs resources that expose a public list endpoint. A singleton object — Balance is the clearest example — has no list endpoint and isn't available through Data Pipeline at all, real-time or batch. If a piece of logic depends on your current Stripe balance specifically, this feature doesn't carry that data for you.
Second, and more relevant to anyone building churn or dunning logic on top of this: during public preview, updates to payment_intents, invoice_items, setup_intents, identity_verification_sessions, topups, quotes, and climate_orders can take up to 10 minutes to show up, and new checkout_sessions rows carry the same delay. Everything else — including creates on those same tables — syncs in real time. payment_intents is exactly the object most involuntary-churn tooling watches for a status flip from succeeded to a failure state, which means the one signal a lot of teams would reach for first is also the one still subject to this exception. Ten minutes is a vast improvement over a multi-hour warehouse lag, but it's not the "a few seconds" the headline promises, and Stripe doesn't commit to a date for closing that gap.
What this is actually good for, and what it isn't
The clean use case is a query you couldn't run cheaply before: anything that needs to scan your whole customer base against current billing state, joined against your own application schema, without you having built and maintained the pipe that gets Stripe's data into a place you can join it from. "Every account with an active subscription, a feature flag we shipped last month, and an invoice that's been open more than 3 days" is a single SQL query against synced tables and your own tables sitting in the same database — not a webhook handler, not an export job, not a spreadsheet reconciliation. Sage's CPO and CTO, quoted in Stripe's announcement, put it plainly: the real-time sync "replaces all of the other work we had to do to get our data," letting the company retire a chunk of its own API-and-webhook plumbing.
What it isn't is a trigger system. Nothing pushes a notification to your code when a row changes — you still need a webhook, a polling job, or a Postgres LISTEN/NOTIFY trigger watching the synced tables if you want something to actually happen the moment a payment fails, rather than the next time you query. And it isn't a replacement for the single-customer, render-time check covered in our entitlements piece — asking "does this one user have this one Feature right now" is cheaper and more precise as a direct CustomerSession read than as a query against a full table sync. This release gives you a third option alongside those two, not a replacement for either.
What to actually build with it
- Start with the tables your dunning logic already cares about. Subscriptions, invoices, charges, and customers are the obvious first set — you can enable or disable individual tables at any time from the Dashboard, so there's no reason to sync your entire account on day one.
- Put a trigger on the synced tables, not a cron job polling them. A Postgres trigger that fires when a subscription row flips to
past_duegets you the "act on this now" behavior webhooks give you, without the retry and idempotency code a webhook handler needs — the sync itself already handles delivery guarantees on Stripe's side. - Treat the 10-minute preview exception as a known blind spot, not a rounding error. If a dunning banner or save-offer trigger keys off
payment_intentstatus specifically, keep your existing webhook path live for that one signal until Stripe closes the gap, and use the Postgres copy for everything downstream of it instead. - Don't delete your webhook handlers yet. This is additive infrastructure for reads. Anything that needs to fire an action the instant something happens — not just know about it on the next query — still needs an event-driven path somewhere in the stack.
The honest framing is that this closes a gap adjacent to involuntary churn without touching the mechanism we described in our piece on webhook bugs misread as churn — a crashed handler can still crash. What changes is how much custom code has to exist for that crash to be possible in the first place, and how current the data sitting behind your dunning and recovery logic actually is while you decide what to build on top of it. A cancellation flow needs the same thing a dunning banner does: an accurate, current read of what someone actually owes and what state their account is actually in, at the exact moment they show up to leave. CancelFlow is built around having that answer right when the decision is happening, not a snapshot from whenever the last batch job ran — and it's worth running the numbers with our churn calculator on what even a modest reduction in detection lag is worth, before deciding whether wiring this up is a priority this quarter.
Frequently asked questions
What is Stripe's real-time sync to Postgres?+
It's a new Data Pipeline destination, shipped in public preview on October 15, 2026, that continuously streams a copy of your Stripe data into a Postgres database you control — your own self-hosted instance, or a managed one on Supabase, Neon, AWS, or Databricks Lakebase. Stripe backfills existing records first, then keeps every subsequent change in sync. Per Stripe's own documentation, most changes reach your database within a few seconds of happening in your Stripe account.
How is this different from Stripe Data Pipeline's existing Snowflake, BigQuery, Redshift, and Databricks destinations?+
Those are batch destinations: Stripe shares data on a recurring cadence (typically every 3 hours), and Stripe's own freshness documentation puts the median (P50) delay at up to 6 hours from when an event actually happened to when it's queryable in your warehouse. The new Postgres destination is continuous rather than batched — Stripe describes it as reaching your database within seconds, not hours — and it lands in an operational database next to your own application tables instead of a separate analytics warehouse.
Does real-time Postgres sync replace Stripe webhooks?+
No, and Stripe doesn't position it that way. Webhooks deliver a notification the instant something happens, which is what you need to trigger an action — send an email, fire a Slack alert, kick off a workflow. Data Pipeline's Postgres sync maintains a continuously current copy of your data for you to query, but it doesn't push a notification to your code when a row changes. If you want to act the moment a payment fails, you still need either a webhook handler or a polling or trigger-based process watching the synced tables. What it removes is the need to build and maintain your own ingestion layer just to have a queryable, current copy of Stripe's data in the first place.
What data isn't real-time yet during the public preview?+
Two exceptions. First, only resources with a public list endpoint can sync at all — a singleton object like Balance has no list endpoint and simply isn't available through Data Pipeline, real-time or otherwise. Second, during public preview, updates to payment_intents, invoice_items, setup_intents, identity_verification_sessions, topups, quotes, and climate_orders can take up to 10 minutes to appear, along with new checkout_sessions records. Creates on those same payment-intent-adjacent tables, and everything else in the pipeline, still sync in real time. Stripe doesn't give a date for when that 10-minute exception closes.
Stop losing subscribers today
One script tag. One function call. A live cancellation flow in under 10 minutes.
Start free trial →