Skip to main content
Google BigQuery is a serverless cloud data warehouse. OpenRouter can stream traces directly into a BigQuery table for custom analytics, long-term storage, and business intelligence. Each request becomes one row, so you can sum tokens and costs without filtering by observation type. See rows and aggregation for details.

Step 1: Choose a project and enable the BigQuery API

  1. Open the Google Cloud Console and create or select a project.
  2. Copy the project ID (not the project name). It is the lowercase identifier shown in IAM & Admin > Settings.
  3. In APIs & Services > Library, search for BigQuery API and click Enable.

Step 2: Create the dataset

  1. Open BigQuery > Explorer, select the project, and choose Create dataset.
  2. Choose a dataset ID, such as openrouter.
  3. Choose the dataset location carefully. A dataset’s region cannot be changed after creation, so pick a location allowed by your data-residency requirements.
BigQuery Datasets

Step 3: Create the traces table

Create the openrouter_traces table in your dataset. You can find the exact SQL in the OpenRouter dashboard when configuring the destination — click View Setup Instructions. Replace my-gcp-project in the DDL with your project ID (and the dataset or table IDs if you chose different ones), then run it in the BigQuery SQL workspace:
BigQuery Table Setup
Once created, the table appears in your dataset:
BigQuery Dataset Tables

Step 4: Create a service account

  1. Open IAM & Admin > Service Accounts in the project and click Create service account (e.g., openrouter-broadcast).
  2. Grant it the BigQuery Data Editor role on the trace dataset (not at the organization or project level): open the dataset’s menu in BigQuery, choose Share > Permissions, add the service account email, and select BigQuery Data Editor. Do not grant BigQuery Job User — this destination uses streaming inserts and does not create query jobs.
  3. Open the service account’s Keys tab, choose Add key > Create new key, select JSON, and download the key.
Keep the downloaded key private. It contains a private signing key and should not be committed to source control.

Step 5: Enable Broadcast in OpenRouter

Go to Settings > Observability and toggle Enable Broadcast.
Enable Broadcast

Step 6: Configure BigQuery

Click the edit icon next to Google BigQuery and enter:
BigQuery Configuration
  • Google Cloud project ID: The project ID containing the dataset.
  • Service-account key JSON: The complete contents of the downloaded JSON key file.
  • BigQuery dataset: The dataset ID created above (default: openrouter).
  • BigQuery table: The table ID created above (default: openrouter_traces).

Step 7: Test and save

Click Test Connection to verify the setup. The connection test reads the table’s metadata to verify the project, dataset, table, and credentials, then checks that the credentials hold the row-insert permission, so read-only access fails the test rather than failing on every later trace. The configuration only saves if the test passes.

Step 8: Send a test trace

Click Send Trace, or make an API request through OpenRouter, and query your BigQuery table to verify the trace was received:
BigQuery Table Preview

Example queries

Cost analysis by model

User activity analysis

Error analysis

Provider performance comparison

Usage by API key

Accessing JSON columns

The attributes, input, output, metadata, model_parameters, and resource_attributes columns are JSON typed. Use BigQuery’s JSON functions to query nested fields:
To parse input messages:

Schema design

The dashboard-generated CREATE TABLE statement is the source of truth for setup. The field reference below describes the current schema without duplicating the generated SQL.

Field reference

Field ownership and custom metadata

OpenRouter populates all columns except for the following caller-controlled values:
  • trace_id can be set with trace.trace_id. If supplied, the same trace ID can span multiple API requests.
  • parent_span_id can be set with trace.parent_span_id.
  • user_id comes from the request user field.
  • session_id comes from the request session_id field.
  • Custom values are stored in the metadata JSON column.
Additional keys in trace are merged into metadata using their original keys. Top-level request metadata.<k> values are also placed in metadata, under the key metadata_<k>. There is no separate database column for each custom key. trace_name and generation_name affect internal trace and observation names but have no dedicated table columns. trace.span_name is accepted as a reserved request field but is currently ignored.

Rows and aggregation

Unlike the Snowflake and ClickHouse destinations, BigQuery receives one row per generation, not one row per observation. Each API request produces a single GENERATION row:
  • span_id is the generation ID.
  • parent_span_id is NULL unless the request set trace.parent_span_id.
  • Per-attempt provider detail is in metadata.provider_responses, and phase timings are in the openrouter_* fields of attributes.
The moderation, provider-attempt, and post-generation spans aren’t written as separate rows, so you can sum tokens and costs without filtering on span_type. If you set trace.trace_id across requests, several rows share one trace_id, so count requests with span_id rather than trace_id. BigQuery deduplicates streaming inserts on (trace_id, span_id) on a best-effort basis only, so a retried insert can still land a duplicate row. Deduplicate on (trace_id, span_id) to remove duplicates. Rows that BigQuery permanently rejects, or that still fail after transient retries, are dropped rather than redelivered, so deduplication can’t recover missing rows.

Typed columns

The field reference above identifies commonly queried identifiers, timestamps, model information, token metrics, and cost metrics as typed columns for efficient filtering and aggregation.

JSON columns

Less commonly accessed and variable-structure data is stored in the JSON columns identified in the field reference above. The tags column is a repeated STRING column (ARRAY<STRING>). Use BigQuery’s JSON_VALUE and JSON_QUERY functions to query the JSON fields.

Custom Metadata

Custom metadata from the trace field is stored in the metadata JSON column. You can query it using BigQuery’s JSON functions.

Supported Metadata Keys

Example

Querying Custom Metadata

Additional Context

  • The user field maps to the user_id typed column
  • The session_id field maps to the session_id typed column
  • Additional custom keys from trace are stored in the metadata JSON column for flexible querying
  • trace_name and generation_name affect internal names but have no dedicated table columns
  • trace.span_name is reserved and currently ignored

Troubleshooting

  • Project not found or permission denied: Confirm that the configured project ID is the project containing the dataset and that the service account belongs to the expected project.
  • Table not found: Confirm the dataset and table IDs and that the table was created in the configured dataset location.
  • 403 permission denied: Grant the service account BigQuery Data Editor on the dataset. Project-level access may be restricted by organization policy, so verify the dataset permission directly.
  • 400 invalid or schema mismatch: Compare the table schema with the DDL from the setup instructions. In particular, timestamps must be TIMESTAMP, nested trace fields must be JSON, and tags must be ARRAY<STRING>.

Additional resources

Privacy Mode

When Privacy Mode is enabled for this destination, prompt and completion content and provider error text are excluded from traces. All other trace data — token usage, costs, timing, model information, and custom metadata — is still sent normally. See Privacy Mode for details.