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).
Import or instrument
Section titled “Import or instrument”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.
What an import can and cannot recover
Section titled “What an import can and cannot recover”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 nofuncormodule. - 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
streamflag 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.
Supported sources
Section titled “Supported sources”| 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.
Connect a source
Section titled “Connect a source”-
Open Imports
Section titled “Open Imports”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). -
Select Connect
Section titled “Select Connect”Choose Connect and select Langfuse, LangSmith, Braintrust, Phoenix or Portkey.
-
Fill in the source fields
Section titled “Fill in the source fields”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 settingscheduled_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 anhttp://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). -
Optionally select Test connection
Section titled “Optionally select Test connection”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. -
Select Connect
Section titled “Select Connect”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).
How ongoing sync works
Section titled “How ongoing sync works”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_scopeandimport_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,nextRunTextcomment).
Verify the first import
Section titled “Verify the first import”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.
Troubleshooting
Section titled “Troubleshooting”| 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) |
Appendix: the relay for self-hosters
Section titled “Appendix: the relay for self-hosters”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.
Shared setup
Section titled “Shared setup”-
Create an ingest key
Section titled “Create an ingest key”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.
-
Install the relay
Section titled “Install the relay”Install
metergraphrelaywith Python 3.10 or newer (metergraphrelay/pyproject.toml,requires-python).pip install metergraphrelay -
Write a
Section titled “Write a .env file”.envfile.env METERGRAPH_APP_TOKEN=<your ingest key># Only for a Metergraph you operate yourself.# METERGRAPH_INGEST_URL=https://metergraph.internal.example.comNever commit
.env.
Backfill: pull, then push
Section titled “Backfill: pull, then push”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.jsonlmetergraphrelay 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.
Scheduled backfill: sync
Section titled “Scheduled backfill: sync”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).