Limits
Eventkiwi applies two independent limits: a monthly event allowance tied to your plan, and a request rate limit that protects the service from bursts. They are enforced separately, so it is possible to be within one and blocked by the other.
Plan Limits
Every account has an event allowance. The free allowance is a one-time trial, not a monthly one — this is the most common surprise, so it is worth being precise about.
| Plan | Event allowance | Resets? | Price |
|---|---|---|---|
| Free | 100 events total | Never — one-time trial | $0 |
| Pro | 10,000 events | Every billing period | $19/mo |
| Business | 50,000 events | Every billing period | $59/mo |
The free trial is one-time
The 100 free events are counted for the lifetime of your account. They are not 100 per month, and they do not roll over or refill. Once you have sent 100 events, further events are rejected until you upgrade to a paid plan.
The trial is sized to let you wire up an aggregator, send real events, and receive an actual report — enough to judge whether Eventkiwi is useful to you.
Paid plans reset each period
On Pro and Business the allowance refills at the start of each billing period.
The periodStart and periodEnd fields in the error response tell you exactly
when.
What happens when you run out
Events sent after the allowance is used up are rejected with a 403. Nothing
you have already collected is lost, and your aggregators, schedules and channels
are left untouched — reporting continues on whatever data you had. We also send
you a one-off email the first time you hit the limit, so it does not fail
silently.
Plan Limit Exceeded Response
Exceeding your monthly allowance returns 403 Forbidden:
{
"error": "Plan limit exceeded",
"currentCount": 100,
"limit": 100,
"remaining": 0
}
periodStart and periodEnd are included on paid plans, which reset each
billing period. They are absent on the free trial, which has no period.
The same values are returned as headers:
| Header | Description |
|---|---|
X-PlanLimit-Current | Events used in the current period |
X-PlanLimit-Limit | Total events allowed per period |
X-PlanLimit-Remaining | Events remaining in the current period |
What to do:
- On the free trial, upgrade to a paid plan — the trial does not refill
- On a paid plan, check
periodEndto see when your allowance resets - Send only the events you actually report on, rather than every event you have
Checking your usage
Your current usage is shown on your dashboard and on the Account → Plans page in the app. Free trial accounts show the total used against the one-time 100; paid accounts also show the date the allowance renews.
Rate Limits
API Request Rate
5 requests per second per user account
This limit applies to all API endpoints, including event submission (/api/event).
How It Works
The rate limit uses a sliding window algorithm:
- Limit resets continuously over a 1-second window
- If you exceed the limit, requests are rejected until the window slides forward
- The limit applies per user account (not per aggregator)
Rate Limit Headers
Every API response includes rate limit information in the headers:
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 4
X-RateLimit-Reset: 1640995201
| Header | Description |
|---|---|
X-RateLimit-Limit | Total requests allowed per second |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the limit resets |
Rate Limit Exceeded Response
When you exceed the rate limit, you'll receive a 429 Too Many Requests response:
{
"error": "Rate limit exceeded",
"limit": 5,
"remaining": 0,
"resetTime": 1640995201
}
What to do:
- Wait until the reset time
- Implement exponential backoff in your code
- Reduce request frequency
- Contact us if you need higher limits
Other Limits
Event Name Length
Event names must be URL-compatible and not cause HTTP 414 (URL Too Long) errors.
Practical limit: Keep event names under 200 characters.
Tag Limits
- Maximum tags per event: 50
- Maximum tag key length: 100 characters
- Valid characters: Letters, numbers, hyphens, underscores (after normalization)
Tags exceeding the length limit are silently ignored. An event carrying more than 50 tags is rejected as invalid.
Aggregator Limits
There are no hard limits on the number of aggregators you can create per account.
Best Practices
1. Batch Events When Possible
Instead of sending events immediately, batch them:
// Instead of this (5+ requests/sec):
for (const event of events) {
await sendEvent(event.name);
}
// Do this (controlled rate):
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
for (const event of events) {
await sendEvent(event.name);
await delay(250); // 4 requests/second
}
2. Implement Retry with Exponential Backoff
Handle rate limit errors gracefully:
async function sendEventWithRetry(name, tags, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(
`https://app.eventkiwi.com/api/event?_id=${AGGREGATOR_ID}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name, ...tags })
}
);
if (response.status === 429) {
// Rate limited - wait and retry
const data = await response.json();
const waitTime = (data.resetTime * 1000) - Date.now();
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}
return await response.json();
} catch (error) {
if (i === maxRetries - 1) throw error;
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, i) * 1000)
);
}
}
}
3. Monitor Rate Limit Headers
Check remaining requests before sending:
async function sendEvent(name, tags) {
const response = await fetch(/* ... */);
const remaining = parseInt(
response.headers.get('X-RateLimit-Remaining')
);
if (remaining < 2) {
console.warn('Approaching rate limit, slowing down...');
await new Promise(resolve => setTimeout(resolve, 1000));
}
return response.json();
}
4. Use Queues for High-Volume Applications
For applications with bursts of events:
class EventQueue {
constructor() {
this.queue = [];
this.processing = false;
}
add(name, tags) {
this.queue.push({ name, tags });
if (!this.processing) {
this.process();
}
}
async process() {
this.processing = true;
while (this.queue.length > 0) {
const event = this.queue.shift();
await sendEvent(event.name, event.tags);
// Wait 250ms between requests (4/sec, safe margin)
await new Promise(resolve => setTimeout(resolve, 250));
}
this.processing = false;
}
}
const eventQueue = new EventQueue();
// Usage
eventQueue.add('user_signup', { 'env-production': true });
Need Higher Limits?
If your application requires higher rate limits, we'd love to hear from you!
Contact Us
- Email: Contact on X/Twitter
- Subject: "Rate Limit Increase Request"
Please include:
- Your account email
- Current usage patterns
- Expected request volume
- Use case description
We'll work with you to find a solution that meets your needs.
Monitoring Your Usage
Your event usage against your plan allowance is shown on your dashboard and on the Account → Plans page, along with the renewal date on paid plans.
To monitor limits from your own application:
- Track rate limit headers (
X-RateLimit-*) to stay under the request rate - Track plan limit headers (
X-PlanLimit-*) to see your remaining allowance - Log 429 and 403 responses to identify which limit you are hitting
Next Steps
- Authentication - Set up API authentication
- Sending Events - Learn how to send events
- Event Tags - Add metadata to events