Skip to content

    Pre-launch API. The concepts described here are settled, but the API shape is not: request and response fields, parameters and defaults can still change. Build against it, and talk to your Luigi's Box contact before you put an integration into production.

    Search totals over time

    View source
    POST
    /analytics/v1/searches/series
    curl --request POST \
    --url 'https://api.eu1.luigisbox.ai/analytics/v1/searches/series?channel_ids=example' \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '{ "attribution_model": "last_touch", "date_range": { "date_from": "2026-04-15T12:00:00Z", "date_to": "2026-04-15T12:00:00Z" }, "filters": { "$and": [ { "applied_filter": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "consent_granted": { "eq": true }, "device": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "external_ab_test_variant": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "filtered": { "eq": true }, "followed_by_search": { "eq": true }, "identity": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "internal_ab_test_id": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "internal_ab_test_variant": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "landing_page": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "landing_page_host": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "personalized": { "eq": true }, "platform": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "query": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "redirected": { "eq": true }, "result_type": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "returning": { "eq": true }, "session_rank": { "eq": 1, "gt": 1, "gte": 1, "in": [ 1 ], "lt": 1, "lte": 1, "neq": 1, "not_in": [ 1 ] }, "traffic_source": { "eq": "direct", "in": [ "direct" ], "neq": "direct", "not_in": [ "direct" ] }, "traffic_source_type": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "url_host": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "url_path": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] } } ], "$not": { "applied_filter": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "consent_granted": { "eq": true }, "device": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "external_ab_test_variant": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "filtered": { "eq": true }, "followed_by_search": { "eq": true }, "identity": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "internal_ab_test_id": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "internal_ab_test_variant": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "landing_page": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "landing_page_host": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "personalized": { "eq": true }, "platform": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "query": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "redirected": { "eq": true }, "result_type": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "returning": { "eq": true }, "session_rank": { "eq": 1, "gt": 1, "gte": 1, "in": [ 1 ], "lt": 1, "lte": 1, "neq": 1, "not_in": [ 1 ] }, "traffic_source": { "eq": "direct", "in": [ "direct" ], "neq": "direct", "not_in": [ "direct" ] }, "traffic_source_type": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "url_host": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "url_path": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] } }, "$or": [ { "applied_filter": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "consent_granted": { "eq": true }, "device": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "external_ab_test_variant": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "filtered": { "eq": true }, "followed_by_search": { "eq": true }, "identity": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "internal_ab_test_id": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "internal_ab_test_variant": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "landing_page": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "landing_page_host": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "personalized": { "eq": true }, "platform": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "query": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "redirected": { "eq": true }, "result_type": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "returning": { "eq": true }, "session_rank": { "eq": 1, "gt": 1, "gte": 1, "in": [ 1 ], "lt": 1, "lte": 1, "neq": 1, "not_in": [ 1 ] }, "traffic_source": { "eq": "direct", "in": [ "direct" ], "neq": "direct", "not_in": [ "direct" ] }, "traffic_source_type": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "url_host": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] }, "url_path": { "eq": "example", "exists": true, "in": [ "example" ], "matches": "example", "neq": "example", "not_in": [ "example" ] } } ] }, "granularity": "hour", "group_by": [], "metrics": [ "users" ], "timezone": "UTC", "touchpoints": [ "autocomplete" ] }'

    How search does over time, for trends and charts.

    channel_ids
    required
    Channel Ids

    One or more channel ids to aggregate over. Provide as repeated query parameters (?channel_ids=A&channel_ids=B).

    Array<string>
    >= 1 items

    One or more channel ids to aggregate over. Provide as repeated query parameters (?channel_ids=A&channel_ids=B).

    Media type application/json
    SearchSeriesRequest

    Request for totals per bucket.

    object
    attribution_model
    AttributionModel

    Which of the lists that showed an item is credited with an interaction on it; only last_touch, the last of them, is recorded today.

    string
    default: last_touch
    Allowed values: last_touch
    date_range
    required
    DateRange

    The time range of the report.

    object
    date_from
    required
    Date From

    Start, inclusive.

    string format: date-time
    date_to
    required
    Date To

    End, exclusive.

    string format: date-time
    filters
    Any of:
    One of:

    A group node: exactly one of $and, $or, $not.

    object
    >= 1 properties <= 1 properties
    $and

    Nodes that must all match.

    Array
    >= 1 items
    One of:
    object recursive
    $not
    One of:
    object recursive
    $or

    Nodes of which at least one must match.

    Array
    >= 1 items
    One of:
    object recursive
    granularity
    Granularity

    The length of a bucket.

    string
    default: day
    Allowed values: hour day week month quarter year
    group_by
    Group By

    Fields that split every row, summary and series point, e.g. ["touchpoint", "device"]: at most 2 besides touchpoint, which splits the touchpoints. At most one of landing_page, url_path, url_params.* and context.*; a summary or series holds at most 500 groups, and a session with several values of a key counts once in each of its groups.

    Array
    default: <= 3 items unique items
    metrics
    Metrics

    Metrics to compute; every metric of the report when left out.

    Array<string>
    >= 1 items unique items
    Allowed values: users sessions searches queries clicks click_through_rate users_click_through_rate sessions_click_through_rate searches_without_clicks attributed_cart_value attributed_transactions attributed_transaction_rate users_attributed_transaction_rate sessions_attributed_transaction_rate attributed_revenue attributed_average_order_value attributed_key_events_1 attributed_key_events_1_rate users_attributed_key_events_1_rate sessions_attributed_key_events_1_rate attributed_key_events_2 attributed_key_events_2_rate users_attributed_key_events_2_rate sessions_attributed_key_events_2_rate attributed_key_events_3 attributed_key_events_3_rate users_attributed_key_events_3_rate sessions_attributed_key_events_3_rate searches_without_results no_results_rate users_no_results_rate sessions_no_results_rate users_without_results queries_without_results last_no_results_at unfiltered_searches_without_results unfiltered_no_results_rate unfiltered_users_without_results last_unfiltered_no_results_at exit_rate reformulation_rate average_click_position
    timezone
    Timezone

    The IANA timezone whose calendar the buckets follow, e.g. Europe/Bratislava: UTC or an <area>/<location> name. Every bucket after the first starts at a whole hour, or at the local midnight that starts a day, a Monday, or a month, quarter or year there, so across a change of the zone’s offset a day lasts 23 or 25 hours. date_range stays two instants: give it local midnights, e.g. 2026-10-01T00:00:00+02:00, for whole first and last buckets.

    string
    default: UTC <= 64 characters
    touchpoints
    required
    Touchpoints

    Where the lists were shown. As with channel_ids, the touchpoints are pooled: one total, one series line and one row per key across all of them, and group_by: ["touchpoint"] splits them. Pooled, the counts of searches, impressions, clicks, transactions and key events add up, and so does money; users, sessions and queries count once however many of the touchpoints they appear at; and a rate is taken over the pooled lists. searches counts the lists shown, so an autocomplete submitted as a search counts once at each touchpoint, and an average position mixes the two touchpoints’ scales. A reformulation is a change of query on the next list at the same touchpoint.

    Array<string>
    >= 1 items unique items
    Allowed values: autocomplete serp

    Successful Response

    Media type application/json
    SearchSeries

    Totals per bucket over date_range.

    object
    granularity
    required
    Granularity

    The length of a bucket, as requested.

    string
    Allowed values: hour day week month quarter year
    points
    required
    Points

    Every bucket of date_range in time order; with group_by, every bucket per group, by group value and then time. A bucket without data has counts and money of 0, and every other metric null. At most 10,000 points.

    Array<object>
    SearchSeriesPoint

    One time bucket.

    object
    group
    Group

    Its group_by values; present with group_by.

    object
    channel_id
    Any of:
    string
    consent_granted
    Any of:
    boolean
    context
    Any of:
    object
    key
    additional properties
    Any of:
    string
    device
    Any of:
    string
    external_ab_test_variant
    Any of:
    string
    internal_ab_test_id
    Any of:
    string
    internal_ab_test_variant
    Any of:
    string
    landing_page
    Any of:
    string
    landing_page_host
    Any of:
    string
    personalized
    Any of:
    boolean
    platform
    Any of:
    string
    result_type
    Any of:
    string
    returning
    Any of:
    boolean
    surface_id
    Any of:
    string
    touchpoint
    Any of:
    SearchReportTouchpoint

    Where a search list was shown: serp, the results page, or autocomplete, the as-you-type panel.

    string
    Allowed values: autocomplete serp
    traffic_source
    Any of:
    TrafficSource
    string
    Allowed values: direct paid referral llms
    traffic_source_type
    Any of:
    string
    url_host
    Any of:
    string
    url_params
    Any of:
    object
    key
    additional properties
    Any of:
    string
    url_path
    Any of:
    string
    metrics
    required
    SearchMetrics

    Metrics over the bucket.

    object
    attributed_average_order_value
    Any of:
    number
    attributed_cart_value
    Any of:
    number
    attributed_key_events_1
    Any of:
    number
    attributed_key_events_1_rate
    Any of:
    number
    attributed_key_events_2
    Any of:
    number
    attributed_key_events_2_rate
    Any of:
    number
    attributed_key_events_3
    Any of:
    number
    attributed_key_events_3_rate
    Any of:
    number
    attributed_revenue
    Any of:
    number
    attributed_transaction_rate
    Any of:
    number
    attributed_transactions
    Any of:
    number
    average_click_position
    Any of:
    number
    click_through_rate
    Any of:
    number
    clicks
    Any of:
    number
    exit_rate
    Any of:
    number
    last_no_results_at
    Any of:
    string format: date-time
    last_unfiltered_no_results_at
    Any of:
    string format: date-time
    no_results_rate
    Any of:
    number
    queries
    Any of:
    integer
    queries_without_results
    Any of:
    integer
    reformulation_rate
    Any of:
    number
    searches
    Any of:
    integer
    searches_without_clicks
    Any of:
    integer
    searches_without_results
    Any of:
    integer
    sessions
    Any of:
    integer
    sessions_attributed_key_events_1_rate
    Any of:
    number
    sessions_attributed_key_events_2_rate
    Any of:
    number
    sessions_attributed_key_events_3_rate
    Any of:
    number
    sessions_attributed_transaction_rate
    Any of:
    number
    sessions_click_through_rate
    Any of:
    number
    sessions_no_results_rate
    Any of:
    number
    unfiltered_no_results_rate
    Any of:
    number
    unfiltered_searches_without_results
    Any of:
    integer
    unfiltered_users_without_results
    Any of:
    integer
    users
    Any of:
    integer
    users_attributed_key_events_1_rate
    Any of:
    number
    users_attributed_key_events_2_rate
    Any of:
    number
    users_attributed_key_events_3_rate
    Any of:
    number
    users_attributed_transaction_rate
    Any of:
    number
    users_click_through_rate
    Any of:
    number
    users_no_results_rate
    Any of:
    number
    users_without_results
    Any of:
    integer
    starts_at
    required
    Starts At

    Start of the bucket, as a UTC instant at a boundary of timezone; the first and last buckets are clipped to date_range.

    string format: date-time
    timezone
    required
    Timezone

    The timezone the buckets follow: the request’s timezone, or UTC.

    string
    Example
    {
    "granularity": "hour",
    "points": [
    {
    "group": {
    "touchpoint": "autocomplete",
    "traffic_source": "direct"
    }
    }
    ]
    }

    Malformed request

    Media type application/json
    ErrorResponse

    Canonical error body — every non-2xx response uses this shape.

    object
    exception_details
    ExceptionDetails

    Machine-readable error context; empty object when there is nothing to add.

    object
    error_code
    Error Code

    Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. filter_unknown_field or cursor_mismatch. Safe to branch on; reason is not. Absent when the status code says all there is to say.

    string
    validation_errors
    Validation Errors

    Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value.

    Array<object>
    ValidationProblem

    One field-level reason a request was rejected.

    object
    loc
    required
    Loc

    Path to the offending value from the request root, e.g. ["body", "filters", 0, "operator"].

    Array
    msg
    required
    Msg

    What is wrong with the value at loc.

    string
    type
    required
    Type

    Stable machine code for the problem, e.g. missing or string_too_short.

    string
    key
    additional properties
    any
    reason
    required
    Reason

    Human-readable, actionable error message.

    string
    request_id
    Any of:
    string
    Example generated
    {
    "exception_details": {
    "error_code": "example",
    "validation_errors": [
    {
    "loc": [
    "example"
    ],
    "msg": "example",
    "type": "example"
    }
    ]
    },
    "reason": "example",
    "request_id": "example"
    }

    Missing or invalid credentials

    Media type application/json
    ErrorResponse

    Canonical error body — every non-2xx response uses this shape.

    object
    exception_details
    ExceptionDetails

    Machine-readable error context; empty object when there is nothing to add.

    object
    error_code
    Error Code

    Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. filter_unknown_field or cursor_mismatch. Safe to branch on; reason is not. Absent when the status code says all there is to say.

    string
    validation_errors
    Validation Errors

    Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value.

    Array<object>
    ValidationProblem

    One field-level reason a request was rejected.

    object
    loc
    required
    Loc

    Path to the offending value from the request root, e.g. ["body", "filters", 0, "operator"].

    Array
    msg
    required
    Msg

    What is wrong with the value at loc.

    string
    type
    required
    Type

    Stable machine code for the problem, e.g. missing or string_too_short.

    string
    key
    additional properties
    any
    reason
    required
    Reason

    Human-readable, actionable error message.

    string
    request_id
    Any of:
    string
    Example generated
    {
    "exception_details": {
    "error_code": "example",
    "validation_errors": [
    {
    "loc": [
    "example"
    ],
    "msg": "example",
    "type": "example"
    }
    ]
    },
    "reason": "example",
    "request_id": "example"
    }

    Authenticated but not permitted

    Media type application/json
    ErrorResponse

    Canonical error body — every non-2xx response uses this shape.

    object
    exception_details
    ExceptionDetails

    Machine-readable error context; empty object when there is nothing to add.

    object
    error_code
    Error Code

    Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. filter_unknown_field or cursor_mismatch. Safe to branch on; reason is not. Absent when the status code says all there is to say.

    string
    validation_errors
    Validation Errors

    Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value.

    Array<object>
    ValidationProblem

    One field-level reason a request was rejected.

    object
    loc
    required
    Loc

    Path to the offending value from the request root, e.g. ["body", "filters", 0, "operator"].

    Array
    msg
    required
    Msg

    What is wrong with the value at loc.

    string
    type
    required
    Type

    Stable machine code for the problem, e.g. missing or string_too_short.

    string
    key
    additional properties
    any
    reason
    required
    Reason

    Human-readable, actionable error message.

    string
    request_id
    Any of:
    string
    Example generated
    {
    "exception_details": {
    "error_code": "example",
    "validation_errors": [
    {
    "loc": [
    "example"
    ],
    "msg": "example",
    "type": "example"
    }
    ]
    },
    "reason": "example",
    "request_id": "example"
    }

    Request validation failed, or the report cannot answer it as asked (exception_details.problems).

    Media type application/json
    ReportError

    The 422 body of a report request.

    object
    exception_details
    required
    ReportExceptionDetails

    Machine-readable error context.

    object
    error_code
    Error Code

    Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. filter_unknown_field or cursor_mismatch. Safe to branch on; reason is not. Absent when the status code says all there is to say.

    string
    problems
    Problems

    Why the report cannot answer the request as asked; validation_errors covers malformed fields.

    Array<object>
    ReportProblem

    One reason a report request cannot be answered.

    object
    code
    required
    ReportProblemCode

    Stable code of the problem; switch on it, not on message.

    string
    Allowed values: too_many_groups too_many_high_cardinality_groups too_many_points ab_test_variant_without_test nothing_to_compare report_too_large
    loc
    required
    Loc

    Where in the request, as in validation_errors[].loc, e.g. ["body", "group_by", 1].

    Array
    message
    required
    Message

    What to change.

    string
    validation_errors
    Validation Errors

    Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value.

    Array<object>
    ValidationProblem

    One field-level reason a request was rejected.

    object
    loc
    required
    Loc

    Path to the offending value from the request root, e.g. ["body", "filters", 0, "operator"].

    Array
    msg
    required
    Msg

    What is wrong with the value at loc.

    string
    type
    required
    Type

    Stable machine code for the problem, e.g. missing or string_too_short.

    string
    key
    additional properties
    any
    reason
    required
    Reason

    Human-readable, actionable error message.

    string
    request_id
    Any of:
    string
    Example
    {
    "exception_details": {
    "problems": [
    {
    "code": "too_many_groups"
    }
    ]
    }
    }

    Service temporarily unavailable

    Media type application/json
    ErrorResponse

    Canonical error body — every non-2xx response uses this shape.

    object
    exception_details
    ExceptionDetails

    Machine-readable error context; empty object when there is nothing to add.

    object
    error_code
    Error Code

    Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. filter_unknown_field or cursor_mismatch. Safe to branch on; reason is not. Absent when the status code says all there is to say.

    string
    validation_errors
    Validation Errors

    Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value.

    Array<object>
    ValidationProblem

    One field-level reason a request was rejected.

    object
    loc
    required
    Loc

    Path to the offending value from the request root, e.g. ["body", "filters", 0, "operator"].

    Array
    msg
    required
    Msg

    What is wrong with the value at loc.

    string
    type
    required
    Type

    Stable machine code for the problem, e.g. missing or string_too_short.

    string
    key
    additional properties
    any
    reason
    required
    Reason

    Human-readable, actionable error message.

    string
    request_id
    Any of:
    string
    Example generated
    {
    "exception_details": {
    "error_code": "example",
    "validation_errors": [
    {
    "loc": [
    "example"
    ],
    "msg": "example",
    "type": "example"
    }
    ]
    },
    "reason": "example",
    "request_id": "example"
    }