# Factory Analytics API

REST API for organization-level usage, Factory Standard Credits consumption, tool usage, and productivity metrics.

The Factory Analytics API returns organization-level usage data for Factory. Query Factory Standard Credits consumption, tool invocations, user activity, and productivity metrics across your organization.

---

## Authentication

All requests require a Factory API key in the `Authorization` header.

```bash
Authorization: Bearer fk-your-api-key
```

[Factory API keys settings](https://app.factory.ai/settings/api-keys)

### Permissions

Only members with the **Manager** or **Owner** role can access the Analytics API. Members with the **User** role receive a `403` error.

---

## Base URL

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

---

## Response format

All responses follow a consistent envelope structure:

```json
{
  "data": [ ... ],
  "meta": { ... }
}
```

| Field  | Type   | Description                                                    |
| :----- | :----- | :------------------------------------------------------------- |
| `data` | array  | Array of result objects (one per day, or per group when using `group_by`) |
| `meta` | object | Request 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:

| Endpoint         | Description                           |
| :--------------- | :------------------------------------ |
| `/tokens`        | Factory Standard Credits consumption by model and user |
| `/tools`         | Tool invocations and autonomy metrics |
| `/activity`      | Daily, weekly, and monthly active users |
| `/productivity`  | File operations and git activity      |
| `/users`         | Per-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_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.

<LabeledDivider label='Endpoint reference' />

## Factory Standard Credits usage

<Endpoint method="GET" path="/tokens">

Returns daily Factory Standard Credits consumption across your organization.

<ApiSection title="Query Parameters">
  <ApiField name="startDate" type="string" location="query" required>Start date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="endDate" type="string" location="query" required>End date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="group_by" type="string" location="query">Set to `model` to group results by model</ApiField>
</ApiSection>

<ApiSection title="Response Fields">
  <ApiField name="date" type="string">Date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="billable_tokens" type="number">Factory Standard Credits consumed, computed from raw input and raw output tokens with cache discounts</ApiField>
  <ApiField name="input_tokens" type="number">Raw input tokens sent to model</ApiField>
  <ApiField name="output_tokens" type="number">Tokens generated by model</ApiField>
  <ApiField name="cache_read_tokens" type="number">Tokens read from prompt cache</ApiField>
  <ApiField name="cache_write_tokens" type="number">Tokens written to prompt cache</ApiField>
  <ApiField name="by_model" type="array">Breakdown per model</ApiField>
  <ApiField name="by_user" type="array">Breakdown per user</ApiField>
</ApiSection>

<ApiSection title="Example">

```bash
# Factory Standard Credits 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"
```

</ApiSection>

</Endpoint>

---

## Tool usage

<Endpoint method="GET" path="/tools">

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

<ApiSection title="Query Parameters">
  <ApiField name="startDate" type="string" location="query" required>Start date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="endDate" type="string" location="query" required>End date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="group_by" type="string" location="query">Set to `tool_name` to group results by tool</ApiField>
</ApiSection>

<ApiSection title="Response">

<StatusBadge status="200" />

```json
{
  "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"
  }
}
```

</ApiSection>

<ApiSection title="Response Fields">
  <ApiField name="date" type="string">Date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="tool_calls" type="number">Total tool invocations</ApiField>
  <ApiField name="by_tool" type="array">Breakdown by tool name</ApiField>
  <ApiField name="mcp_users_with_mcp" type="number">Users who used MCP servers</ApiField>
  <ApiField name="mcp_by_server" type="array">Invocations per MCP server</ApiField>
  <ApiField name="skills_invocations" type="number">Total skill activations</ApiField>
  <ApiField name="skills_by_name" type="array">Breakdown by skill</ApiField>
  <ApiField name="slash_commands_invocations" type="number">Total slash command uses</ApiField>
  <ApiField name="slash_commands_by_name" type="array">Breakdown by command</ApiField>
  <ApiField name="hooks_invocations" type="number">Total hook executions</ApiField>
  <ApiField name="hooks_by_event" type="array">Breakdown by event type</ApiField>
  <ApiField name="web_users" type="number">Users who used web/workspace interface</ApiField>
  <ApiField name="autonomy_ratio_avg" type="number">Average tool calls per user turn</ApiField>
  <ApiField name="autonomy_ratio_p50" type="number">Median autonomy ratio</ApiField>
  <ApiField name="autonomy_ratio_p90" type="number">90th percentile autonomy ratio</ApiField>
  <ApiField name="tool_calls_per_session_avg" type="number">Average tool calls per session</ApiField>
  <ApiField name="user_turns_per_session_avg" type="number">Average user messages per session</ApiField>
  <ApiField name="tool_autonomy_level_ratio" type="object">Distribution of autonomy levels</ApiField>
</ApiSection>

<ApiSection title="Grouped Response">

When `group_by=tool_name`, returns one row per tool per day inside `data`:

```json
{
  "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"
  }
}
```

</ApiSection>

</Endpoint>

---

## User activity

<Endpoint method="GET" path="/activity">

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

<ApiSection title="Query Parameters">
  <ApiField name="startDate" type="string" location="query" required>Start date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="endDate" type="string" location="query" required>End date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="group_by" type="string" location="query">Set to `client` to group by client type</ApiField>
</ApiSection>

<ApiSection title="Response">

<StatusBadge status="200" />

```json
{
  "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"
  }
}
```

</ApiSection>

<ApiSection title="Response Fields">
  <ApiField name="date" type="string">Date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="daily_active_users" type="number">Unique users on this day</ApiField>
  <ApiField name="weekly_active_users" type="number">Unique users in trailing 7 days</ApiField>
  <ApiField name="monthly_active_users" type="number">Unique users in trailing 30 days</ApiField>
  <ApiField name="daily_active_users_by_client" type="object">DAU breakdown by client type</ApiField>
  <ApiField name="sessions" type="number">Total sessions started</ApiField>
  <ApiField name="messages" type="number">Total messages (user + assistant)</ApiField>
  <ApiField name="user_messages" type="number">Messages from users only</ApiField>
</ApiSection>

<ApiSection title="Client Types">

| Client               | Description                              |
| :------------------- | :--------------------------------------- |
| `terminal-ui`        | Interactive CLI sessions                 |
| `web`                | Factory App                              |
| `non-interactive-cli`| Headless/automated CLI (`droid exec`)    |

</ApiSection>

<ApiSection title="Grouped Response">

When `group_by=client`, returns one row per client type per day inside `data`:

```json
{
  "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"
  }
}
```

</ApiSection>

</Endpoint>

---

## Productivity

<Endpoint method="GET" path="/productivity">

Returns daily file operations and git activity.

<ApiSection title="Query Parameters">
  <ApiField name="startDate" type="string" location="query" required>Start date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="endDate" type="string" location="query" required>End date in `YYYY-MM-DD` format</ApiField>
</ApiSection>

<ApiSection title="Response">

<StatusBadge status="200" />

```json
{
  "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"
  }
}
```

</ApiSection>

<ApiSection title="Response Fields">
  <ApiField name="date" type="string">Date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="files_created" type="number">New files created by agent</ApiField>
  <ApiField name="files_edited" type="number">Existing files modified by agent</ApiField>
  <ApiField name="by_extension" type="array">Operations per file extension</ApiField>
  <ApiField name="by_language" type="array">Operations per programming language</ApiField>
  <ApiField name="git_commits" type="number">Commits made via agent</ApiField>
  <ApiField name="git_prs_created" type="number">Pull requests created via agent</ApiField>
</ApiSection>

</Endpoint>

---

## Per-user metrics

<Endpoint method="GET" path="/users">

Returns detailed metrics per user with cursor-based pagination.

<ApiSection title="Query Parameters">
  <ApiField name="startDate" type="string" location="query" required>Start date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="endDate" type="string" location="query" required>End date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="limit" type="number" location="query">Users per page, 1-100 (default: 20)</ApiField>
  <ApiField name="cursor" type="string" location="query">User ID for pagination (from `next_cursor`)</ApiField>
</ApiSection>

<ApiSection title="Response">

<StatusBadge status="200" />

```json
{
  "data": [
    {
      "user_id": "user_01HPMQ7NXKHM7Y7PR3TTZY3JZS",
      "user_email": "developer@company.com",
      "date": "2026-01-15",
      "tool_calls": 1250,
      "billable_tokens": 450000,
      "primary_model": "claude-sonnet-4-5-20250929",
      "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"
  }
}
```

</ApiSection>

<ApiSection title="Response Fields">
  <ApiField name="user_id" type="string">Unique user identifier</ApiField>
  <ApiField name="user_email" type="string | null">User email</ApiField>
  <ApiField name="date" type="string">Date in `YYYY-MM-DD` format</ApiField>
  <ApiField name="tool_calls" type="number">Tool invocations by this user</ApiField>
  <ApiField name="billable_tokens" type="number">Factory Standard Credits consumed by this user</ApiField>
  <ApiField name="primary_model" type="string">Most-used model</ApiField>
  <ApiField name="primary_model_tier" type="string">Model tier (`standard` or `thinking`)</ApiField>
  <ApiField name="files_created" type="number">Files created</ApiField>
  <ApiField name="files_edited" type="number">Files edited</ApiField>
  <ApiField name="git_commits" type="number">Commits made</ApiField>
  <ApiField name="git_prs_created" type="number">Pull requests created</ApiField>
  <ApiField name="mcp_calls" type="number">MCP tool invocations</ApiField>
  <ApiField name="skill_calls" type="number">Skill activations</ApiField>
  <ApiField name="slash_commands" type="number">Slash command uses</ApiField>
  <ApiField name="hooks" type="number">Hook executions</ApiField>
  <ApiField name="sessions" type="number">Sessions started</ApiField>
  <ApiField name="messages" type="number">Total messages</ApiField>
  <ApiField name="user_messages" type="number">User messages only</ApiField>
  <ApiField name="assistant_messages" type="number">Assistant messages</ApiField>
  <ApiField name="autonomy_ratio" type="number">Tool calls per user turn</ApiField>
  <ApiField name="delegation_level" type="string">Primary autonomy mode</ApiField>
  <ApiField name="languages" type="array">Programming languages worked in</ApiField>
</ApiSection>

<ApiSection title="Delegation Levels">

| Level         | Description                                        |
| :------------ | :------------------------------------------------- |
| `auto-high`   | Maximum autonomy, minimal confirmations            |
| `auto-medium` | Balanced autonomy with some confirmations          |
| `auto-low`    | Limited autonomy, frequent confirmations           |
| `spec`        | Specification mode, planning before execution      |
| `manual`      | Full manual control, confirm each action           |

</ApiSection>

<ApiSection title="Pagination">

Use cursor-based pagination to iterate through users:

```bash
# 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"
```

</ApiSection>

</Endpoint>

<LabeledDivider label='Operational notes' />

## Important constraints

### Date requirements

<PropertyList>
  <Property name='Format'>All dates must be `YYYY-MM-DD`.</Property>
  <Property name='Timezone'>UTC only (no timezone parameter).</Property>
  <Property name='Data availability'>
    Data is available through yesterday (UTC). Requesting today's date returns
    a `400` error.
  </Property>
  <Property name='Historical data'>Available from January 14, 2026.</Property>
</PropertyList>

### Rate limits

Rate limits vary by plan. [Contact us](mailto:support@factory.ai) for specifics or if you need higher limits for dashboard or automation use cases.

---

## Errors

The API returns standard HTTP status codes:

| Status | Description                                          |
| :----- | :--------------------------------------------------- |
| `400`  | Invalid date format, today's date requested, or limit out of range |
| `401`  | Missing or invalid API key                           |
| `403`  | Insufficient permissions (requires Manager or Owner role) |
| `500`  | Internal error                                       |

### Error response format

```json
{
  "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:

```text
CLI/Daemon → OTEL Events → BigQuery (raw) → dbt models → API
```

{/* sweep-allow: term-bullets */}

- **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

<Note>
A few known data quality considerations:

{/* sweep-allow: term-bullets */}

- **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
</Note>

---

## Use cases

### Cost monitoring dashboard

Track usage trends and identify cost drivers:

```bash
# Daily 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:

```bash
# 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:

```bash
# 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:

```bash
# 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"
```
