For your own tools
Developers
On this page
Read your store from your own tools: your list, your community's members, a course's students, your affiliates and your bookings. Every plan has it. The API reads and changes nothing; to be told when something happens instead, use webhooks.
Keys
Make a key in your studio, under API keys. It is shown once, when you make it, and never again: we keep only a fingerprint of it. A store can have 5 keys; each shows when it was last used, and revoking one refuses the very next request made with it.
Send it on every request:
curl https://nimbuslabsai.com/api/v1/store \
-H "Authorization: Bearer nl_live_…"A key belongs to one store and reads only that store. Keep it on a server, in Zapier or in Make — never in a web page, where anybody who opens the page could copy it. The API sends no CORS headers, so a browser refuses to use one.
Answers, pages and limits
Every answer is JSON. A list comes as data, and a long list comes a page at a time with next: send it back as ?cursor= for the next page, until it is null. A page holds up to 200 rows. Times are ISO 8601, in UTC. Amounts are in the currency's smallest unit — cents for dollars.
Each key may make 120 requests a minute. Past that, the answer is 429 with Retry-After: 60.
401 {"error": "unauthorized", "message": "Send your API key as: Authorization: Bearer nl_live_…"}
429 {"error": "rate_limited", "message": "At most 120 requests a minute per key. Try again in a minute."}
502 {"error": "unavailable", "message": "… could not be read just now. Try again in a moment."}A missing, mistyped and revoked key all get the same 401.
Store
GET /api/v1/store Which store the key reads. The first call to make, to check a key works.
{
"data": { "handle": "harbor", "name": "Harbor Kitchen", "currency": "usd" }
}Products
GET /api/v1/products Every product, in the order your store shows them, drafts included.
{
"data": [
{
"id": "k3j9x2p1ab",
"title": "The 7-Day Reset",
"price": 700,
"currency": "usd",
"kind": "one_time",
"interval": null,
"hidden": false,
"url": "https://nimbuslabsai.com/@harbor/p/k3j9x2p1ab"
}
],
"next": null
}kind is one of one_time, free, membership, course or call. interval is set for a membership.
Leads
GET /api/v1/leads Your list: everybody who asked for something free, bought, or came in with an import — the same rows as the CSV export in your studio. Add ?agreed=true for only those who agreed to hear from you and have not unsubscribed: the ones it is fair to email.
{
"data": [
{
"email": "dana@example.com",
"name": null,
"agreed": true,
"agreed_at": "2026-09-12T14:03:22.000Z",
"unsubscribed": false,
"unsubscribed_at": null,
"first_at": "2026-09-12T14:03:22.000Z",
"last_at": "2026-09-20T09:41:10.000Z",
"products": ["Free Pantry Guide", "The 7-Day Reset"],
"product_ids": ["p8w2m4q9zz", "k3j9x2p1ab"],
"tags": [],
"source": "free"
}
],
"next": "1840"
}Members
GET /api/v1/members Everybody who has come into your community, as your own member list shows them. 404 with no_community when the store has none.
{
"data": [
{
"email": "dana@example.com",
"name": "Dana Ortiz",
"handle": "danaortiz",
"listed": true,
"announcements": true,
"muted": false,
"removed": false,
"joined_at": "2026-09-14T18:22:05.000Z",
"last_seen_at": "2026-09-29T21:10:44.000Z"
}
],
"next": null
}Students
GET /api/v1/students?product=<id> Everybody who has opened one course, with how far they got, the most recently active first, up to 500. product is the course product's id, from Products.
{
"total": 1,
"data": [
{
"email": "dana@example.com",
"lessons_done": 7,
"lessons_total": 12,
"started_at": "2026-09-15T10:00:00.000Z",
"last_seen_at": "2026-09-29T08:15:31.000Z",
"blocked": false
}
]
}Affiliates
GET /api/v1/affiliates Every affiliate, with what they earned, were paid and are owed — the numbers on your affiliates page.
{
"currency": "usd",
"refunds_checked": true,
"data": [
{
"id": "0123456789ab",
"email": "sara@example.com",
"code": "sara",
"status": "approved",
"applied_at": "2026-09-02T16:40:12.000Z",
"clicks": 214,
"sales": 9,
"earned": 18900,
"paid": 12000,
"owed": 6900,
"payable": 4200,
"waiting": 2700
}
]
}payable is what can be paid today; waiting is still inside the wait you set before a sale can be paid. When refunds_checked is false, Stripe could not be asked about refunds just now and the amounts may be high: read again before paying anyone from them.
Bookings
GET /api/v1/bookings Paid calls and seats in live sessions, each at the time it is booked for now — a booking its buyer moved is at its new time.
{
"data": [
{
"id": "cs_live_a1B2c3…",
"product_id": "c9v7n2k4ww",
"title": "30-minute strategy call",
"email": "dana@example.com",
"name": "Dana Ortiz",
"starts_at": "2026-10-03T15:00:00.000Z",
"ends_at": "2026-10-03T15:30:00.000Z",
"buyer_time_zone": "America/New_York",
"moved": 0,
"amount": 9900,
"currency": "usd",
"answers": [{ "label": "What should we cover?", "value": "Pricing my first course" }]
}
]
}Sales and refunds
Sales, subscriptions and refunds are charged on your own Stripe account, so Stripe's own API is where to read them, with your own Stripe key. To be told about each one as it happens, add a webhook in your studio.