Analytics capacity starts with a simple relationship:
stored events ≈ events per day × retained days
A product sending 80,000 events per day and retaining 90 days will settle near 7.2 million stored events, assuming a steady rate. Growth, seasonality, replays, and late imports all change that number, so treat the estimate as a baseline rather than a ceiling.
Understand what drives usage#
GraphJSON pricing is based on stored events, not the number of charts you create. The current published plan includes up to 5,000 stored events for free; paid usage is $12 per million stored events per month and is calculated from the highest stored-event watermark recorded during the month.
Check the pricing page before making a purchasing decision because pricing can change. Contact hi@graphjson.com when you expect sustained high volume or need contractual capacity commitments.
Four decisions have the largest effect on stored volume:
- Event scope. Track decisions and meaningful state changes, not every internal function call.
- Retention. Keep each collection only as long as its use case requires.
- Environment isolation. Development and CI data count too; give them short retention or separate disposal.
- Replay policy. Backfills and duplicate retries increase stored rows unless you deduplicate at query time.
Forecast by collection#
Estimate each stream separately because a product event and a diagnostic log rarely need the same lifetime.
| Collection | Daily events | Retention | Estimated stored events |
|---|---|---|---|
product_events |
80,000 | 365 days | 29.2 million |
webhook_events |
250,000 | 30 days | 7.5 million |
staging_product_events |
10,000 | 14 days | 140,000 |
Add headroom for peak traffic, imports, and delayed deletion. A useful planning range is the baseline plus your expected peak multiplier, not an arbitrary universal percentage.
Retention changes can take time to affect a monthly high-water mark. Lowering retention after a spike does not retroactively change a watermark that was already recorded.
Use retention as a product decision#
Longer retention is useful only when the additional history changes an analysis.
- Activation and onboarding analysis often needs weeks or months.
- Annual renewal and seasonality analysis may need more than a year.
- Delivery diagnostics may be useful for days, not years.
- Development events are usually disposable.
Before shortening a collection’s retention, inventory the dashboards, alerts, SQL queries, embeds, and scheduled business reviews that depend on older data. Before extending it, document the new question that the history will answer.
Deleting a collection is irreversible and removes all of its events. Export anything you are required to preserve before deletion.
Keep event volume intentional#
One business action should not generate a spray of nearly identical analytics events. Prefer a well-designed completion event over logging every internal step unless those steps have a clear diagnostic purpose.
For example, a server-side invoice_paid event is a better revenue fact than inferring payment from several page views. If both product behavior and delivery internals matter, place them in separate collections with different retention.
Use sampling only for genuinely statistical workloads, never for financial facts or customer-visible totals. When sampling, attach a sample_rate and apply the correction consistently in queries.
Keep payloads lean#
A single GraphJSON event may contain at most 10,000 serialized characters. That is a safety limit, not a target. Large events make review harder and often signal that raw application objects are being logged without a contract.
Prefer:
{
"event": "report_generated",
"report_type": "retention",
"row_count": 18240,
"duration_ms": 731
}
over a full report configuration, user object, request headers, and response body. Record a stable reference such as report_id when details remain available in your system of record.
High-cardinality fields such as request IDs are useful for targeted diagnostics but poor chart splits. Keep them when they support investigation; do not make them the default dimension of every event.
Plan ingestion throughput#
The public API accepts bulk requests of up to 50 events. A workspace is currently guarded at 40 ingestion requests per second. Batching can therefore reduce request overhead substantially, but these limits should not be interpreted as a throughput service-level agreement.
Build a queue-backed sender that:
- batches up to 50 events
- retries transient failures with exponential backoff and jitter
- respects
429responses - records queue depth and oldest-event age
- isolates permanently invalid events
Do not optimize by dropping events silently. When expected peaks approach public limits, contact GraphJSON with the expected events per second, average payload size, peak duration, and recovery requirements.
Budget query complexity too#
Stored-event pricing and query performance are different concerns. Wide time ranges, repeated JSON extraction, high-cardinality grouping, and joins across large collections all increase query work.
Keep interactive dashboards focused:
- select only required fields
- filter the time range early
- avoid unbounded
ORDER BY - cap result sets for charts
- materialize repeated business concepts in clean event properties
The SQL interface limits outer query results to 2,000 rows. Aggregate for visualization instead of returning raw event history.
Monitor leading indicators#
Review these values by collection at least monthly:
- events per day and peak events per minute
- stored events and effective retained days
- average and p95 payload size
- duplicate rate
- delivery retry and rejection rate
- percentage of events missing required IDs
- top event names and unexpected volume changes
An unexpected drop can be as important as a spike. Pair cost monitoring with instrumentation reconciliation so an inexpensive broken pipeline is not mistaken for efficiency.
Planning worksheet#
For every collection, record:
purpose:
owners:
events/day (average and peak):
average serialized bytes/event:
required history:
configured retention:
duplicate policy:
reconciliation source:
expected monthly stored-event range:
Revisit the worksheet when traffic, event scope, or retention changes materially. Capacity planning is most reliable when it is part of instrumentation design, not a billing surprise after launch.
Use Billing and subscription management to compare the current stored-event count with an invoice and manage the subscription portal.