The API
One endpoint to send events, one to read counts. Keys come from the dashboard; each key posts into one group.
Send an event
POST https://geteventbar.com/api/v1/event/{type}
Authorization: Bearer eb_live_…
Content-Type: application/json
{"uid": "u_8d2", "platform": "ios", "time": "2026-10-10T17:02:11Z"}
{type}isa-z 0-9 _ . -, up to 64 characters, starting with a letter or digit. Examples:new_user,order.paid.- The body is a JSON object of up to 16 KB, or empty. Plain text becomes
{"message": "…"}. - Defined types (you gave them a template in the dashboard) render
{field}placeholders from the body, plus{_type},{_group}and{_time}. A missing field renders empty. - Undefined types work too. The title is the name made readable (
new_user→ "New user"); the body ismessageif present, else the first six fields askey: value. A group keeps counts for up to 20 undefined names at a time. time, if present as ISO 8601 or epoch seconds or milliseconds, is the event's displayed time. Counts always use arrival time.- An
Idempotency-Keyheader, or anidfield in the body, makes a repeat within 24 hours free: the first answer comes back and nothing is counted twice.
Answers
| Status | Body | Meaning |
|---|---|---|
| 202 | {"ok":true,"id":"01J…","defined":true} | Counted and delivered. duplicate: true when the idempotency key matched. |
| 202 | {"ok":true,"delivered":false,"reason":"plan"} | The group or type is paused by the plan (after a downgrade). Nothing counted. |
| 400 | {"error":"type"} | The type name is not allowed. |
| 401 | {"error":"unauthorized"} | Unknown or revoked key. |
| 413 | {"error":"too_large"} | Body over 16 KB. |
| 429 | {"error":"rate"} | More than 60 requests a minute on this key. Retry after a moment. |
| 429 | {"error":"quota","resetsAt":"…"} | The month's events are used up. Counting resumes on the first. |
| 503 | {"error":"unavailable"} | Our side. Retry with backoff. |
Read counts
GET https://geteventbar.com/api/v1/stats Authorization: Bearer eb_live_…
Returns the group the key belongs to and every type in it with hour, day, week, month and all counts, plus the month's usage. Hour and day are to the minute; week and month are UTC days including today. Signed-in apps call the same endpoint with their device token and see the whole account.
{
"plan": "plus",
"groups": [{"id": "01J…", "name": "EulerPad", "paused": false}],
"types": [{"group": "01J…", "groupName": "EulerPad", "type": "new_user", "defined": true, "label": "New user",
"paused": false, "hour": 3, "day": 41, "week": 212, "month": 890, "all": 4120,
"firstAt": 1759000000000, "lastAt": 1760100000000}],
"usage": {"month": "202610", "events": 1320, "limit": 5000, "resetsAt": "2026-11-01T00:00:00.000Z"}
}
Examples
Python
import requests
requests.post("https://geteventbar.com/api/v1/event/new_user",
headers={"Authorization": f"Bearer {EVENTBAR_KEY}"},
json={"uid": user.id, "platform": "ios"}, timeout=5)
Node
await fetch("https://geteventbar.com/api/v1/event/order.paid", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.EVENTBAR_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ id: order.id, amount: order.total, message: `${order.total} from ${order.country}` }),
});
Shell, from a cron job
curl -sS -X POST https://geteventbar.com/api/v1/event/backup_done \ -H "Authorization: Bearer $EVENTBAR_KEY" -d "message=$(du -sh /backups | cut -f1) backed up"
Fire and forget: a failed post should never break your app. Use a short timeout and ignore errors.
Limits
| Free | Plus | Pro | |
|---|---|---|---|
| Event types (defined) | 3 | 25 | 250 |
| Events a month | 100 | 5,000 | 100,000 |
| Groups | 1 | 20 | 100 |
| Offline buffer per device | none | 10 days / 250 | 30 days / 1,000 |
| Devices | 2 | 5 | 10 |
| API keys | 1 | 10 | 50 |
Every key: 60 requests a minute, 16 KB a body. Months are calendar months in UTC.