> ## Documentation Index
> Fetch the complete documentation index at: https://docs.promptlayer.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Request Analytics — Custom Queries

Run custom aggregations over your request logs. Define what to measure and how to slice it; the API returns structured data you can use however you want — feed it into a chart, run analysis on it, pipe it into a dashboard, or process it programmatically.

Each query in the `customCharts` array specifies a metric and optionally a breakdown dimension or time bucketing:

| Shape                    | Fields required                                         |
| ------------------------ | ------------------------------------------------------- |
| Single aggregate         | `metric` (and `metricField` unless `metric` is `count`) |
| Grouped breakdown        | Add `groupByField` or `groupByMetadataKey`              |
| Over time                | Add `timeSeries: true`                                  |
| Multiple metrics at once | Replace `metric`/`metricField` with a `series` array    |

## Metrics

`metric` controls the aggregation function:

| Value        | Description                                           |
| ------------ | ----------------------------------------------------- |
| `count`      | Number of matching requests (no `metricField` needed) |
| `sum`        | Total of a numeric field                              |
| `avg`        | Average of a numeric field                            |
| `min`        | Minimum value                                         |
| `max`        | Maximum value                                         |
| `percentile` | Arbitrary percentile — requires `percentile` (0–100)  |

## Supported metric fields (`metricField`)

`input_tokens`, `output_tokens`, `cost`, `latency_ms`, `prompt_version_number`, `turn_count`, `tool_call_count`, `cached_tokens`, `thinking_tokens`

> Latency values are returned in **seconds** (converted from milliseconds internally).

## Group-by fields (`groupByField`)

`engine`, `provider_type`, `prompt_id`, `prompt_version_number`, `status`, `error_type`, `tags`, `metadata_keys`, `output_keys`, `input_variable_keys`, `tool_names`

Use `groupByMetadataKey` instead to break down by values of a specific metadata key (e.g. `"environment"` or `"user_id"`).

## Filters

All filter fields from [Search Request Logs](/reference/search-request-logs) are supported (`filter_group`, `q`, `sort_by`, `sort_order`). Filters are applied before aggregation.

## Response shape

Each entry in the response `customCharts` array contains:

* **`id`** — echoes the id you sent
* **`series`** — array of series descriptors: `{ key, label, unit }` — describes what each numeric key in the data rows represents
* **`data`** — array of rows, each with a `label` and one numeric key per series. Time-bucketed rows also include `bucketKey` (ISO date string).
* **`derivedInsights`** — (multi-metric only) pre-computed ratio summaries

`chartType` and `title` are also echoed back but are optional hints — use them if you're rendering a chart, ignore them if you're just processing the numbers.

## Behavior Notes

* `sort_by` / `sort_order` are accepted for compatibility but do not affect aggregated output.
* Overall aggregates (no `timeSeries`, no `groupByField`) return a single row with `label: "Overall"`.
* Multi-metric grouped time-series is not supported — use multiple single-metric queries instead.
* `series` keys must be unique within a query; `id` values must be unique within the request.

## Related

* [Request Analytics](/reference/request-analytics)
* [Search Request Logs](/reference/search-request-logs)
* [Analytics](/why-promptlayer/analytics)


## OpenAPI

````yaml POST /api/public/v2/requests/analytics/custom-analytics
openapi: 3.1.0
info:
  title: FastAPI
  version: 0.1.0
servers: []
security:
  - ApiKeyAuth: []
paths:
  /api/public/v2/requests/analytics/custom-analytics:
    post:
      tags:
        - tracking
      summary: Request Analytics Custom Analytics
      operationId: getRequestAnalyticsCustomAnalytics
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestAnalyticsCustomAnalyticsQuery'
            examples:
              cost_by_model:
                summary: Cost breakdown by model
                value:
                  filter_group:
                    logic: AND
                    filters:
                      - field: request_start_time
                        operator: between
                        value:
                          - '2025-06-01T00:00:00Z'
                          - '2025-06-08T00:00:00Z'
                  customCharts:
                    - id: cost_by_model
                      title: Cost by Model
                      chartType: bar
                      metric: sum
                      metricField: cost
                      groupByField: engine
                      limit: 10
              requests_over_time:
                summary: Request volume over time
                value:
                  customCharts:
                    - id: requests_over_time
                      title: Requests Over Time
                      chartType: line
                      metric: count
                      timeSeries: true
              multi_series:
                summary: Input vs output tokens over time
                value:
                  customCharts:
                    - id: token_breakdown
                      title: Token Usage
                      chartType: area
                      timeSeries: true
                      series:
                        - key: input
                          label: Input Tokens
                          metric: sum
                          metricField: input_tokens
                        - key: output
                          label: Output Tokens
                          metric: sum
                          metricField: output_tokens
                      derivedInsights:
                        - label: Input/Output Ratio
                          numeratorSeriesKey: input
                          denominatorSeriesKey: output
              p95_latency_by_metadata:
                summary: p95 latency broken down by a metadata key
                value:
                  customCharts:
                    - id: latency_by_env
                      title: p95 Latency by Environment
                      chartType: bar
                      metric: percentile
                      percentile: 95
                      metricField: latency_ms
                      groupByMetadataKey: environment
      responses:
        '200':
          description: Custom chart results in the order requested.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RequestAnalyticsCustomAnalyticsResponse'
              examples:
                cost_by_model:
                  summary: Cost by model result
                  value:
                    success: true
                    customCharts:
                      - id: cost_by_model
                        title: Cost by Model
                        chartType: bar
                        series:
                          - key: value
                            label: Cost
                            unit: currency
                        data:
                          - label: gpt-4o
                            value: 8.42
                          - label: claude-sonnet-4-6
                            value: 3.91
        '400':
          description: Invalid chart spec or unsupported filter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          $ref: '#/components/responses/ValidationError'
components:
  schemas:
    RequestAnalyticsCustomAnalyticsQuery:
      type: object
      title: RequestAnalyticsCustomAnalyticsQuery
      description: >-
        Request body for POST
        /api/public/v2/requests/analytics/custom-analytics. Inherits all filter
        fields from RequestLogQuery and adds `customCharts`.
      required:
        - customCharts
      properties:
        filter_group:
          $ref: '#/components/schemas/StructuredFilterGroup'
          nullable: true
          description: Nested filter group with AND/OR logic.
        q:
          type: string
          nullable: true
          description: Free-text search query.
        sort_by:
          type: string
          nullable: true
          enum:
            - request_start_time
            - input_tokens
            - output_tokens
            - cost
            - latency_ms
            - status
          description: Accepted for compatibility; does not affect aggregated output.
        sort_order:
          type: string
          enum:
            - asc
            - desc
          nullable: true
        customCharts:
          type: array
          minItems: 1
          description: One or more chart definitions to compute. Chart ids must be unique.
          items:
            $ref: '#/components/schemas/CustomAnalyticsSpec'
      example:
        filter_group:
          logic: AND
          filters:
            - field: request_start_time
              operator: between
              value:
                - '2025-06-01T00:00:00Z'
                - '2025-06-08T00:00:00Z'
        customCharts:
          - id: cost_by_model
            title: Cost by Model
            chartType: bar
            metric: sum
            metricField: cost
            groupByField: engine
          - id: requests_over_time
            title: Requests Over Time
            chartType: line
            metric: count
            timeSeries: true
    RequestAnalyticsCustomAnalyticsResponse:
      type: object
      title: RequestAnalyticsCustomAnalyticsResponse
      required:
        - success
        - customCharts
      properties:
        success:
          type: boolean
          enum:
            - true
        customCharts:
          type: array
          description: Results in the same order as the input `customCharts` array.
          items:
            $ref: '#/components/schemas/CustomAnalyticsResult'
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          default: false
          description: Indicates that the request failed.
        message:
          type: string
          description: Human-readable error message.
        error:
          type: string
          description: Machine-readable or fallback error message.
      additionalProperties: true
      description: >-
        Standard error response. Some legacy endpoints may return either message
        or error.
    StructuredFilterGroup:
      type: object
      title: StructuredFilterGroup
      description: >-
        A group of filters combined with AND or OR logic. Can be nested
        recursively.
      required:
        - filters
      properties:
        logic:
          type: string
          enum:
            - AND
            - OR
          default: AND
          description: How to combine the filters in this group.
        filters:
          type: array
          description: List of filters or nested filter groups.
          items:
            oneOf:
              - $ref: '#/components/schemas/StructuredFilter'
              - $ref: '#/components/schemas/StructuredFilterGroup'
    CustomAnalyticsSpec:
      type: object
      title: CustomAnalyticsSpec
      description: Definition for a single custom analytics query.
      required:
        - id
        - chartType
      properties:
        id:
          type: string
          description: >-
            Stable identifier for this chart in the response (alphanumeric,
            hyphens, underscores; max 64 chars). Must be unique within the
            request.
        title:
          type: string
          nullable: true
          description: Optional display title (max 200 chars). Defaults to id.
        chartType:
          type: string
          enum:
            - bar
            - line
            - area
          description: >-
            Chart visualization type. Overall aggregate charts (no timeSeries,
            no groupByField) must use `bar`.
        metric:
          type: string
          nullable: true
          enum:
            - count
            - sum
            - avg
            - min
            - max
            - percentile
          description: Aggregation function. Omit when using `series` (multi-series mode).
        metricField:
          type: string
          nullable: true
          enum:
            - input_tokens
            - output_tokens
            - cost
            - latency_ms
            - prompt_version_number
            - turn_count
            - tool_call_count
            - cached_tokens
            - thinking_tokens
          description: >-
            Numeric field to aggregate. Required unless metric is `count` or
            using multi-series mode.
        percentile:
          type: number
          nullable: true
          minimum: 0
          maximum: 100
          description: Required when metric is `percentile`.
        series:
          type: array
          nullable: true
          description: >-
            Multi-series mode: define two or more series. Omit
            metric/metricField/percentile when using this.
          items:
            $ref: '#/components/schemas/CustomAnalyticsSeriesSpec'
        derivedInsights:
          type: array
          nullable: true
          description: >-
            Ratio insights computed from series totals. Only valid in
            multi-series mode.
          items:
            $ref: '#/components/schemas/DerivedRatioInsightSpec'
        groupByField:
          type: string
          nullable: true
          enum:
            - engine
            - provider_type
            - prompt_id
            - prompt_version_number
            - status
            - error_type
            - tags
            - metadata_keys
            - output_keys
            - input_variable_keys
            - tool_names
          description: >-
            Break results down by this request log field. Cannot be combined
            with groupByMetadataKey.
        groupByMetadataKey:
          type: string
          nullable: true
          description: >-
            Break results down by the values of this metadata key. Cannot be
            combined with groupByField.
        timeSeries:
          type: boolean
          default: false
          description: >-
            When true, bucket results over time (bucket size chosen
            automatically from the filter range).
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
          description: Maximum number of group-by buckets to return.
    CustomAnalyticsResult:
      type: object
      title: CustomAnalyticsResult
      description: Computed result for a single custom analytics query.
      properties:
        id:
          type: string
          description: Echoes the chart id from the request.
        title:
          type: string
          description: Chart title (echoed from request, or defaults to id).
        chartType:
          type: string
          enum:
            - bar
            - line
            - area
        series:
          type: array
          description: >-
            Series descriptors (one entry per series). For single-metric charts
            the only entry has key `value`.
          items:
            $ref: '#/components/schemas/CustomAnalyticsSeriesMeta'
        data:
          type: array
          description: >-
            Rows of chart data. Each row has a `label` string and one numeric
            key per series. Time-series rows also include `bucketKey` (ISO date
            string).
          items:
            additionalProperties: true
            type: object
        derivedInsights:
          type: array
          nullable: true
          description: Computed ratio insights (multi-series charts only).
          items:
            $ref: '#/components/schemas/DerivedRatioInsightResult'
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    StructuredFilter:
      type: object
      title: StructuredFilter
      description: A single filter condition on a request log field.
      required:
        - field
        - operator
      properties:
        field:
          type: string
          description: >-
            The request log field to filter on. Intent fields are virtual fields
            that classify request content by tone and do not require any
            additional logging configuration. `user_intent` classifies the
            user's message; valid values: `frustrated`, `satisfied`, `curious`.
            `agent_intent` classifies the agent's response; valid values:
            `apologetic`, `refusal`, `uncertain`. Intent fields support
            operators: `is`, `is_not`, `in`, `not_in`.
          enum:
            - pl_id
            - prompt_id
            - engine
            - provider_type
            - input_text
            - output_text
            - prompt_version_number
            - input_tokens
            - output_tokens
            - cost
            - latency_ms
            - request_start_time
            - request_end_time
            - status
            - is_json
            - is_tool_call
            - is_plain_text
            - tags
            - metadata_keys
            - metadata
            - tool_names
            - output
            - output_keys
            - input_variables
            - input_variable_keys
            - user_intent
            - agent_intent
        operator:
          type: string
          description: The comparison operator.
          enum:
            - is
            - is_not
            - in
            - not_in
            - contains
            - not_contains
            - starts_with
            - ends_with
            - eq
            - neq
            - gt
            - gte
            - lt
            - lte
            - between
            - before
            - after
            - is_true
            - is_false
            - is_empty
            - is_not_empty
            - is_null
            - is_not_null
            - key_equals
            - key_not_equals
            - key_contains
        value:
          description: >-
            The value to compare against. Type depends on the field and
            operator.
          oneOf:
            - type: string
            - type: number
            - type: boolean
            - type: array
            - type: 'null'
        nested_key:
          type: string
          nullable: true
          description: >-
            Required for nested fields (metadata, output, input_variables).
            Specifies which key within the nested object to filter on.
    CustomAnalyticsSeriesSpec:
      type: object
      title: CustomAnalyticsSeriesSpec
      description: One series in a multi-series custom analytics query.
      required:
        - key
        - label
        - metric
        - metricField
      properties:
        key:
          type: string
          description: >-
            Unique identifier for this series within the chart (alphanumeric,
            hyphens, underscores; max 64 chars).
        label:
          type: string
          description: Human-readable series label shown in chart legends (max 120 chars).
        metric:
          type: string
          enum:
            - sum
            - avg
            - min
            - max
            - percentile
          description: Aggregation function for this series.
        metricField:
          type: string
          enum:
            - input_tokens
            - output_tokens
            - cost
            - latency_ms
            - prompt_version_number
            - turn_count
            - tool_call_count
            - cached_tokens
            - thinking_tokens
          description: Numeric field to aggregate.
        percentile:
          type: number
          nullable: true
          minimum: 0
          maximum: 100
          description: Required when metric is `percentile`; omit otherwise.
    DerivedRatioInsightSpec:
      type: object
      title: DerivedRatioInsightSpec
      description: A display-only derived insight computed as a ratio of two series totals.
      required:
        - label
        - numeratorSeriesKey
        - denominatorSeriesKey
      properties:
        type:
          type: string
          enum:
            - ratio
          default: ratio
        label:
          type: string
          description: Label shown for this insight (max 200 chars).
        numeratorSeriesKey:
          type: string
          description: Key of the numerator series (must match a series key in the chart).
        denominatorSeriesKey:
          type: string
          description: >-
            Key of the denominator series (must match a series key in the
            chart).
    CustomAnalyticsSeriesMeta:
      type: object
      title: CustomAnalyticsSeriesMeta
      description: Metadata describing one series in a custom analytics query response.
      properties:
        key:
          type: string
          description: Series key (matches keys in each data row).
        label:
          type: string
          description: Human-readable label.
        unit:
          type: string
          enum:
            - count
            - tokens
            - currency
            - duration_seconds
            - number
          description: Unit hint for rendering axes.
    DerivedRatioInsightResult:
      type: object
      title: DerivedRatioInsightResult
      description: A computed ratio insight in the response.
      properties:
        type:
          type: string
          enum:
            - ratio
        label:
          type: string
        numeratorSeriesKey:
          type: string
        denominatorSeriesKey:
          type: string
        value:
          type: number
          description: Ratio of numerator total to denominator total.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
  responses:
    UnauthorizedError:
      description: Unauthorized - missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ForbiddenError:
      description: Forbidden - API key does not have access to the requested resource.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ValidationError:
      description: Validation error - request parameters or body are invalid.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/HTTPValidationError'
              - $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-KEY

````