Skip to content
Knowledge base

Set up the errors SDK

Application error tracking: an ingest key, a few lines of SDK setup, and honest drop accounting.

1. Create an errors project

From the realuptime errors dashboard, create a project for each app or service. Each project has its own ingest key.

2. Install the SDK

Install from each SDK's real package registry: npm install @realuptime/errors, pip install realuptime-errors for Python, or gem install realuptime-errors for Ruby. Go is go get github.com/RealUptimeHQ/realuptime-errors-go, which has never needed a separate registry step since a Go module installs straight from a public git repo permanently. PHP still installs from the public mirror (composer require realuptime/errors:dev-main) until Packagist carries a tagged release.

3. Add the ingest key to your app

The key ships inside your client bundle by design; it can do exactly one thing: submit error events for that project. Rotating it in settings revokes the old key, which then refuses loudly (RU-4002) so old deploys log it once and stop.

4. Send a test event before you wire up your app

Click "Send test event" on the project in Errors → Settings; your first issue appears in Issues in seconds. It goes through the exact same ingest pipeline as a real event -- scrubbed, grouped, quota-metered -- so it proves the whole wire, not just that a page loaded. It's a normal issue, labeled with a "test event" chip so you can tell it apart from your app's own errors.

5. Use a framework integration if one fits your stack

The JS SDK ships adapters for Next.js (instrumentation.ts and App Router error hooks), Express, and a generic wrap() for serverless handlers. The Python SDK ships Django, Flask, FastAPI/Starlette, and Celery integrations. The Ruby gem ships a Rails Railtie, plain Rack middleware, and Sidekiq server middleware. The Go module ships net/http middleware and a log/slog handler hook. Every adapter is zero-dependency and feature-detected: it never requires the framework to be installed just to import it, and it shares the same capture, scrub, and delivery path as a plain init() call. Pick your framework from the dropdown on the project creation screen to see its snippet.

6. Send events in batches

The SDK batches events to POST /api/errors/v1/ingest/<key>. A malformed batch is refused whole (RU-4004) and counted, never partially accepted: partial acceptance would fabricate data.

7. Attach who it happened to, and what your app was doing

setUser({ id }) attaches an identity, setTag("tenant", "acme") attaches a label you can filter the issues list by, and setContext("cartTotal", "49.99") attaches free-form state. Python and Ruby use set_user, set_tag and set_context; Go uses SetUser, SetTag and SetContext. All four stick until you change them, and the SDK also reports the runtime it is on (Node, Python, Ruby or Go version, platform, architecture) and never your hostname or IP.

8. Know what gets scrubbed, and how to opt one field back in

A user id rides as you set it. An email or username is replaced with [scrubbed] inside your own process, before the event is serialized, and the issue detail shows that marker rather than hiding the field. If you genuinely need one, name it: allowFields: ["user.email"] in init(), per field, in code. There is no server-side switch, on purpose. Tag and context values pass the same card and token scrub as everything else, so a card number in a tag never reaches us.

9. Stay inside the context size caps

Twenty tags and twenty context entries per event, keys to 64 characters, values to 512, and 4KB across all of it. Going over does not cost you the error report: what fits is kept, the rest is dropped, and the issue says exactly how many entries were dropped. Only a runaway payload (more than 200 entries in one map) is refused, with RU-4009. An event with all this context is still one event against your quota, never more.

10. Watch quota and drops on the dashboard

Every drop (rate-limited RU-4003, over-quota RU-4001, malformed, or client-side) is counted and shown on the dashboard. Nothing disappears silently.

11. Add a custom scrub rule for a field only your app has

The shared defaults already remove card numbers, tokens, JWTs, and long hex/base64 blobs on every project. If your app has its own sensitive field, name it in Errors settings, per project: an exact field name, a key glob like x-internal-*, or a safe-subset value regex. These only add redaction on top of the defaults; there is no setting anywhere that loosens or turns one off. Use "Test a sample payload" in the same panel to see exactly what the defaults plus your rules would redact before you rely on it.

12. Turn on the user feedback dialog (optional)

Off by default, and gated by two switches: turn the project's feedback toggle on under Errors → Settings, and pass init({ feedback: {...} }) (optionally autoShow: true to open it automatically after an unhandled error). The dialog collects a name, email, and comments, scrubs them the same way as everything else, and attaches the report to the exact occurrence. Reports show up on the issue detail page, with a count on the issue's row in the issues list.

Go deeper

The full reference lives in the docs: Getting started documentation. Error codes named above are each explained in the error-code reference.