Skip to content

Importing existing traffic

If your LLM calls are already recorded in Langfuse, LangSmith, Braintrust, Phoenix or Portkey, connect that tool on the Imports page. Metergraph pulls the history you choose and then keeps level with the source on a schedule. Nothing is installed and your application is not touched.

Imports is in the dashboard sidebar. It is available on hosted Metergraph and on the local deployment profile, not on BYOC core (capabilities.py, scheduled_imports). Any workspace role can view it. Owners and members can connect, test, pause, resume, sync and remove imports; viewers cannot (api/import_connections.py).

Importing gives you the history the source kept. Instrumenting captures new calls with application context.

Import Install the SDK
Code change None One init() call
History Everything the other tool kept New calls only
Function attribution No Yes
Route names Only if the source recorded one Yes, inferred or declared
Streaming and time to first token No Yes
Recorded outcomes No Yes, via record_outcome()
Content controls None on most paths Per field, per call

The two are not exclusive. You can import history from an existing tool and install the SDK for new traffic.

The hosted importer and metergraphrelay use the same query builders and row normalizers. Only the hosted HTTP fetch is different, so the field behavior described here applies to both paths (app/pyproject.toml, metergraphrelay>=0.6.0; import_providers/*.py module docstrings).

An imported row is only as good as what the source tool stored. The importer never invents a value it did not read.

What comes across on most paths. Timestamp, provider, model, input and output token counts, status, error type, the source’s own request id, trace and span ids, and the prompt and response text the source held. Cache and reasoning token buckets come across wherever the source recorded them.

What does not come across, on any path:

  • Function attribution. The SDK records the module and function that made the call at capture time (metergraph/_capture.py:621-652). A source export does not contain that information, so imported rows have no func or module.
  • Route names, unless the source recorded one. Each source page describes the route the source provides and its fallback. The hosted import has no route override. The relay appendix can override the route with --route.
  • Streaming detail. No stream flag and no time to first token.
  • Batch metadata, template hashes, and unit counts.
  • Recorded outcomes. Outcomes are written by your application through the SDK, so a trace export has nothing to import. See Record real outcomes.
  • Repository identity, so imported rows do not link back to a commit.

Latency is per source. Braintrust, LangSmith, Phoenix and Portkey carry it. Langfuse does not, because its import reads the observation start time only.

Cost is also per source. Where the source computed a cost, that figure is carried across as reported. Where it did not, the row arrives unpriced and Metergraph prices it from its own model catalog at read time.

Source Page Hosted page Relay
Langfuse Langfuse yes yes
LangSmith LangSmith yes yes
Braintrust Braintrust yes yes
Phoenix Phoenix yes yes
Portkey Logs Export API Portkey yes yes
Portkey export file Portkey no yes
OpenAI stored completions OpenAI no yes

The hosted page offers Portkey, Langfuse, Braintrust, Phoenix and LangSmith (import_connections.py, SOURCE_POLICIES). OpenAI stored completions and Portkey export files are relay-only.

If you send OpenTelemetry spans today, you may not need an import. The SDK’s span exporter reads the OpenInference and gen_ai.* dialects directly, so those spans can reach Metergraph live. See OTLP spans.

  1. Open Imports in the Metergraph sidebar. After you connect a source, the first run starts within a minute: the worker checks for due connections every 15 seconds (config.py, scheduled_imports_interval_seconds).

  2. Choose Connect and select Langfuse, LangSmith, Braintrust, Phoenix or Portkey.

  3. Enter the source-specific scope and key, the Import from date, the Run interval, and an optional Name. A scope is at most 255 characters (import_connections.py, MAX_SCOPE_LENGTH). A name is at most 120 characters (import_connections.py, MAX_DISPLAY_NAME_LENGTH). Portkey can read its optional workspace id from the key. Langfuse uses a stable project label. Braintrust, Phoenix and LangSmith use a project name or id (imports.js, IMPORT_SOURCES).

    Import from defaults to 7 days ago and cannot be in the future (imports.js, defaultInitialSince). On hosted Metergraph it cannot be more than 90 days back. That floor is the operator setting scheduled_imports_max_backfill_days, default 90, which a self-hosted operator can set anywhere from 1 to 366 days (config.py, scheduled_imports_max_backfill_days).

    Run uses the fixed picker Every minute, Every 5 minutes, Every 15 minutes, Every 30 minutes, Every hour, Every 6 hours or Daily (imports.js, INTERVAL_OPTIONS). The default is Every minute (connect-import-dialog.jsx, emptyForm; import_connections.py, DEFAULT_INTERVAL_SECONDS). Every source has a floor of 1 minute (import_connections.py, CURSOR_MINIMUM_INTERVAL_SECONDS; SOURCE_POLICIES). Every source has a ceiling of one day (import_connections.py, MAX_INTERVAL_SECONDS).

    Leave API base URL blank for a provider’s cloud default. Use the Self-hosted disclosure for another endpoint. Phoenix always requires a base URL because it has no cloud default (import_connections.py, SOURCE_POLICIES). The provider endpoint must be reachable from the public internet over HTTPS: a hosted import refuses an http:// URL, and an explicit port must be 443 or 80. Loopback, private, link-local and metadata addresses, and redirects are refused (import_egress.py, check_url, ALLOWED_PORTS).

  4. Test connection makes one or two short provider calls, each within 6 seconds, to prove the key and scope. The endpoint is rate limited to 6 tests per minute per workspace (import_providers/*.py, TEST_TIMEOUT_SECONDS; api/import_connections.py, TEST_RATE_LIMIT_PER_MINUTE). Connect does not contact the provider, except that Portkey reads the workspace id from a blank workspace field.

  5. Metergraph stores the credential encrypted with a key derived from the operator secret and never returns it through the API. The page shows only the first 4 and last 4 characters (import_connections.py, encrypt_credential, credential_hint). The hosted path pushes rows in-process and does not need an ingest key. An ingest key is needed only for the relay appendix (import_providers/__init__.py, module docstring).

    If the workspace already has a checkpoint for the source and scope, Metergraph resumes from it instead of the start date (api/import_connections.py, create_import_connection).

If the operator has not set METERGRAPH_SCHEDULED_IMPORTS_CREDENTIALS_KEY, the page shows “Scheduled imports are not configured for this deployment yet” and cannot create connections. Hosted Metergraph has this key configured. A self-hosted operator must set it (config.py, scheduled_imports_credentials_key; import_connections.py, credentials_configured; Imports.jsx).

The worker checks for due connections every 15 seconds by default (config.py, scheduled_imports_interval_seconds). A run acquires the next window, imports it, completes it, and repeats until it is caught up or the run budget runs out (import_runner.py, module docstring).

  • Window. Each window contains at most one hour of source time (import_sync.py, MAX_WINDOW_SECONDS).
  • Overlap. Every window after the first reaches 5 minutes behind the checkpoint (import_sync.py, OVERLAP).
  • Deduplication. Each row carries import_source, import_source_scope and import_event_id. A row whose identity is already claimed is dropped, so overlap does not double count (import_identity.py, claim_rows).
  • Lease and claim. The sync lease lasts 15 minutes (import_sync.py, LEASE_TTL). The importer’s connection claim lasts 65 minutes and is renewed while the run is active (import_connections.py, CLAIM_SECONDS).
  • Run budget. The default budget is 50 minutes per invocation. The allowed ceiling is one hour (config.py, scheduled_imports_run_budget_seconds). A run that stops at its budget after completing at least one window is recorded as completed, and the next run continues.
  • Window deadline. Each window has a default deadline of 20 minutes (config.py, scheduled_imports_window_deadline_seconds). A window that outlives it fails on its own, and the connection’s backoff owns the retry.
  • Next run. A successful run is scheduled one interval later. Consecutive failures double the delay, capped at 6 hours or at the interval when that is longer. If a backlog remains, the next run is scheduled at once (import_connections.py, next_run_after; importTimeline.js, nextRunText comment).

On Imports, inspect the connection row and its Run history. The row shows Imported through, Calls in selected range, Rows pushed per run, Success rate and Next run, plus imported windows (import-row.jsx). Its statuses include Scheduled, not run yet, Importing for a duration, Up to date, Behind by a duration, Last run failed, Failed N in a row, Paused, Waiting on the provider and Waiting for another sync (importTimeline.js, connectionStatus).

Then open Traces. Check the provider, model, tokens, status, route and the source timestamp on the first rows.

Symptom Likely cause What to do
Test connection says the key or scope is refused The provider credential, project, workspace or data-plane URL is wrong Check the source fields and run the test again. Provider pages describe the exact credential and region fields
“Scheduled imports are not configured for this deployment yet” The operator has not set METERGRAPH_SCHEDULED_IMPORTS_CREDENTIALS_KEY The operator must set the key before a connection can be created (config.py, scheduled_imports_credentials_key)
Behind by a duration The source has more history than the worker can import on the current schedule Leave the connection enabled. Each run takes another window until it catches up, or use Sync now (import_connections.py, request_sync_now)
Failed N in a row A provider request, window or push failed Fix the error shown in Run history. The delay doubles after consecutive failures and is capped at 6 hours or the interval when that is longer (import_connections.py, next_run_after)
Endpoint refused for a private or loopback address, or for http:// The hosted egress policy blocks the endpoint Use a public HTTPS endpoint. A Phoenix on localhost is relay-only (import_egress.py, check_url)
Waiting on Portkey Portkey is still preparing the export for that window Wait for the export or use Sync now. The next run resumes the export (import_runner.py)
Rows counted twice A relay sync and a hosted connection used the same source and scope Stop one path. The server holds one checkpoint per source and scope (import_sync.py)

Use metergraphrelay when the Metergraph you operate has no credentials key set, when a source is not reachable from the public internet, when you need a one-off file-based backfill before the data leaves the source machine, or when you are importing OpenAI stored completions or Portkey export files. A local Phoenix such as localhost:6006 is relay-only because the hosted page cannot reach it.

  1. Sign in to Metergraph and open Keys. Copy an ingest key. Keep it in a secret manager. The hosted path does not use this key.

  2. Install metergraphrelay with Python 3.10 or newer (metergraphrelay/pyproject.toml, requires-python).

    pip install metergraphrelay
  3. .env
    METERGRAPH_APP_TOKEN=<your ingest key>
    # Only for a Metergraph you operate yourself.
    # METERGRAPH_INGEST_URL=https://metergraph.internal.example.com

    Never commit .env.

pull writes a local JSONL file of Metergraph-shaped rows. Read the file, then push uploads it. Use this for a one-off load or when you want to inspect rows before they leave the machine.

metergraphrelay pull <provider> [selectors] --since 2026-08-01T00:00:00Z --output traces.jsonl
metergraphrelay push traces.jsonl

--output defaults to ./traces.jsonl. -n caps rows, not traces, and defaults to 100 on every provider except OpenAI, where it is 10.

sync pulls the next window and uploads it. Metergraph holds the checkpoint, so a cron job does not need local state.

metergraphrelay sync <provider> --source-scope <stable name> --initial-since 2026-08-01T00:00:00Z [selectors]

--source-scope is a permanent label for the source and selector set. It must not change. --initial-since seeds only the first run and is ignored after a checkpoint exists. Give each selector set its own scope. Do not run relay pull and sync, or a relay sync and hosted connection, over the same scope and range. The hosted and relay paths share the server checkpoint.

For selectors, use the provider page. Relay-only selectors include --trace-name, --tag, --environment, --name, --project and --route, depending on the source. --route is relay-only. A sync command can also take --allow-skipped when you deliberately want to advance past a row the relay cannot normalize.

0 means the window completed, Metergraph was already caught up, or another run held the lease. Any other exit code prints an Error: line to stderr, releases the lease, and leaves the window to retry. A “could not be normalized” error means the source returned a row the relay could not read and the window stays pending. Pass --allow-skipped deliberately to advance past it; once the checkpoint moves, the skipped row is gone. A 422 during acquire means the server lacks the source support or the first run omitted --initial-since.

The relay needs a Metergraph server with migration 0073_import_sync_sources (migrations/0073_import_sync_sources).