Your project API key is auto-filled in the setup guide on your project dashboard. These docs use placeholder keys.
Works with any framework — React, Next.js, Vue, plain HTML. Add to your root layout or <head>:
<script defer data-api-key="ef_your_key_here" src="https://easyfunnel.co/sdk.js" ></script>
Track clicks and section views with data attributes — no JavaScript needed:
<!-- Fires a "signup_cta" event on click --> <button data-ef-track="signup_cta">Sign Up</button> <!-- Fires a "section_view" event when scrolled into view --> <section data-ef-section="pricing">...</section>
For programmatic tracking, use the global window.easyfunnel object:
<script>
easyfunnel.track('click_signup', { plan: 'indie' })
easyfunnel.identify('user_123')
</script>Auto-updates: The script tag always loads the latest SDK from our CDN. You get new features and bug fixes automatically — no deploys needed.
npm install @easyfunnel/sdk
import { EasyFunnel } from '@easyfunnel/sdk'
const ef = EasyFunnel.init({
apiKey: 'ef_your_key_here',
debug: true, // logs events to console in development
})
// Track custom events
ef.track('click_signup', { plan: 'indie' })
// Identify users after login
ef.identify('user_123')Auto-updates: The npm package is a thin loader that fetches the latest SDK from our CDN at runtime. You get new features and fixes automatically — no need to run npm update.
If you prefer React hooks for programmatic tracking, install the React package. For most apps, the script tag + data-ef-track attributes above are simpler and auto-update.
npm install @easyfunnel/sdk @easyfunnel/react
Add the provider to your root layout:
import { EasyFunnelProvider } from '@easyfunnel/react'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<EasyFunnelProvider apiKey="ef_your_key_here">
{children}
</EasyFunnelProvider>
</body>
</html>
)
}Track events from any component:
import { useTrack } from '@easyfunnel/react'
function PricingButton() {
const track = useTrack()
return (
<button onClick={() => track('click_subscribe', { plan: 'indie' })}>
Subscribe
</button>
)
}The SDK automatically tracks:
data-ef-track attributedata-ef-section attribute fires a section_view event when scrolled into view<!-- Fires a "signup_cta" event on click --> <button data-ef-track="signup_cta">Sign Up</button>
<!-- Fires a "section_view" event when this section scrolls into view --> <section data-ef-section="pricing"> ... </section>
Section tracking powers the Landing Page Health Score on your dashboard. Add data-ef-section to each major section of your page to measure scroll depth and engagement.
Enable optional modules for richer insights. Each module is off by default — opt in via script attributes or SDK options.
Core Web Vitals (LCP, CLS, INP, FCP, TTFB)
Script: data-web-vitals — JS: webVitals: true
Scroll depth, time on page, engaged reading time
Script: data-engagement — JS: engagement: true
Rage clicks and dead clicks
Script: data-frustration — JS: frustrationDetection: true
Form starts, abandons, and submissions
Script: data-form-tracking — JS: formTracking: true
Uncaught JS errors with stack traces
Script: data-error-tracking — JS: errorTracking: true
Add data attributes to enable modules:
<script defer data-api-key="ef_your_key_here" data-web-vitals data-engagement data-frustration data-form-tracking data-error-tracking src="https://easyfunnel.co/sdk.js" ></script>
Pass options to EasyFunnel.init() or as props on <EasyFunnelProvider>:
EasyFunnel.init({
apiKey: 'ef_your_key_here',
webVitals: true,
engagement: true,
frustrationDetection: true,
formTracking: true,
errorTracking: true,
})Each module collects data automatically. The SDK enriches every event with browser, OS, device type, viewport, language, timezone, and country — no configuration needed.
__web_vitals events with metric name, value, and rating (good / needs-improvement / poor)__engagement on page unload with scroll depth %, engaged time, and total time__rage_click (3+ fast clicks) and __dead_click (click with no DOM change) with element selector__form_start, __form_submit, and __form_abandon with form ID and duration__js_error with error type, message, and source locationThe SDK automatically detects bots (Googlebot, headless browsers, web scrapers, etc.) and tags every event with an is_bot flag. Bot sessions are excluded from the Active Sessions chart in your dashboard so your metrics reflect real human traffic only.
Before a user signs up, they are anonymous (tracked by session ID). When you call ef.identify('user_123'), all their previous anonymous events are retroactively linked to that user ID.
// Call after login/signup
ef.identify('user_123')
// Or with the React hook:
const identify = useIdentify()
identify('user_123')The MCP server lets Claude instrument your code and query your analytics directly from the editor.
claude mcp add easyfunnel -e EASYFUNNEL_API_KEY=efa_your_account_api_key -- npx -y @easyfunnel/mcp@latest
{
"mcpServers": {
"easyfunnel": {
"command": "npx",
"args": ["-y", "@easyfunnel/mcp@latest"],
"env": {
"EASYFUNNEL_API_KEY": "efa_your_account_api_key"
}
}
}
}Find your account API key in Settings.
list_projectsList all your projectscreate_projectCreate a new projectsetup_sdkInstall SDK, hardcode API key, and add provider to your codebasescan_for_actionsFind trackable buttons, forms, and linksinstrument_codeAdd tracking attributes or calls to elementscreate_funnelDefine a conversion funnel from event stepsget_funnel_healthGet conversion and drop-off dataquery_eventsQuery event counts, recent events, or breakdownssetup_chat_widgetSet up the AI chat widget for a projectAPI key handling: setup_sdk hardcodes your ef_* project API key directly in the layout file. This is safe — the key is public and always visible in the browser bundle. Hardcoding eliminates deployment failures on static hosts (Cloudflare Workers, GitHub Pages, S3) where env vars aren't available. You can still override it via an env var if you prefer.
Add an AI-powered chat widget to your website. Visitors can ask questions and get answers based on your knowledge base — like a lightweight Intercom.
<script defer data-api-key="ef_your_key_here" src="https://easyfunnel.co/chat.js" ></script>
import { EasyFunnelChat } from '@easyfunnel/sdk'
EasyFunnelChat.init({
apiKey: 'ef_your_key_here',
// Optional:
primaryColor: '#6366f1',
position: 'bottom-right',
})Tell Claude: "Set up the AI chat widget for my project" and it will create the configuration and show you the embed code.
POST https://easyfunnel.co/api/collect
Content-Type: application/json
{
"api_key": "ef_your_project_key",
"events": [
{
"session_id": "anonymous-uuid",
"event_name": "page_view",
"properties": {
"url": "/pricing",
"referrer": "https://google.com"
}
}
]
}
Response: 202 AcceptedMax 20 events per request. Max 4KB per event. Rate limit: 100 req/s per API key.
Ad blockers and browser privacy features can prevent JavaScript analytics from capturing every page view. EasyFunnel includes a built-in capture rate validator so you can see exactly how much traffic the SDK is capturing.
On every page view, the SDK fires a 1x1 transparent pixel via an <img> tag in addition to the normal JavaScript event. Image beacons are nearly impossible for ad blockers to block, so the pixel count represents your true traffic baseline.
Your project dashboard shows a Capture Rate card that compares pixel pings vs SDK events:
Compare your pixel ping count (shown on the capture rate card) with Google Analytics page views for the same period. If the numbers are close, both tools are seeing the same traffic. If GA shows significantly more, check that the SDK script is loading early enough (use beforeInteractive in Next.js or place the script in <head>).
The SDK is designed for reliable event delivery with minimal impact on your page performance:
sendBeacon for non-blocking delivery. Falls back to fetch with keepalive.visibilitychange, pagehide, and beforeunload to flush queued events before the user leaves. This works reliably on all browsers including mobile Safari.EasyFunnel provides 13 free marketing tools — calculators, generators, and audit tools. No signup required.