Fetch Workspace Stats
Changed onAggregate statistics over a filtered set of records, without listing the records. Use this for any "how many", "what's the breakdown", or "what's the total" question: it is dramatically cheaper than paging a collection and counting client-side.
One workspace endpoint serves every analyzable resource. Name the resource and request the stats you want as query parameters:
GET /api/v2/workspaces/{workspace_id}/stats?resource=contact&count=true&count_by[property]=is_active&filter[tag_ids]=42
A flag of true computes the stat; an object-style query supplies a stat's parameters. Omit a stat (or pass false) to skip it. Calling with only resource returns a 422 that lists the stats and property values the named resource supports, so the endpoint is self-describing.
Narrowing the set
The same selectors the resource's own index accepts, composed with AND:
- filter - simple field matches, e.g. filter[tag_ids]=42 for contacts
- stable_id - a one-off Refine filter token, e.g. from POST /workspaces/{workspace_id}/contacts/filters
- stored_filter_id - a saved Refine filter id
Omit all three to report on every record of that resource in the workspace.
Beyond the index's own keys, filter takes a created_at window on every resource (created_at_after inclusive, created_at_before exclusive, ISO 8601 only, with an offset's + sent as %2B), and on order the two attribution keys a sales dashboard needs: funnel_id (orders attributed to that funnel) and product_id (orders containing that product, from any channel). Every id filter (id, contact_id, page_id, tag_ids, funnel_id, product_id) takes a numeric id or a public_id, comma-separated for several; an id that is not this workspace's is a 422 naming it, never a silent zero:
GET /api/v2/workspaces/{workspace_id}/stats?resource=order&count=true&sum[property]=total_collected&filter[funnel_id]=41&filter[created_at_after]=2026-10-01T00:00:00Z
Large sets
Every stat is computed over the whole matching set, bounded in time rather than in rows: each query has a statement timeout, and the request has an overall time budget. A stat that cannot finish in time is not returned; it is named in unavailable, with unavailable_reason saying whether to narrow the filter or to ask for it on its own, rather than computed over part of the set.
count has one fallback of its own. Past 100,000 matches, if counting the whole set cannot finish in time, it returns 100,000 with count_capped_at beside it - the real total is higher, so narrow the filter.
When no requested stat can be computed in time, the response is a 422, not a 503: retrying it unchanged fails the same way, so narrow it instead.
Access
Computing a statistic persists nothing, so a token with read-only access may call it. Authorization follows the category of the resource being analyzed: contact requires read access to Contacts, order requires read access to Store, form_submission requires read access to Forms.
Form submissions per page
form_submission reports on the same rows GET /workspaces/{workspace_id}/form_submissions lists: one per form post on a ClickFunnels page. count_by[property]=page_id returns the submission count of every page in one call, so counting a funnel's submissions no longer means paging the whole log:
GET /api/v2/workspaces/{workspace_id}/stats?resource=form_submission&count_by[property]=page_id&filter[page_id]=41,42
These are form posts, not opt-ins: checkout submits and repeat posts by the same contact count too. For opt-ins as the funnel dashboard counts them, read optins from GET /funnels/{funnel_id}/stats or GET /pages/{page_id}/stats.
It has no Refine filter class, so stable_id and stored_filter_id are refused with a 422 for it.
Request
- Base URLhttps://myworkspace.myclickfunnels.com/api/v2
- Auth
- HTTP bearer
- or OAuth 2Authorization codehttps://accounts.myclickfunnels.com/oauth/authorizeToken URLhttps://accounts.myclickfunnels.com/oauth/token
- TypeScriptA function with typed inputs. Its response is checked against the schema.
Path parameters
Query parameters
The resource to report on.
How many records match. Exact unless counting a set of more than 100,000 cannot finish in time - see count_capped_at in the response.
Breaks the matching set down by one allow-listed property, returning a count per distinct value.
{
"property": "is_active"
}The earliest and latest created_at in the matching set. Exact; listed in unavailable instead when it cannot finish in time.
Totals one allow-listed numeric property. Exact; listed in unavailable instead when it cannot finish in time. Not available for every resource.
{
"property": "total_amount"
}Simple field matches. The keys the resource's index accepts - contact: id, email_address, is_active, tag_ids; order: id, contact_id, order_type, billing_status, service_status, live_mode; form_submission: id, page_id, contact_id - plus created_at_after (inclusive) and created_at_before (exclusive, both ISO 8601 only; send an offset's + as %2B) on every resource, and funnel_id and product_id on order. Every id filter takes a numeric id or a public_id; one that is not this workspace's is a 422. Comma-separated values match any of them.
A URL-encoded Refine stable_id token, applied to the set before computing. Same token format the resource's index accepts; for contacts it can come from POST /workspaces/{workspace_id}/contacts/filters. Pass it exactly as returned. Not accepted for form_submission (422).
The id or public_id of a saved Refine filter in this workspace, applied to the set before computing. Must match the resource's filter type. Not accepted for form_submission (422).
Response
OK
Changes
- info
endpoint added
- info