Overview
Conversion funnels help you understand where users drop off in multi-step processes like signups, checkouts, or onboarding flows. Sparklytics supports:
2-8 steps per funnel
Page and event matching for step definitions
Up to 20 funnels per website
Real-time results with timezone support
Creating Funnels
Via Dashboard
Navigate to Funnels in the sidebar
Click Create Funnel
Name your funnel (e.g., “Signup Flow”)
Define 2-8 steps with match conditions
Click Save
Via API
POST /api/websites/{website_id}/funnels
Content-Type: application/json
Authorization: Bearer {api_key}
{
"name" : "Signup Flow" ,
"steps" : [
{
"match_type" : "page" ,
"match_value" : "/pricing" ,
"label" : "View Pricing"
},
{
"match_type" : "page" ,
"match_value" : "/signup" ,
"label" : "Start Signup"
},
{
"match_type" : "event" ,
"match_value" : "signup_complete" ,
"label" : "Complete Signup"
}
]
}
Response (HTTP 201):
{
"data" : {
"id" : "fnl_abc123" ,
"name" : "Signup Flow" ,
"steps" : [
{
"step_number" : 1 ,
"match_type" : "page" ,
"match_value" : "/pricing" ,
"label" : "View Pricing"
},
{
"step_number" : 2 ,
"match_type" : "page" ,
"match_value" : "/signup" ,
"label" : "Start Signup"
},
{
"step_number" : 3 ,
"match_type" : "event" ,
"match_value" : "signup_complete" ,
"label" : "Complete Signup"
}
],
"created_at" : "2026-03-03T10:00:00Z" ,
"updated_at" : "2026-03-03T10:00:00Z"
}
}
Step Definitions
Match Types
Match pageviews by URL path (e.g., /pricing, /checkout)
Match custom events by name (e.g., signup_click, purchase)
Match Value Rules
Maximum 500 characters
Must not be empty
Case-sensitive exact matching
No wildcards (use Journey Analysis for pattern matching)
Optional Labels
Labels make funnel steps easier to read in reports:
Maximum 120 characters
Displayed in the UI instead of raw match values
Defaults to match value if not provided
Analyzing Funnel Results
GET /api/websites/{website_id}/funnels/{funnel_id}/results?
start_date = 2026-02-01 &
end_date = 2026-03-01 &
timezone = America/New_York
Response:
{
"data" : {
"funnel_id" : "fnl_abc123" ,
"steps" : [
{
"step_number" : 1 ,
"label" : "View Pricing" ,
"visitors" : 1250 ,
"conversion_rate" : 100.0 ,
"drop_off_rate" : 0.0
},
{
"step_number" : 2 ,
"label" : "Start Signup" ,
"visitors" : 487 ,
"conversion_rate" : 38.96 ,
"drop_off_rate" : 61.04
},
{
"step_number" : 3 ,
"label" : "Complete Signup" ,
"visitors" : 312 ,
"conversion_rate" : 24.96 ,
"drop_off_rate" : 75.04
}
],
"total_entered" : 1250 ,
"total_completed" : 312 ,
"overall_conversion_rate" : 24.96
}
}
Metrics Explained
Number of unique visitors who reached this step
Percentage of visitors from Step 1 who reached this step
Percentage of visitors from Step 1 who did NOT reach this step
Filtering Results
Apply the same filters available on the main dashboard:
GET /api/websites/{website_id}/funnels/{funnel_id}/results?
start_date = 2026-02-01 &
end_date = 2026-03-01 &
filter_country = US &
filter_device = mobile &
filter_utm_source = google
Available Filters
filter_country — ISO 3166-1 alpha-2 country code
filter_region — Region/state name
filter_city — City name
filter_browser — Browser name (e.g., “Chrome”)
filter_os — Operating system (e.g., “macOS”)
filter_device — Device type (desktop/mobile/tablet)
filter_page — Filter by entry page
filter_referrer — Filter by referrer domain
filter_utm_source — UTM source parameter
filter_utm_medium — UTM medium parameter
filter_utm_campaign — UTM campaign parameter
Managing Funnels
List All Funnels
GET /api/websites/{website_id}/funnels
Get Single Funnel
GET /api/websites/{website_id}/funnels/{funnel_id}
Update Funnel
PUT /api/websites/{website_id}/funnels/{funnel_id}
Content-Type: application/json
{
"name" : "Updated Signup Flow" ,
"steps" : [
// New step definitions
]
}
Delete Funnel
DELETE /api/websites/{website_id}/funnels/{funnel_id}
Returns HTTP 204 on success.
Validation Rules
From crates/sparklytics-server/src/routes/funnels.rs:144:
fn validate_steps ( steps : & [ CreateFunnelStepRequest ]) -> Result <(), ( StatusCode , Json < Value >)> {
if ! ( 2 ..= 8 ) . contains ( & steps . len ()) {
return Err ( unprocessable (
"validation_error" ,
"funnels must have between 2 and 8 steps" ,
Some ( "steps" ),
));
}
for step in steps {
if step . match_value . trim () . is_empty () {
return Err ( unprocessable (
"validation_error" ,
"match_value must not be empty" ,
Some ( "match_value" ),
));
}
if step . match_value . len () > 500 {
return Err ( unprocessable (
"validation_error" ,
"match_value must be 500 characters or fewer" ,
Some ( "match_value" ),
));
}
// Label validation...
}
Ok (())
}
Rate Limiting
Funnel endpoints have tiered rate limits:
Read Operations
Mutations
Results
# 60 requests/minute per client
# 240 requests/minute per website
GET /api/websites/{website_id}/funnels
GET /api/websites/{website_id}/funnels/{funnel_id}
From crates/sparklytics-server/src/routes/funnels.rs:33:
const FUNNELS_READ_RATE_LIMIT : usize = 60 ;
const FUNNELS_MUTATION_RATE_LIMIT : usize = 30 ;
const FUNNELS_RESULTS_RATE_LIMIT : usize = 10 ;
const FUNNELS_SCOPE_READ_RATE_LIMIT : usize = 240 ;
const FUNNELS_SCOPE_MUTATION_RATE_LIMIT : usize = 60 ;
const FUNNELS_SCOPE_RESULTS_RATE_LIMIT : usize = 20 ;
Funnel queries use a concurrency-limited semaphore to prevent resource exhaustion:
let _permit = tokio :: time :: timeout (
Duration :: from_secs ( 1 ),
state . funnel_results_semaphore . acquire (),
)
. await
. map_err ( | _ | AppError :: RateLimited ) ? ;
If all slots are occupied, requests return 429 Too Many Requests with a 1-second timeout.
Best Practices
Start Simple
Begin with 3-4 steps to identify the biggest drop-off points:
{
"name" : "Basic Checkout" ,
"steps" : [
{ "match_type" : "page" , "match_value" : "/cart" },
{ "match_type" : "page" , "match_value" : "/checkout" },
{ "match_type" : "event" , "match_value" : "purchase" }
]
}
Use Descriptive Labels
Make reports easier to understand:
{
"label" : "View Product Page"
}
// Better than:
{
"match_value" : "/products/*"
}
Segment by Device
Compare mobile vs desktop conversion rates:
# Mobile
GET .../results?filter_device=mobile
# Desktop
GET .../results?filter_device=desktop
Filter by UTM parameters to compare campaign effectiveness:
GET .../results?filter_utm_campaign=spring_sale
Common Use Cases
SaaS Signup Funnel
{
"name" : "Trial Signup" ,
"steps" : [
{ "match_type" : "page" , "match_value" : "/pricing" , "label" : "View Pricing" },
{ "match_type" : "event" , "match_value" : "trial_start" , "label" : "Start Trial" },
{ "match_type" : "event" , "match_value" : "onboarding_complete" , "label" : "Complete Onboarding" },
{ "match_type" : "event" , "match_value" : "first_project" , "label" : "Create First Project" }
]
}
E-commerce Checkout
{
"name" : "Purchase Flow" ,
"steps" : [
{ "match_type" : "event" , "match_value" : "add_to_cart" , "label" : "Add to Cart" },
{ "match_type" : "page" , "match_value" : "/cart" , "label" : "View Cart" },
{ "match_type" : "page" , "match_value" : "/checkout" , "label" : "Start Checkout" },
{ "match_type" : "event" , "match_value" : "purchase" , "label" : "Complete Purchase" }
]
}
Content Engagement
{
"name" : "Blog to Newsletter" ,
"steps" : [
{ "match_type" : "page" , "match_value" : "/blog" , "label" : "Read Blog" },
{ "match_type" : "event" , "match_value" : "newsletter_form_view" , "label" : "View Form" },
{ "match_type" : "event" , "match_value" : "newsletter_subscribe" , "label" : "Subscribe" }
]
}
Next Steps
Retention Analysis Track long-term user retention with cohort analysis
Journey Analysis Explore all paths users take through your site