- The project ID, which is part of every request path. Connect delivers it beside the key; the
projectIdplugin option overrides it. - The key itself, sent bare in the
Authorizationheader. Keen adds noBearerscheme, and a key with one is rejected.
- A read key cannot write, and a write key cannot read. Keen answers 403 when the key’s kind does not permit the call. The plugin cannot tell which kind arrived, so a 403 usually means the key is the wrong kind rather than the wrong key.
- Events take a moment to be queryable. Keen acknowledges
events.recordat once but says it can take up to 10 seconds before the event appears in a query. events.recordManypartly succeeds. It answers one result per event, keyed by collection, and an entry withsuccess: falsewas not stored. Keen recommends batches of 5,000 or less.- Queries are POSTs but read nothing back into Keen.
queries.runandqueries.multiAnalysissend the analysis as a body. Because the method is POST, they are not replayed after a 5xx; a count that failed mid-way is safe to send again, and the caller can. - A query has five minutes. Past that Keen stops it and answers 504
QueryIncompleteError. The plugin raisesisQueryIncompleteand does not retry, because the same query would take as long again; narrow the timeframe instead. - Every analysis but
countandfunnelneeds atarget_property, andpercentilealso needs itspercentilevalue. Afunneltakessteps, each with its ownevent_collectionandactor_property, instead of a top-level collection. These are checked before the request, so a malformed analysis costs no call. - Cached results. A saved query’s
refresh_rateis0(no cache) or 14400 to 86400 seconds (four to 24 hours).savedQueries.resultreads the cached answer, which is what makes a dashboard load instantly. - Datasets page by cursor.
datasets.listreturnsnext_after_name, read out of Keen’snext_page_url; pass it back asafter_name. - Rate limits are per project, not per key. A 429 blocks that endpoint category for the next minute, and Keen sends no
Retry-After, so the plugin waits a minute before its next attempt. - Webhooks. The plugin does not receive Keen webhooks; none are configured here.
- 16 typed API operations
Setup
1
Install
2
Add the plugin
corsair.ts
multiTenancy: true, scope calls with corsair.withTenant(id). See Quick start for KEK + Hub keys, and Multi-tenancy for account isolation.3
Choose authentication
- API Key (Recommended)
Store the Keen API key before the first call: More: API Key
pnpm corsair setup --plugin=keen --tenant=acme api_key=<your-key> (leave out --tenant without multiTenancy), or pass it as key in the plugin options. With Hub, each tenant enters their own key on the connect page. See API key auth.4
Connect a tenant
Mint a connect link and send the tenant to it. Hub hosts the page and delivers the result to your app — see Connect / OAuth.
Example API calls
Count purchases and sum revenue in the last weekWhat’s next
API reference
Every
keen.api.* operation with input and output types.Connect / OAuth
createLink, Hub delivery, and tenant connect flows.
Use with agents
Expose this plugin’s operations as MCP tools.