Skip to main content
The Analytics API provides programmatic access to organization-level usage data for Factory. Query token consumption, tool invocations, user activity, and productivity metrics across your entire organization.

Quick Start

curl -H "Authorization: Bearer $FACTORY_API_KEY" \
  "https://api.factory.ai/api/v1/analytics/tokens?startDate=2026-01-14&endDate=2026-01-28"

Authentication

All requests require a Factory API key in the Authorization header:
Authorization: Bearer fk-your-api-key
Generate API keys at app.factory.ai/settings/api-keys.

Permissions

Only users with Manager or Owner roles can access the Analytics API. Members and guests will receive a 403 error.

Base URL

https://api.factory.ai/api/v1/analytics

Response Format

All responses follow a consistent envelope structure:
{
  "data": [ ... ],
  "meta": { ... }
}
FieldTypeDescription
dataarrayArray of result objects (one per day, or per group when using group_by)
metaobjectRequest metadata: org_id, start_date, end_date, and pagination info for /users

Endpoints

The Analytics API provides five endpoints, each focused on a specific category of metrics:
EndpointDescription
/tokensToken consumption by model and user
/toolsTool invocations and autonomy metrics
/activityDaily, weekly, and monthly active users
/productivityFile operations and git activity
/usersPer-user metrics with pagination

Understanding group_by

Several endpoints support a group_by parameter. Here’s how it works:
  • Without group_by: Returns one row per day with nested breakdowns (e.g., by_model, by_tool, daily_active_users_by_client). Use this when you want all dimensions in a single response.
  • With group_by: Flattens one of those nested arrays into separate rows. Each row has a group_key field identifying the dimension value. Use this when piping data into tools that expect flat rows (spreadsheets, BI tools, time-series databases).
For example, /activity without group_by returns daily_active_users_by_client as an object. With group_by=client, you get separate rows for terminal-ui, web, and non-interactive-cli - useful for plotting each client type as its own line on a chart.

Token Usage

Returns daily token consumption across your organization.
GET /tokens

Query Parameters

ParameterTypeRequiredDescription
startDatestringYesStart date in YYYY-MM-DD format
endDatestringYesEnd date in YYYY-MM-DD format
group_bystringNoSet to model to group results by model

Response

{
  "data": [
    {
      "date": "2026-01-15",
      "billable_tokens": 1250000,
      "input_tokens": 980000,
      "output_tokens": 270000,
      "cache_read_tokens": 150000,
      "cache_write_tokens": 45000,
      "by_model": [
        {
          "model_id": "claude-sonnet-4-20250514",
          "model_tier": "standard",
          "billable_tokens": 800000,
          "input_tokens": 620000,
          "output_tokens": 180000
        }
      ],
      "by_user": [
        {
          "user_id": "user_01HPMQ7NXKHM7Y7PR3TTZY3JZS",
          "user_email": "[email protected]",
          "billable_tokens": 450000,
          "by_model": [...]
        }
      ]
    }
  ],
  "meta": {
    "org_id": "org_01HPMQ6ABCDE...",
    "start_date": "2026-01-15",
    "end_date": "2026-01-15"
  }
}

Response Fields

FieldTypeDescription
datestringDate in YYYY-MM-DD format
billable_tokensnumberTotal billable tokens (input + output, with cache discounts)
input_tokensnumberRaw input tokens sent to model
output_tokensnumberTokens generated by model
cache_read_tokensnumberTokens read from prompt cache
cache_write_tokensnumberTokens written to prompt cache
by_modelarrayBreakdown per model
by_userarrayBreakdown per user

Grouped Response

When group_by=model, returns one row per model per day inside data:
{
  "data": [
    {
      "date": "2026-01-15",
      "group_key": "claude-sonnet-4-20250514",
      "billable_tokens": 800000,
      "input_tokens": 620000,
      "output_tokens": 180000
    },
    {
      "date": "2026-01-15",
      "group_key": "claude-opus-4-20250514",
      "billable_tokens": 450000,
      "input_tokens": 360000,
      "output_tokens": 90000
    }
  ],
  "meta": {
    "org_id": "org_01HPMQ6ABCDE...",
    "start_date": "2026-01-15",
    "end_date": "2026-01-15"
  }
}

Example

# Token usage for a date range
curl -H "Authorization: Bearer $FACTORY_API_KEY" \
  "https://api.factory.ai/api/v1/analytics/tokens?startDate=2026-01-14&endDate=2026-01-28"

# Grouped by model
curl -H "Authorization: Bearer $FACTORY_API_KEY" \
  "https://api.factory.ai/api/v1/analytics/tokens?startDate=2026-01-15&endDate=2026-01-15&group_by=model"

Tool Usage

Returns daily tool invocations, MCP usage, skills, slash commands, and autonomy metrics.
GET /tools

Query Parameters

ParameterTypeRequiredDescription
startDatestringYesStart date in YYYY-MM-DD format
endDatestringYesEnd date in YYYY-MM-DD format
group_bystringNoSet to tool_name to group results by tool

Response

{
  "data": [
    {
      "date": "2026-01-15",
      "tool_calls": 45000,
      "by_tool": [
        { "tool": "Read", "invocations": 12500 },
        { "tool": "Edit", "invocations": 8200 },
        { "tool": "Execute", "invocations": 6100 }
      ],
      "mcp_users_with_mcp": 42,
      "mcp_by_server": [
        { "server": "github", "invocations": 1200 },
        { "server": "notion", "invocations": 850 }
      ],
      "skills_invocations": 320,
      "skills_by_name": [
        { "name": "browser", "count": 180 },
        { "name": "frontend-ui", "count": 95 }
      ],
      "slash_commands_invocations": 1500,
      "slash_commands_by_name": [
        { "name": "review", "count": 420 },
        { "name": "test", "count": 380 }
      ],
      "hooks_invocations": 2800,
      "hooks_by_event": [
        { "event": "PostToolUse", "matcher": "*.ts", "command": "eslint --fix", "count": 1200 }
      ],
      "web_users": 42,
      "autonomy_ratio_avg": 8.5,
      "autonomy_ratio_p50": 6.2,
      "autonomy_ratio_p90": 18.4,
      "tool_calls_per_session_avg": 45.2,
      "user_turns_per_session_avg": 5.3,
      "tool_autonomy_level_ratio": {
        "auto_high": 0.35,
        "auto_medium": 0.42,
        "auto_low": 0.18,
        "manual": 0.05
      }
    }
  ],
  "meta": {
    "org_id": "org_01HPMQ6ABCDE...",
    "start_date": "2026-01-15",
    "end_date": "2026-01-15"
  }
}

Response Fields

FieldTypeDescription
datestringDate in YYYY-MM-DD format
tool_callsnumberTotal tool invocations
by_toolarrayBreakdown by tool name
mcp_users_with_mcpnumberUsers who used MCP servers
mcp_by_serverarrayInvocations per MCP server
skills_invocationsnumberTotal skill activations
skills_by_namearrayBreakdown by skill
slash_commands_invocationsnumberTotal slash command uses
slash_commands_by_namearrayBreakdown by command
hooks_invocationsnumberTotal hook executions
hooks_by_eventarrayBreakdown by event type
web_usersnumberUsers who used web/workspace interface
autonomy_ratio_avgnumberAverage tool calls per user turn
autonomy_ratio_p50numberMedian autonomy ratio
autonomy_ratio_p90number90th percentile autonomy ratio
tool_calls_per_session_avgnumberAverage tool calls per session
user_turns_per_session_avgnumberAverage user messages per session
tool_autonomy_level_ratioobjectDistribution of autonomy levels

Grouped Response

When group_by=tool_name, returns one row per tool per day inside data:
{
  "data": [
    {
      "date": "2026-01-15",
      "group_key": "Read",
      "tool_calls": 12500
    },
    {
      "date": "2026-01-15",
      "group_key": "Edit",
      "tool_calls": 8200
    }
  ],
  "meta": {
    "org_id": "org_01HPMQ6ABCDE...",
    "start_date": "2026-01-15",
    "end_date": "2026-01-15"
  }
}

User Activity

Returns daily, weekly, and monthly active users along with session counts.
GET /activity

Query Parameters

ParameterTypeRequiredDescription
startDatestringYesStart date in YYYY-MM-DD format
endDatestringYesEnd date in YYYY-MM-DD format
group_bystringNoSet to client to group by client type

Response

{
  "data": [
    {
      "date": "2026-01-15",
      "daily_active_users": 128,
      "weekly_active_users": 312,
      "monthly_active_users": 485,
      "daily_active_users_by_client": {
        "terminal-ui": 95,
        "web": 42,
        "non-interactive-cli": 18
      },
      "sessions": 890,
      "messages": 12500,
      "user_messages": 4200
    }
  ],
  "meta": {
    "org_id": "org_01HPMQ6ABCDE...",
    "start_date": "2026-01-15",
    "end_date": "2026-01-15"
  }
}

Response Fields

FieldTypeDescription
datestringDate in YYYY-MM-DD format
daily_active_usersnumberUnique users on this day
weekly_active_usersnumberUnique users in trailing 7 days
monthly_active_usersnumberUnique users in trailing 30 days
daily_active_users_by_clientobjectDAU breakdown by client type
sessionsnumberTotal sessions started
messagesnumberTotal messages (user + assistant)
user_messagesnumberMessages from users only

Client Types

ClientDescription
terminal-uiInteractive CLI sessions
webFactory web interface
non-interactive-cliHeadless/automated CLI (droid exec)

Grouped Response

When group_by=client, returns one row per client type per day inside data:
{
  "data": [
    {
      "date": "2026-01-15",
      "group_key": "terminal-ui",
      "daily_active_users": 95
    },
    {
      "date": "2026-01-15",
      "group_key": "web",
      "daily_active_users": 42
    }
  ],
  "meta": {
    "org_id": "org_01HPMQ6ABCDE...",
    "start_date": "2026-01-15",
    "end_date": "2026-01-15"
  }
}

Productivity

Returns daily file operations and git activity.
GET /productivity

Query Parameters

ParameterTypeRequiredDescription
startDatestringYesStart date in YYYY-MM-DD format
endDatestringYesEnd date in YYYY-MM-DD format

Response

{
  "data": [
    {
      "date": "2026-01-15",
      "files_created": 245,
      "files_edited": 1820,
      "by_extension": [
        { "extension": ".ts", "count": 890 },
        { "extension": ".tsx", "count": 420 },
        { "extension": ".py", "count": 310 }
      ],
      "by_language": [
        { "language": "TypeScript", "count": 1310 },
        { "language": "Python", "count": 310 }
      ],
      "git_commits": 156,
      "git_prs_created": 42
    }
  ],
  "meta": {
    "org_id": "org_01HPMQ6ABCDE...",
    "start_date": "2026-01-15",
    "end_date": "2026-01-15"
  }
}

Response Fields

FieldTypeDescription
datestringDate in YYYY-MM-DD format
files_creatednumberNew files created by agent
files_editednumberExisting files modified by agent
by_extensionarrayOperations per file extension
by_languagearrayOperations per programming language
git_commitsnumberCommits made via agent
git_prs_creatednumberPull requests created via agent

Per-User Metrics

Returns detailed metrics per user with cursor-based pagination.
GET /users

Query Parameters

ParameterTypeRequiredDescription
startDatestringYesStart date in YYYY-MM-DD format
endDatestringYesEnd date in YYYY-MM-DD format
limitnumberNoUsers per page, 1-100 (default: 20)
cursorstringNoUser ID for pagination (from next_cursor)

Response

{
  "data": [
    {
      "user_id": "user_01HPMQ7NXKHM7Y7PR3TTZY3JZS",
      "user_email": "[email protected]",
      "date": "2026-01-15",
      "tool_calls": 1250,
      "billable_tokens": 450000,
      "primary_model": "claude-sonnet-4-20250514",
      "primary_model_tier": "standard",
      "files_created": 12,
      "files_edited": 85,
      "git_commits": 8,
      "git_prs_created": 2,
      "mcp_calls": 45,
      "skill_calls": 8,
      "slash_commands": 22,
      "hooks": 120,
      "sessions": 15,
      "messages": 180,
      "user_messages": 62,
      "assistant_messages": 118,
      "autonomy_ratio": 9.2,
      "delegation_level": "auto-high",
      "languages": ["TypeScript", "Python", "Go"]
    }
  ],
  "meta": {
    "org_id": "org_01HPMQ6ABCDE...",
    "start_date": "2026-01-15",
    "end_date": "2026-01-15",
    "has_more": true,
    "next_cursor": "user_01HPMQ8ABCDE7Y7PR3TTZY4KLM"
  }
}

Response Fields

FieldTypeDescription
user_idstringUnique user identifier
user_emailstring | nullUser email
datestringDate in YYYY-MM-DD format
tool_callsnumberTool invocations by this user
billable_tokensnumberTokens consumed by this user
primary_modelstringMost-used model
primary_model_tierstringModel tier (standard or thinking)
files_creatednumberFiles created
files_editednumberFiles edited
git_commitsnumberCommits made
git_prs_creatednumberPull requests created
mcp_callsnumberMCP tool invocations
skill_callsnumberSkill activations
slash_commandsnumberSlash command uses
hooksnumberHook executions
sessionsnumberSessions started
messagesnumberTotal messages
user_messagesnumberUser messages only
assistant_messagesnumberAssistant messages
autonomy_rationumberTool calls per user turn
delegation_levelstringPrimary autonomy mode
languagesarrayProgramming languages worked in

Delegation Levels

LevelDescription
auto-highMaximum autonomy, minimal confirmations
auto-mediumBalanced autonomy with some confirmations
auto-lowLimited autonomy, frequent confirmations
specSpecification mode, planning before execution
manualFull manual control, confirm each action

Pagination

Use cursor-based pagination to iterate through users:
# First page
curl -H "Authorization: Bearer $FACTORY_API_KEY" \
  "https://api.factory.ai/api/v1/analytics/users?startDate=2026-01-15&endDate=2026-01-15&limit=50"

# Next page
curl -H "Authorization: Bearer $FACTORY_API_KEY" \
  "https://api.factory.ai/api/v1/analytics/users?startDate=2026-01-15&endDate=2026-01-15&limit=50&cursor=user_01HPMQ8ABCDE7Y7PR3TTZY4KLM"

Important Constraints

Date Requirements

  • Format: All dates must be YYYY-MM-DD
  • Timezone: UTC only (no timezone parameter)
  • Data availability: Data is available through yesterday (UTC). Requesting today’s date returns a 400 error.
  • Historical data: Available from January 14, 2026

Rate Limits

Rate limits vary by plan. Contact us for specifics or if you need higher limits for dashboard or automation use cases.

Error Handling

The API returns standard HTTP status codes:
StatusDescription
400Invalid date format, today’s date requested, or limit out of range
401Missing or invalid API key
403Insufficient permissions (requires Manager or Owner role)
500Internal error

Error Response Format

{
  "title": "Bad Request",
  "detail": "Cannot query today's date - analytics data has a 24-hour lag",
  "status": 400,
  "requestId": "req_01HPMQ9WXYZ..."
}

Data Pipeline

Analytics data flows through the following pipeline:
CLI/Daemon → OTEL Events → BigQuery (raw) → dbt models → API
  • Source: OpenTelemetry spans from the CLI and daemon
  • Processing: Daily batch aggregation via dbt
  • Availability: Data is available the day after it’s generated

Data Quality Notes

A few known data quality considerations:
  • MCP server names: Some duplicates exist due to case sensitivity (e.g., axiom vs Axiom)
  • Tool names: Approximately 0.006% of entries contain parsing artifacts
  • User counts: A user active on multiple clients counts once in DAU but appears in each client breakdown

Use Cases

Cost Monitoring Dashboard

Track token consumption trends and identify cost drivers:
# Daily token usage for the month
curl -H "Authorization: Bearer $FACTORY_API_KEY" \
  "https://api.factory.ai/api/v1/analytics/tokens?startDate=2026-01-14&endDate=2026-01-28"

Adoption Tracking

Monitor DAU/WAU/MAU and identify adoption patterns:
# Activity metrics with client breakdown
curl -H "Authorization: Bearer $FACTORY_API_KEY" \
  "https://api.factory.ai/api/v1/analytics/activity?startDate=2026-01-14&endDate=2026-01-28&group_by=client"

Team Productivity Reports

Measure output and efficiency:
# Productivity metrics
curl -H "Authorization: Bearer $FACTORY_API_KEY" \
  "https://api.factory.ai/api/v1/analytics/productivity?startDate=2026-01-14&endDate=2026-01-28"

Individual Performance

Export per-user metrics for team leads:
# Paginate through all users
curl -H "Authorization: Bearer $FACTORY_API_KEY" \
  "https://api.factory.ai/api/v1/analytics/users?startDate=2026-01-15&endDate=2026-01-15&limit=100"