Skip to main content
GET
/
v0
/
funnel
Get multi-step conversion funnel
curl --request GET \
  --url https://api.formo.so/v0/funnel \
  --header 'Authorization: Bearer <token>'
import requests

url = "https://api.formo.so/v0/funnel"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.text)
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

fetch('https://api.formo.so/v0/funnel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://api.formo.so/v0/funnel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"net/http"
"io"
)

func main() {

url := "https://api.formo.so/v0/funnel"

req, _ := http.NewRequest("GET", url, nil)

req.Header.Add("Authorization", "Bearer <token>")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://api.formo.so/v0/funnel")
.header("Authorization", "Bearer <token>")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.formo.so/v0/funnel")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
{
  "meta": [
    {
      "name": "step",
      "type": "UInt64"
    },
    {
      "name": "event",
      "type": "String"
    },
    {
      "name": "users",
      "type": "UInt64"
    },
    {
      "name": "total",
      "type": "UInt64"
    },
    {
      "name": "conversion_from_start",
      "type": "Nullable(Float64)"
    },
    {
      "name": "conversion_from_previous",
      "type": "Nullable(Float64)"
    },
    {
      "name": "dropoff_from_previous",
      "type": "Nullable(Float64)"
    },
    {
      "name": "median_seconds_from_previous",
      "type": "Nullable(Float64)"
    },
    {
      "name": "median_seconds_from_start",
      "type": "Nullable(Float64)"
    }
  ],
  "data": [
    {
      "step": 1,
      "event": "page::0",
      "users": 1240,
      "total": 1240,
      "conversion_from_start": 1,
      "conversion_from_previous": null,
      "dropoff_from_previous": null,
      "median_seconds_from_previous": null,
      "median_seconds_from_start": null
    },
    {
      "step": 2,
      "event": "connect::1",
      "users": 482,
      "total": 482,
      "conversion_from_start": 0.3887,
      "conversion_from_previous": 0.3887,
      "dropoff_from_previous": 0.6113,
      "median_seconds_from_previous": 38,
      "median_seconds_from_start": 38
    },
    {
      "step": 3,
      "event": "swap::2",
      "users": 187,
      "total": 187,
      "conversion_from_start": 0.1508,
      "conversion_from_previous": 0.388,
      "dropoff_from_previous": 0.612,
      "median_seconds_from_previous": 124,
      "median_seconds_from_start": 162
    }
  ],
  "rows": 3,
  "rows_before_limit_at_least": 3
}
{
"error": {
"message": "<string>",
"doc_url": "<string>",
"param": "<string>",
"details": {}
}
}
{
"error": {
"message": "<string>",
"doc_url": "<string>",
"param": "<string>",
"details": {}
}
}
{
"error": {
"message": "<string>",
"doc_url": "<string>",
"param": "<string>",
"details": {}
}
}

Authorizations

Authorization
string
header
required

Workspace API key (e.g. formo_xxx). Create one in the Formo dashboard under Team Settings > API Keys.

Query Parameters

date_from
string<date>
required

Inclusive ISO date for the start of the funnel window (YYYY-MM-DD). The events scan extends past date_to by window_seconds so a user who fires step 1 just before date_to can still complete the funnel inside their conversion window.

date_to
string<date>
required

Inclusive ISO date for the end of the start-event window (YYYY-MM-DD).

steps
string
required

JSON-encoded array of 2 to 10 step specs. Each step is {type, event, name, filters?: [{operand, operator, value}]}. type is the event type (e.g. event for page views, track for custom events, transaction, signature, decoded_log). event is the event name. name is the unique step id (use "<event>::<index>" to disambiguate repeated events).

Filter operators: equals, notEquals, in, notIn, gt, lt, gte, lte, startsWith, endsWith, includes. For in/notIn, pass the values as a |-separated string in value (e.g. "ethereum|polygon|base").

Standard columns (rendered via direct column access): origin, device, browser, os, location, referrer, direct, ref, utm_source, utm_medium, utm_campaign, utm_content, utm_term, builder_codes, version, locale, timezone, page_path. Anything else is treated as a JSON property and read from properties via JSONExtractString (or JSONExtractFloat for numeric comparators).

window_seconds
integer
default:1209600

Conversion window length in seconds. Defaults to 1,209,600 (14 days). For closed funnels this is the in-order completion cap; for both variants the events scan is extended by this amount past date_to.

Required range: x >= 1
funnel_type
enum<string>
default:closed

closed (default): ordered, in-window. open: unordered per-step; emits an extra dropped_off_users column.

Available options:
closed,
open
group_by
enum<string>

Optional dimension to break each step down by. Defaults to first-touch attribution; pass attribution=last_touch to bucket by each user's latest value. When set, the response gains a breakdown column.

Available options:
device,
browser,
os,
location,
referrer,
ref,
utm_source,
utm_medium,
utm_campaign,
utm_content,
utm_term,
builder_codes
limit
integer
default:5

Top-N breakdown categories to keep (by user count), used only with group_by. Remaining categories are bucketed as Others. Defaults to 5.

Required range: x >= 1
attribution
enum<string>
default:first_touch

Per-user attribution for the group_by dimension. first_touch (default) buckets each user by their earliest value; last_touch by their latest. Ignored unless group_by is set.

Available options:
first_touch,
last_touch

Response

Per-step funnel results

Analytics endpoint response. The data array contains the rows; the exact row shape depends on the endpoint. meta carries column type information for rendering, rows is the row count, and statistics holds query timing metadata.

data
object[]
meta
object[]
rows
integer
rows_before_limit_at_least
integer
statistics
object