Commerce8 min read

How to Integrate Lemon Squeezy with Your Headless CMS

Sync product pages, pricing copy, and checkout links between Lemon Squeezy and your headless CMS so customers always see the right plan before they buy.

Published April 29, 2026
01Overview

What is Lemon Squeezy?

Lemon Squeezy is a merchant of record platform for selling software, digital products, subscriptions, and license keys. It handles checkout, payments, sales tax, VAT, subscriptions, invoices, and order webhooks, which makes it common for SaaS teams, indie software companies, and digital product sellers. Teams often use it when they want commerce operations handled without building billing, tax, and payment flows from scratch.


02The case for integration

Why integrate Lemon Squeezy with a headless CMS?

If your product page lives in one system and your checkout configuration lives in Lemon Squeezy, small changes can turn into risky manual work. A pricing page update might require an editor to change plan copy, a developer to update frontend constants, and an operations teammate to confirm the matching Lemon Squeezy variant ID. That works for 3 products. It gets messy when you have 20 plans, 4 currencies, limited-time discounts, comparison tables, and launch pages going live at 9:00 AM.


03Architecture

Architecture overview

A typical flow starts when an editor publishes a product or plan document in Sanity Studio. That document is written to the Content Lake with fields such as title, slug, shortDescription, priceDisplay, feature references, lemonSqueezyProductId, and lemonSqueezyVariantId. A Sanity webhook fires on the publish event, or a Sanity Function runs on the content mutation, and the handler receives the changed document ID. The handler then runs a GROQ query to fetch only the fields Lemon Squeezy needs, including joined feature copy from referenced documents. From there, server-side code calls Lemon Squeezy's API or the @lemonsqueezy/lemonsqueezy.js SDK with a private API key. For example, it can update a variant's public name and description, create a checkout for a specific variant, or refresh checkout URLs used by the frontend. The customer-facing site queries Sanity for the product page, renders the current copy and comparison table, and sends the buyer to Lemon Squeezy checkout for payment, tax, subscriptions, license keys, and receipts. Lemon Squeezy order and subscription webhooks can then update your app account system after purchase.


04Use cases

Common use cases

💳

SaaS pricing pages

Model Free, Pro, and Team plans in Sanity, map each plan to a Lemon Squeezy variant, and keep plan copy, feature rows, and checkout buttons in sync.

🔑

License key product launches

Publish a launch page from Sanity and route buyers to Lemon Squeezy products that issue license keys after payment.

📦

Digital downloads

Use Sanity for product storytelling, screenshots, changelogs, and FAQs while Lemon Squeezy handles file delivery, receipts, and tax.

🏷️

Campaign-specific checkouts

Create seasonal landing pages in Sanity that point to Lemon Squeezy variants, discounts, or prefilled checkout URLs for specific campaigns.


05Implementation

Step-by-step integration

  1. 1

    Set up Lemon Squeezy

    Create a Lemon Squeezy account, add your store, create products and variants in the dashboard, and copy the store ID, product IDs, and variant IDs. Generate an API key from Lemon Squeezy settings, and keep it server-side only.

  2. 2

    Install the SDKs

    In your app or Sanity Function project, install @lemonsqueezy/lemonsqueezy.js for Lemon Squeezy API calls and @sanity/client for GROQ reads from the Content Lake.

  3. 3

    Model products in Sanity Studio

    Create a product or plan schema with fields like title, slug, shortDescription, priceDisplay, feature references, lemonSqueezyProductId, lemonSqueezyVariantId, and checkoutCta. Keep billing rules in Lemon Squeezy, not in editable content fields.

  4. 4

    Create the sync trigger

    Add a Sanity webhook that fires when product documents are published, or use a Sanity Function if you want the sync code to run on Sanity's event system without running a separate server. Configure the webhook payload to include _id and _type.

  5. 5

    Call Lemon Squeezy from server-side code

    Use GROQ to fetch the changed product, then call Lemon Squeezy's API with the mapped product or variant ID. Common actions include updating variant display fields, creating checkout links, or validating that every published plan has a matching Lemon Squeezy variant.

  6. 6

    Test the storefront and checkout path

    Publish a test product, confirm the sync response, load the frontend page, click through to Lemon Squeezy checkout, and verify that order or subscription webhooks update your app after purchase.


06Code

Code example

typescriptapp/api/sanity-to-lemon/route.ts
import { createClient } from "@sanity/client";
import { lemonSqueezySetup, updateVariant } from "@lemonsqueezy/lemonsqueezy.js";

lemonSqueezySetup({ apiKey: process.env.LEMONSQUEEZY_API_KEY! });

const sanity = createClient({
  projectId: process.env.SANITY_PROJECT_ID!,
  dataset: process.env.SANITY_DATASET!,
  apiVersion: "2025-02-19",
  useCdn: false,
  token: process.env.SANITY_READ_TOKEN
});

const query = `*[_id == $id][0]{
  title,
  "description": shortDescription,
  "variantId": lemonSqueezy.variantId,
  priceCents
}`;

export async function POST(req: Request) {
  const event = await req.json();
  const product = await sanity.fetch(query, { id: event._id });

  if (!product?.variantId) {
    return Response.json({ skipped: true });
  }

  const { data, error } = await updateVariant(product.variantId, {
    name: product.title,
    description: product.description,
    price: product.priceCents
  });

  if (error) {
    return Response.json({ error: error.message }, { status: 502 });
  }

  return Response.json({ syncedVariantId: data?.id });
}

07Why Sanity

How Sanity + Lemon Squeezy works

Build your Lemon Squeezy integration on Sanity

Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect product content with Lemon Squeezy checkout flows.

Start building free →

08Comparison

CMS approaches to Lemon Squeezy

CapabilityTraditional CMSSanity
Product content modelingProduct copy often sits inside pages, so plan names, feature rows, and checkout IDs can be hard to reuse safely.Schema-as-code lets you model plans, feature groups, FAQs, launch windows, and Lemon Squeezy IDs as versioned fields.
Sync on publishTeams often rely on manual updates or scheduled scripts after a pricing page changes.GROQ-powered webhooks and Functions can trigger only for product documents and send the exact fields your sync needs.
Checkout field ownershipPayment-related values can get mixed into page content, which raises the chance of editing mistakes.Sanity Studio can show editable marketing fields while keeping Lemon Squeezy product, variant, and billing identifiers controlled.
Multi-channel product pagesContent is usually built for the website first, then copied into landing pages, apps, or emails.GROQ can join product copy, feature references, media, and checkout IDs for web, mobile, email, and agent experiences.
Operational trade-offsSimple for small sites, but manual commerce updates become risky as product count grows.Functions can run sync logic on content events, though you'll still need clear rules for retries, id mapping, and which fields Lemon Squeezy owns.

09Next steps

Keep building

Explore related integrations to complete your content stack.

Ready to try Sanity?

See how Sanity's Content Operating System powers integrations with Lemon Squeezy and 200+ other tools.