Claude AI
Port's Claude AI integration allows you to ingest Claude usage, cost, and Claude Code analytics metrics into your software catalog.
Prerequisites
- An Anthropic Admin API key with access to usage and cost reports.
- Your Port user role is set to
Admin.
Setup
Choose one of the installation methods below. If you are not sure which option to use, review the installation methods overview.
Configuration
Port integrations use a YAML mapping block to ingest data from the third-party API into Port.
The mapping uses the JQ JSON processor to transform fields and shape incoming Claude data into Port entities.
Default mapping configuration
This is the default mapping configuration for this integration:
Default mapping configuration (Click to expand)
resources:
- kind: claude-usage-record
selector:
query: .results | length > 0
startingDate: '2026-01-01T00:00:00Z'
bucketWidth: 1d
groupBy: []
port:
entity:
mappings:
identifier: .starting_at[:10] + "-usage"
title: '"Claude Usage - " + .starting_at[:10]'
blueprint: '"claude_usage_record"'
properties:
record_date: .starting_at
uncached_input_tokens: (.results[0].uncached_input_tokens // 0)
output_tokens: (.results[0].output_tokens // 0)
cache_read_input_tokens: (.results[0].cache_read_input_tokens // 0)
cache_creation_5m_tokens: (.results[0].cache_creation.ephemeral_5m_input_tokens // 0)
cache_creation_1h_tokens: (.results[0].cache_creation.ephemeral_1h_input_tokens // 0)
web_search_requests: (.results[0].server_tool_use.web_search_requests // 0)
- kind: claude-cost-record
selector:
query: .results | length > 0
startingDate: '2026-01-01T00:00:00Z'
bucketWidth: 1d
port:
entity:
mappings:
identifier: .starting_at[:10] + "-cost"
title: '"Claude Cost - " + .starting_at[:10]'
blueprint: '"claude_cost_record"'
properties:
record_date: .starting_at
amount: ((.results[0].amount // "0") | tonumber)
currency: (.results[0].currency // "USD")
- kind: claude-usage-record
selector:
query: .results | length > 0
startingDate: '2026-01-01T00:00:00Z'
bucketWidth: 1d
groupBy:
- workspace_id
port:
itemsToParse: .results
itemsToParseTopLevelTransform: false
entity:
mappings:
identifier: (.item.workspace_id // "default") + "-" + .starting_at[:10]
title: (.item.workspace_id // "default") + " - " + .starting_at[:10]
blueprint: '"claude_workspace_usage"'
properties:
record_date: .starting_at
workspace_id: (.item.workspace_id // "default")
uncached_input_tokens: (.item.uncached_input_tokens // 0)
output_tokens: (.item.output_tokens // 0)
cache_read_input_tokens: (.item.cache_read_input_tokens // 0)
cache_creation_5m_tokens: (.item.cache_creation.ephemeral_5m_input_tokens // 0)
cache_creation_1h_tokens: (.item.cache_creation.ephemeral_1h_input_tokens // 0)
web_search_requests: (.item.server_tool_use.web_search_requests // 0)
- kind: claude-usage-record
selector:
query: .results | length > 0
startingDate: '2026-01-01T00:00:00Z'
bucketWidth: 1d
groupBy:
- model
port:
itemsToParse: .results
itemsToParseTopLevelTransform: false
entity:
mappings:
identifier: .item.model + "-" + .starting_at[:10]
title: .item.model + " - " + .starting_at[:10]
blueprint: '"claude_model_usage"'
properties:
record_date: .starting_at
model: .item.model
uncached_input_tokens: (.item.uncached_input_tokens // 0)
output_tokens: (.item.output_tokens // 0)
cache_read_input_tokens: (.item.cache_read_input_tokens // 0)
cache_creation_5m_tokens: (.item.cache_creation.ephemeral_5m_input_tokens // 0)
cache_creation_1h_tokens: (.item.cache_creation.ephemeral_1h_input_tokens // 0)
web_search_requests: (.item.server_tool_use.web_search_requests // 0)
- kind: claude-code-analytics
selector:
query: 'true'
startingDate: '2026-01-01'
port:
entity:
mappings:
identifier: .organization_id + "-" + (.actor.api_key_name // .actor.email // "unknown") + "-" + .date[:10]
title: (.actor.api_key_name // .actor.email // "unknown") + " - " + .date[:10]
blueprint: '"claude_code_analytics"'
properties:
record_date: .date
organization_id: .organization_id
actor_type: .actor.type
actor_name: (.actor.api_key_name // .actor.email // "unknown")
terminal_type: .terminal_type
num_sessions: (.core_metrics.num_sessions // 0)
lines_added: (.core_metrics.lines_of_code.added // 0)
lines_removed: (.core_metrics.lines_of_code.removed // 0)
commits: (.core_metrics.commits_by_claude_code // 0)
pull_requests: (.core_metrics.pull_requests_by_claude_code // 0)
edit_tool_accepted: (.tool_actions.edit_tool.accepted // 0)
edit_tool_rejected: (.tool_actions.edit_tool.rejected // 0)
write_tool_accepted: (.tool_actions.write_tool.accepted // 0)
write_tool_rejected: (.tool_actions.write_tool.rejected // 0)
model_breakdown: .model_breakdown
Advanced selector configuration
queryapplies to all Claude resource kinds and defaults to'true'.startingDateis required forclaude-usage-recordandclaude-cost-record. Forclaude-code-analytics, eitherstartingDateortimeFramemust be provided (mutually exclusive).bucketWidthcontrols time granularity for usage and cost resources.groupByis available forclaude-usage-recordand lets you break down usage by dimensions.
For claude-usage-record, you can use:
selector:
query: 'true'
startingDate: '2026-01-01T00:00:00Z'
bucketWidth: 1d # supported: 1m | 1h | 1d
groupBy: [workspace_id]
startingDatemust use ISO-8601 UTC format (YYYY-MM-DDTHH:MM:SSZ).bucketWidthsupports1m,1h, and1dfor usage records.groupBysupports dimensions such asapi_key_id,workspace_id,context_window,speed,inference_geo,account_id,service_account_id,model, andservice_tier.
For claude-cost-record, use:
selector:
query: 'true'
startingDate: '2026-01-01T00:00:00Z'
bucketWidth: 1d
startingDatemust use ISO-8601 UTC format (YYYY-MM-DDTHH:MM:SSZ).bucketWidthsupports1donly for cost records in this integration.
For claude-code-analytics, the API returns data for one specific day per call. To control the date window, provide exactly one of the following fields:
| Field | Format | Description |
|---|---|---|
timeFrame | number | Number of days to look back from today. The integration calls the API once per day for each of the last N days. Recommended for ongoing syncs. |
startingDate | YYYY-MM-DD | Iterates from this date to today, calling the API once per day. Use for historical backfills. A warning is logged and no data is fetched if the date is in the future. |
timeFrame and startingDate cannot both be set on claude-code-analytics. Providing neither or both will cause a validation error.
Using timeFrame (recommended for ongoing syncs):
selector:
query: 'true'
timeFrame: 30 # fetch the last 30 days, one API call per day
Using startingDate (for a historical backfill):
selector:
query: 'true'
startingDate: '2026-01-01' # iterate from this date to today
Mapping & examples per resource
To view and test the integration mapping against sample API responses, use the jq playground in your data sources page.
Monitoring and sync status
To learn more about how to monitor and check the sync status of your integration, see the relevant documentation.
Migration guide
If you currently ingest Claude metrics through a custom Ocean integration, migrate to the dedicated Claude AI integration:
- Create the new Claude AI data source from your data sources page.
- Copy your existing Claude API credentials into the new integration setup.
- Move your custom blueprint and mapping configuration into the dedicated integration.
- Run a sync and validate that records are ingested for all enabled resources.
- Disable the legacy custom Ocean Claude integration after validation.