Build in Public

Lessons from launching an AI-driven price optimization script for a niche SaaS product

1. Define the problem scope before coding

Start with a clear objective: maximize monthly recurring revenue (MRR) while keeping churn below a target threshold (e.g., 2 %). Limit the experiment to a single pricing tier or add‑on to avoid confounding variables. Document assumptions: price elasticity is roughly linear in the observed range, customers are homogeneous enough for a segment‑level model, and price changes can be applied weekly without violating contractual terms.

2. Gather and clean historical data

Export at least 6 months of usage, invoice, and churn events from your billing system (Stripe, Chargebee, etc.) into a CSV. Key columns:

  • customer_id
  • timestamp (date of invoice)
  • plan_price
  • usage_metrics (e.g., API calls, storage GB)
  • churn_flag (1 if subscription ended next period)
  • discount_code (if any)

Remove test accounts, filter out plans with < 10 observations, and impute missing usage with the median of the same plan. Verify that price changes in the log are infrequent enough to treat each price point as a quasi‑experiment.

3. Choose a simple modeling approach

For a first iteration, avoid complex deep RL. A linear regression of churn probability on price and usage works as a baseline:

churn_prob = β0 + β1*price + β2*usage + β3*price*usage

Fit with scikit-learn’s LogisticRegression (penalty='none') to get interpretable coefficients. If the interaction term is significant, you have evidence that price sensitivity varies with usage—a signal for segmentation.

If you want to experiment with RL, frame the environment as a discrete‑action problem:

  • State: recent usage metrics + current price
  • Action: {‑10 %, ‑5 %, 0 %, +5 %, +10 %} price adjustment
  • Reward: next‑month MRR change – λ * churn increase

Use OpenAI Gym’s Env wrapper and train with Proximal Policy Optimization (PPO) from stable‑baselines3. Keep the episode length to one billing cycle (≈30 days) to match real‑world feedback latency.

4. Implement a safe rollout mechanism

Never change prices for all users at once. Use a feature flag (LaunchDarkly, Unleash, or a simple boolean column in PostgreSQL) to route 5 % of new sign‑ups to the model‑suggested price. Monitor:

  • Daily MRR per flag group
  • Churn rate per flag group
  • Support tickets mentioning pricing

Set up alerts in Grafana (or Metabase) that trigger if the test group’s churn exceeds the control group by more than 0.5 % absolute.

5. Automate retraining and deployment

Schedule a weekly GitHub Actions workflow:

  1. Pull latest billing export from Stripe via API.
  2. Run the data‑cleaning notebook (Papermill) to produce a cleaned dataset.
  3. Retrain the model, store the artifact (pickle or ONNX) in an S3 bucket.
  4. Update a JSON configuration file read by the pricing microservice at startup.

Tag each run with a semantic version (e.g., price-model-v2024-09-25) and keep the last three versions for rollback.

6. Evaluate results with statistical rigor

After 4–6 weeks, compute the lift in MRR using a difference‑in‑means test:

ΔMRR = mean(MRR_test) - mean(MRR_control)

Calculate a 95 % confidence interval via bootstrapping (10 000 resamples). If the interval excludes zero and the churn difference is not statistically significant, consider the model successful.

Document any confounding events (e.g., a concurrent marketing campaign) that could bias the outcome.

7. Iterate based on feedback

If the model suggests prices that are too high for low‑usage customers, introduce a usage‑based tier or a discount rule. Re‑label the data with the new tier and repeat the modeling cycle.

Keep a changelog of model versions, feature flags, and observed metrics. This transparency makes it easier to explain decisions to stakeholders and to reproduce the experiment later.

8. Cost and alternatives

  • Paid: Stripe Billing for automated invoice adjustments (~0.5 % of transaction volume).
  • Free/open‑source: Use Stripe’s basic API with a cron job to update price_id via the Checkout Session or Subscription API; host the model on a low‑cost VPS (e.g., $5/mo DigitalOcean droplet).
  • ML library alternatives: If scikit‑learn feels limiting, try statsmodels for econometric regression or Prophet for time‑series price elasticity.

Always verify current pricing on the provider’s website before committing, as rates change.

FAQ

How much historical data do I need to trust a price‑optimization model?
At least 3–4 months of weekly price observations per segment; fewer data points increase variance and risk of overfitting.
Can I use a rule‑based approach instead of ML?
Yes. Simple elasticity rules (e.g., lower price by 5 % when churn > 3 % in the prior month) often work well for early experiments and are easier to audit.
What if my customers are on annual contracts?
Model the renewal decision rather than monthly churn; use the upcoming renewal date as the action horizon and adjust the offered renewal price accordingly.
How do I avoid angering existing customers when I test new prices?
Limit experiments to new sign‑ups or to customers who have explicitly opted into beta pricing via a checkbox in your settings page.
Is it worth the engineering effort for a micro‑SaaS with <$5k MRR?
If you have at least one pricing lever you can change weekly and you see measurable churn variation with price, a lightweight model can uncover 5‑15 % MRR lifts with < 10 hours of setup. Otherwise, start with manual A/B tests.

More in Build in Public

Questions & answers

Ask anything about this post — you'll get a reply here.

  • No questions yet — be the first.

One concrete AI-automation tip a week

No hype. Unsubscribe anytime.