Set up the official Webflow JS SDK (webflow-api on npm) and configure authentication
using either a workspace/site API token or OAuth 2.0 for Data Client Apps.
https://developers.webflow.com
# npm
npm install webflow-api
# pnpm
pnpm add webflow-api
# yarn
yarn add webflow-api
The package is webflow-api (not @webflow/sdk). Current version: 3.x (Data API v2).
Webflow offers two auth methods:
| Method | Use Case | Scope |
|---|---|---|
| API Token (workspace) | Server-side scripts, internal tools | All sites in workspace |
| API Token (site) | Single-site integrations | One site only |
| OAuth 2.0 | Public apps, Webflow Marketplace apps | User-authorized scopes |
# Set environment variable (never hardcode tokens)
echo 'WEBFLOW_API_TOKEN=your-token-here' >> .env
echo '.env' >> .gitignore
import { WebflowClient } from "webflow-api";
// Initialize with workspace or site token
const webflow = new WebflowClient({
accessToken: process.env.WEBFLOW_API_TOKEN!,
});
For apps that need user authorization, implement the OAuth 2.0 authorization code flow:
import express from "express";
import { WebflowClient } from "webflow-api";
const app = express();
const CLIENT_ID = process.env.WEBFLOW_CLIENT_ID!;
const CLIENT_SECRET = process.env.WEBFLOW_CLIENT_SECRET!;
const REDIRECT_URI = "https://yourapp.com/auth/webflow/callback";
// Step 1: Redirect user to Webflow authorization page
// Scopes: sites:read, sites:write, cms:read, cms:write,
// pages:read, pages:write, forms:read, ecommerce:read,
// ecommerce:write, custom_code:read, custom_code:write
app.get("/auth/webflow", (req, res) => {
const scopes = "sites:read cms:read cms:write";
const authUrl =
`https://webflow.com/oauth/authorize` +
`?client_id=${CLIENT_ID}` +
`&response_type=code` +
`&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` +
`&scope=${encodeURIComponent(scopes)}`;
res.redirect(authUrl);
});
// Step 2: Exchange authorization code for access token
// The authorization code expires in 15 minutes
app.get("/auth/webflow/callback", async (req, res) => {
const code = req.query.code as string;
const response = await fetch("https://api.webflow.com/oauth/access_token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
code,
grant_type: "authorization_code",
redirect_uri: REDIRECT_URI,
}),
});
const { access_token } = await response.json();
// Store access_token securely — it does not expire but can be revoked
const webflow = new WebflowClient({ accessToken: access_token });
const { sites } = await webflow.sites.list();
res.json({ authorized: true, siteCount: sites?.length });
});
import { WebflowClient } from "webflow-api";
const webflow = new WebflowClient({
accessToken: process.env.WEBFLOW_API_TOKEN!,
});
async function verify() {
// List all sites accessible with this token
const { sites } = await webflow.sites.list();
if (!sites || sites.length === 0) {
throw new Error("No sites accessible. Check token scopes.");
}
for (const site of sites) {
console.log(`Site: ${site.displayName} (${site.id})`);
console.log(` Short name: ${site.shortName}`);
console.log(` Last published: ${site.lastPublished}`);
}
}
verify().catch(console.error);
| Scope | Access |
|---|---|
sites:read |
List/get sites |
sites:write |
Publish sites |
cms:read |
Read collections and items |
cms:write |
Create/update/delete CMS items |
pages:read |
List/get pages |
pages:write |
Update page content |
forms:read |
Read form submissions |
ecommerce:read |
Read products, orders, inventory |
ecommerce:write |
Create/update products, fulfill orders |
custom_code:read |
Read registered custom code |
custom_code:write |
Register/apply custom code |
webflow-api package.env file, git-ignored)WebflowClient instance| Error | Cause | Solution |
|---|---|---|
401 Unauthorized |
Invalid or revoked token | Generate new token at developers.webflow.com |
403 Forbidden |
Token missing required scope | Add scopes in app settings or generate new token |
429 Too Many Requests |
Rate limit exceeded | Wait for Retry-After header (60s reset) |
MODULE_NOT_FOUND |
Wrong package name | Use webflow-api, not @webflow/sdk |
| OAuth code expired | Authorization code > 15 min old | Re-initiate OAuth flow promptly |
After successful auth, proceed to webflow-hello-world for your first API call.