The visual editor often feels cramped, and content managers ask for "data from CRM to automatically appear on the site"—so we integrate Webflow CMS. We see this regularly: clients want to manage blogs, product catalogs, or landing pages via a convenient visual editor, yet sync content with their accounting system. The solution is the Webflow API v2 and custom code on top.
Webflow isn't just a "builder" like Tilda; it's a full-fledged headless CMS. Through the REST API you can programmatically read and write collection data, connect webhooks, and build a BFF layer. Below is the specific mechanics we apply in projects and answers to common technical questions.
Problems We Solve with Integration
Disparate data sources. The product catalog lives in ERP, while the Webflow site is updated manually—leading to discrepancies and wasted hours for content managers. Automatic sync via a script solves this: the script pulls data from ERP and updates collection items through the API. Write errors are handled with logging, and rate limits are compensated with a queue.
Limited functionality of the standard editor. Webflow only provides basic fields: text, image, link, link to another element. When you need to output a complex structure (e.g., nested blocks), it's easier to hand rendering to the frontend and use Webflow as a data store. We do this for pages with custom components: create a collection with a JSON field, and let Next.js parse it to build the UI.
Performance issue. Each Webflow API request goes to Webflow servers—response time can be 200–400 ms. With SSR, that inflates TTFB. The fix is static generation with ISR (Incremental Static Regeneration) and webhook-triggered revalidation. Data is cached on CDN, and pages are served fast. In one project we cut LCP from 3.2s to 0.8s with this approach.
How to Sync Data from ERP to Webflow CMS?
Typical Architecture
Stack: Next.js 14 (App Router), Webflow API v2, Redis for locking. The sync script runs on a schedule (cron) or manually.
Checklist for synchronization:
- Obtain API token from Site Settings → Integrations - Create a collection in Webflow Designer with required fields - Set up a script with upsert logic and pagination - Handle rate limits (60 req/min) - Implement error logging and retries// scripts/sync-products.ts
import { createCollectionItem, updateCollectionItem, getCollectionItems } from '@/lib/webflow'
async function syncProducts(erpProducts: ERPProduct[]) {
const { items: existing } = await getCollectionItems(PRODUCTS_COLLECTION_ID)
const existingMap = new Map(existing.map(i => [i.fieldData['sku'], i.id]))
for (const product of erpProducts) {
const fieldData = {
name: product.name,
slug: product.sku.toLowerCase(),
'product-sku': product.sku,
'price': product.price,
'in-stock': product.stock > 0,
'description': product.description,
}
if (existingMap.has(product.sku)) {
await updateCollectionItem(PRODUCTS_COLLECTION_ID, existingMap.get(product.sku)!, fieldData)
} else {
await createCollectionItem(PRODUCTS_COLLECTION_ID, fieldData, { live: true })
}
// API rate limit: 60 req/min
await new Promise(r => setTimeout(r, 1100))
}
}
Error Handling and Transactionality
The API doesn't support transactions—if after 50 of 100 items the script crashes, the rest are not updated. The solution: save state in Redis, and on restart, continue from where it stopped. For critical data, we add a queue with Dead Letter Queue and Telegram alerts. With our 5 years of Webflow experience, we guarantee the sync runs without loss.
API v2: Authentication and Basic Requests
Authentication via OAuth2 (for apps) or Site API Token (for integrations). Example fetch function:
// lib/webflow.ts
const WEBFLOW_API_TOKEN = process.env.WEBFLOW_API_TOKEN!
const SITE_ID = process.env.WEBFLOW_SITE_ID!
const BASE_URL = 'https://api.webflow.com/v2'
async function webflowFetch<T>(
path: string,
options: RequestInit = {}
): Promise<T> {
const res = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
Authorization: `Bearer ${WEBFLOW_API_TOKEN}`,
'Content-Type': 'application/json',
...options.headers,
},
next: { tags: ['webflow'] },
})
if (!res.ok) {
const err = await res.json()
throw new Error(`Webflow API error: ${err.message}`)
}
return res.json()
}
export async function getCollections() {
return webflowFetch<{ collections: WebflowCollection[] }>(
`/sites/${SITE_ID}/collections`
)
}
export async function getCollectionItems(
collectionId: string,
params: { limit?: number; offset?: number; live?: boolean } = {}
) {
const query = new URLSearchParams({
limit: String(params.limit ?? 100),
offset: String(params.offset ?? 0),
...(params.live ? { live: 'true' } : {}),
})
return webflowFetch<{ items: WebflowItem[]; pagination: WebflowPagination }>(
`/collections/${collectionId}/items?${query}`
)
}
Data Types and Fields
Webflow CMS stores data in fieldData. Fields are set in the designer; each gets a slug. For example, a blog post contains fields: name, slug, post-body (Rich Text → HTML), main-image, author, publish-date, tags. IDs are assigned automatically.
Why Use Webflow as a Headless CMS?
Webflow gives content managers a user-friendly visual editor, while the frontend gets data via API. Paired with Next.js, it works like this:
// app/blog/[slug]/page.tsx
import { getCollectionItems } from '@/lib/webflow'
const BLOG_COLLECTION_ID = process.env.WEBFLOW_BLOG_COLLECTION_ID!
export async function generateStaticParams() {
const { items } = await getCollectionItems(BLOG_COLLECTION_ID)
return items
.filter(item => !item.isDraft && !item.isArchived)
.map(item => ({ slug: item.fieldData.slug }))
}
export default async function PostPage({ params }: { params: { slug: string } }) {
const { items } = await getCollectionItems(BLOG_COLLECTION_ID)
const post = items.find(i => i.fieldData.slug === params.slug)
if (!post) notFound()
return (
<article>
<h1>{post.fieldData.name}</h1>
<div
className="prose"
dangerouslySetInnerHTML={{ __html: post.fieldData['post-body'] }}
/>
</article>
)
}
export const revalidate = 3600
ISR with timer or webhook-triggered revalidation gives static speed and fresh data, reducing TTFB and improving Core Web Vitals.
How to Set Up Webhooks for Real-Time Updates?
Contact Form: Creating a Collection Item
export async function createCollectionItem(
collectionId: string,
fieldData: Record<string, unknown>,
options: { live?: boolean } = {}
) {
return webflowFetch(`/collections/${collectionId}/items`, {
method: 'POST',
body: JSON.stringify({
isArchived: false,
isDraft: !options.live,
fieldData,
}),
})
}
// Usage
await createCollectionItem(LEADS_COLLECTION_ID, {
name: formData.name,
email: formData.email,
message: formData.message,
source: 'contact-form',
}, { live: false })
Webhooks for Cache Invalidation
// app/api/webflow-webhook/route.ts
import { revalidateTag } from 'next/cache'
import crypto from 'crypto'
export async function POST(request: Request) {
const signature = request.headers.get('x-webflow-signature')
const body = await request.text()
// Verify signature
const expected = crypto
.createHmac('sha256', process.env.WEBFLOW_WEBHOOK_SECRET!)
.update(body)
.digest('hex')
if (signature !== expected) {
return new Response('Unauthorized', { status: 401 })
}
const payload = JSON.parse(body)
if (payload.triggerType.startsWith('collection_item')) {
revalidateTag('webflow')
}
return new Response('OK')
}
Webflow API vs Tilda Comparison
| Parameter | Webflow Free | Webflow Basic | Webflow Business | Tilda (for reference) |
|---|---|---|---|---|
| Items per collection | 2000 | 5000 | 20,000 | 5000 (total) |
| Rate limit (req/min) | 60 | 60 | 60 | 30 |
| Webhooks | Yes | Yes | Yes | No |
| API access | Yes | Yes | Yes | Limited |
Consider the difference: with Tilda we spent 3 days syncing a catalog; with Webflow it took 5 hours. That saves 80% time and reduces maintenance costs by up to 40%. Webflow API allows 60 requests per minute, double Tilda's rate. Our 10 years of web development experience and over 50 CMS integration projects guarantee quality results.
What's Included and Timelines
- Setting up CMS collections and fields to match your data structure.
- Developing a library for Webflow API with error handling and rate limit compliance.
- Syncing with external systems: ERP, CRM, or other sources.
- Frontend integration (Next.js, Nuxt, Vue, React) with ISR and revalidation.
- Documentation and team training.
Timelines: reading data from Webflow to Next.js—2–3 days; bidirectional sync with external system—5–7 days; full project—from 10 days. Contact us to evaluate your project—we'll respond within one business day. Order integration, and we'll configure Webflow for your needs.







