Skip to content

Sync project data ​

Keep a copy of a project's funnel users and their subscriptions up to date in your warehouse, CRM or internal tool. This guide uses an API key so the sync job reaches only the projects and data you allow.

Run the sync from a backend only. Never put a key in browser, funnel or mobile code.

1. Create an API key ​

In Organization Settings → API Keys → Create key, give the key a name, choose All projects or the projects to sync, and grant:

  • Funnels · read (funnels:read)
  • Experiments · read (experiments:read), if you want experiments
  • Funnel users · read (funnel_users:read)
  • Subscriptions · read (subscriptions:read)

Copy the fg_key_… value once and store it in your backend secrets. See Authentication for how keys and scopes work.

2. Discover projects and funnels ​

List the projects the key can use, then the funnels of each project. Every request except GET /projects names its project with projectId.

sh
curl "https://sdk-api.funnelsgrove.com/integration/v1/projects" \
  -H "Authorization: Bearer <api-key>"

curl "https://sdk-api.funnelsgrove.com/integration/v1/funnels?projectId=<project-id>" \
  -H "Authorization: Bearer <api-key>"

Store funnel id, slug and name: funnel-user records in a project-wide sync carry only the funnel id. Funnel updatedAt changes on rename, publish and production-URL changes, so you can refresh this table on a slow schedule.

List experiments the same way if you report on them. It returns variants and traffic splits, not results or per-user assignments.

3. Load funnel users incrementally ​

List funnel users without funnelId returns every funnel of the project in updatedAt order, oldest first. Add updatedSince to fetch only users changed since a moment, and follow nextCursor until it is null.

sh
curl "https://sdk-api.funnelsgrove.com/integration/v1/funnel-users/list?projectId=<project-id>&updatedSince=2026-10-20T00:00:00Z&limit=100" \
  -H "Authorization: Bearer <api-key>"
sh
curl "https://sdk-api.funnelsgrove.com/integration/v1/funnel-users/list?projectId=<project-id>&updatedSince=2026-10-20T00:00:00Z&limit=100&cursorUpdatedAt=2026-10-20T10:00:00.123456%2B00:00&cursorId=<funnel-user-id>" \
  -H "Authorization: Bearer <api-key>"

Each item is a funnel user: identity, answers, metadata, UTM attribution and subscription status. Pages hold up to 100 users. Encode + in timestamps as %2B.

A user's updatedAt changes when their profile, answers, email or login change, and after every payment, refund and subscription change. Chargebacks do not change it. Background maintenance can also change it, so expect some users to repeat.

4. Load subscriptions incrementally ​

List subscriptions works the same way: updatedSince, nextCursor, oldest first. Join each subscription to its user with funnelUserId. A cancellation, renewal or status change moves the subscription into your next updatedSince window.

sh
curl "https://sdk-api.funnelsgrove.com/integration/v1/subscriptions/list?projectId=<project-id>&updatedSince=2026-10-20T00:00:00Z&limit=100" \
  -H "Authorization: Bearer <api-key>"

5. Run the loop ​

  1. For users and for subscriptions, request pages until nextCursor is null, upserting each item by id.
  2. Store the largest updatedAt you processed for each list.
  3. Start the next run a few minutes before it. Timestamps are taken when a write starts, so a slow write can commit with an earlier updatedAt than a faster one you already saw.
  4. De-duplicate by id; the overlap and background maintenance make repeats normal.

The first run, without updatedSince, walks the whole project.

Look up one user ​

To refresh a single record, get the funnel user by user_id, email or funnelUserId:

sh
curl "https://sdk-api.funnelsgrove.com/integration/v1/funnel-users?projectId=<project-id>&funnelId=<funnel-id>&user_id=<user-id>" \
  -H "Authorization: Bearer <api-key>"

Limits and errors ​

The API allows 120 requests per minute per IP address; on 429, wait for Retry-After. A 403 means the key lacks a scope or the project is outside its Project Access, and a 400 means projectId is missing. See Errors.

To react to changes in real time instead of polling, receive project webhooks.

Build. Launch. Grow.