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

> ## Agent Instructions
> Unkey is two separate products. Compute builds, deploys, and runs apps behind a gateway. API Management issues API keys, enforces rate limits, manages identities and permissions, and reports usage. Say which product a page belongs to; a reader can use either without the other.
> Every Unkey API endpoint is an HTTP POST to https://api.unkey.com/v2/{service}.{procedure} with a root key in the Authorization: Bearer header. Root keys are workspace scoped.
> Error codes have the form err:{system}:{category}:{specific} and each has a page at /errors/{system}/{category}/{specific}.
> The word environment means production or preview in Compute. Rate limiting has four meanings on this site; the glossary lists them.

# Query gateway requests and runtime logs

> Query gateway requests and runtime logs with SQL through the analytics API.

export const ProductLink = ({product, href, title, children}) => {
  const productNames = {
    compute: "Compute",
    "api-management": "API Management",
    platform: "Platform"
  };
  return <div className="card unkey-product-card" data-card-href={href}>
      <div data-component-part="card-content-container">
        <h2 data-component-part="card-title">
          <a className="unkey-product-card-title" href={href}>
            {title}
          </a>
        </h2>
        <div data-component-part="card-content">
          <strong>{productNames[product]} docs.</strong> {children}
        </div>
      </div>
    </div>;
};

<ProductLink product="api-management" href="/docs/api-management/analytics/query-language" title="Analytics query language">
  The SQL you can use, query quotas, and query errors are documented in API Management. Here we cover the two Compute tables.
</ProductLink>

You can query your <Tooltip tip="A Compute app: a deployable service inside a project. Not 'your application' in general.">app</Tooltip>'s [request log](/docs/compute/observe/requests) and [runtime logs](/docs/compute/observe/runtime-logs) with SQL, to build your own dashboards, alerts, or exports. Each table has its own endpoint and permission.

| Table | Endpoint | Root key permission |
| - | - | - |
| `gateway_requests_v1` | `POST /v2/analytics.getGatewayRequests` | `project.*.read_gateway_requests` |
| `runtime_logs_v1` | `POST /v2/analytics.getRuntimeLogs` | `project.*.read_runtime_logs` |

<Note>
  You need a root key with the wildcard (`project.*`) permission for the table. A permission for a single project doesn't work here. Create the key under **Settings > Root Keys** and send it as `Authorization: Bearer <root key>`. See [Root keys](/docs/platform/root-keys/overview).
</Note>

## Query rules

* Only `SELECT` runs. CTEs, subqueries, `UNION`, and `EXCEPT` are fine.
* You only ever see your own workspace's data. To narrow further, filter on `project_id`, `app_id`, or `environment_id`.
* The time range can't reach back further than your plan's log query range (3 days on Starter, 7 on Pro, 14 on Business), or the query fails with [`query_range_exceeds_retention`](/docs/errors/user/bad_request/query_range_exceeds_retention). See [Compute limits](/docs/compute/configure/limits#log-query-range).
* A result larger than 4 MiB fails with [`query_memory_limit_exceeded`](/docs/errors/user/unprocessable_entity/query_memory_limit_exceeded).

The response is `{"meta":{"requestId":...},"data":[...]}` with one object per row.

## Examples

<CodeGroup>
  ```bash curl theme={"system"}
  curl -X POST https://api.unkey.com/v2/analytics.getGatewayRequests \
    -H "Authorization: Bearer $UNKEY_ROOT_KEY" \
    -H "Content-Type: application/json" \
    -d '{"query":"SELECT path, count() AS total FROM gateway_requests_v1 WHERE response_status >= 500 AND time >= toUnixTimestamp64Milli(now64(3) - INTERVAL 24 HOUR) GROUP BY path ORDER BY total DESC LIMIT 10"}'
  ```

  ```bash CLI theme={"system"}
  unkey api analytics get-gateway-requests \
    --query="SELECT path, count() AS total FROM gateway_requests_v1 WHERE response_status >= 500 AND time >= toUnixTimestamp64Milli(now64(3) - INTERVAL 24 HOUR) GROUP BY path ORDER BY total DESC LIMIT 10"
  ```
</CodeGroup>

<CodeGroup>
  ```bash curl theme={"system"}
  curl -X POST https://api.unkey.com/v2/analytics.getRuntimeLogs \
    -H "Authorization: Bearer $UNKEY_ROOT_KEY" \
    -H "Content-Type: application/json" \
    -d '{"query":"SELECT time, severity, message FROM runtime_logs_v1 WHERE lower(message) LIKE '"'"'%timeout%'"'"' ORDER BY time DESC LIMIT 100"}'
  ```

  ```bash CLI theme={"system"}
  unkey api analytics get-runtime-logs \
    --query="SELECT time, severity, message FROM runtime_logs_v1 WHERE lower(message) LIKE '%timeout%' ORDER BY time DESC LIMIT 100"
  ```
</CodeGroup>

`time` is a Unix timestamp in milliseconds in both tables, so the examples compare it with `toUnixTimestamp64Milli(...)`.

## gateway\_requests\_v1

One row per request that reached your app. Requests a policy rejected aren't here. See [Request logs](/docs/compute/observe/requests).

<ResponseField name="request_id" type="String">The request ID, also sent as `X-Unkey-Request-Id`.</ResponseField>
<ResponseField name="time" type="Int64">Unix timestamp in milliseconds when the gateway received the request.</ResponseField>
<ResponseField name="workspace_id" type="String">Your workspace, the one the root key belongs to.</ResponseField>
<ResponseField name="project_id" type="String">The project.</ResponseField>
<ResponseField name="app_id" type="String">The app.</ResponseField>
<ResponseField name="environment_id" type="String">The environment.</ResponseField>
<ResponseField name="deployment_id" type="String">The deployment that served the request.</ResponseField>
<ResponseField name="instance_id" type="String">The instance that served the request.</ResponseField>
<ResponseField name="region" type="String">The region of the gateway that served the request.</ResponseField>
<ResponseField name="method" type="String">Upper-case HTTP method.</ResponseField>
<ResponseField name="host" type="String">The requested hostname.</ResponseField>
<ResponseField name="path" type="String">The request path without the query string.</ResponseField>
<ResponseField name="query_string" type="String">The raw query string. Empty unless a logging policy captured query data.</ResponseField>
<ResponseField name="query_params" type="Map(String, Array(String))">Parsed query parameters. Empty unless a logging policy captured query data.</ResponseField>
<ResponseField name="request_headers" type="Array(String)">`Key: Value` strings. Empty unless a logging policy captured request headers. API keys are redacted.</ResponseField>
<ResponseField name="request_body" type="String">Up to 1 MiB. Empty unless a logging policy captured the request body.</ResponseField>
<ResponseField name="response_status" type="Int32">The status the client received.</ResponseField>
<ResponseField name="response_headers" type="Array(String)">`Key: Value` strings. Empty unless a logging policy captured response headers.</ResponseField>
<ResponseField name="response_body" type="String">Up to 1 MiB. Empty unless a logging policy captured the response body.</ResponseField>
<ResponseField name="user_agent" type="String">Empty unless a logging policy captured request headers.</ResponseField>
<ResponseField name="ip_address" type="String">The client IP. Empty unless a logging policy captured request headers.</ResponseField>
<ResponseField name="total_latency" type="Int64">Milliseconds from receipt to the end of the response.</ResponseField>
<ResponseField name="instance_latency" type="Int64">Milliseconds spent by your instance.</ResponseField>
<ResponseField name="gateway_latency" type="Int64">`total_latency` minus `instance_latency`.</ResponseField>

## runtime\_logs\_v1

One row per log line from your instances. Structured attributes are in `attributes_text`, as a JSON string.

<ResponseField name="log_id" type="String">Stable identifier of the log line.</ResponseField>
<ResponseField name="time" type="Int64">Unix timestamp in milliseconds when the line was written.</ResponseField>
<ResponseField name="inserted_at" type="Int64">Unix timestamp in milliseconds when the line was stored. Filter on it as well as on `time` to make queries over a wide range faster.</ResponseField>
<ResponseField name="severity" type="String">Lower-case severity parsed from the line, `info` when none was found.</ResponseField>
<ResponseField name="message" type="String">The message, with color codes removed.</ResponseField>
<ResponseField name="workspace_id" type="String">Your workspace.</ResponseField>
<ResponseField name="project_id" type="String">The project.</ResponseField>
<ResponseField name="environment_id" type="String">The environment.</ResponseField>
<ResponseField name="app_id" type="String">The app.</ResponseField>
<ResponseField name="deployment_id" type="String">The deployment whose instance wrote the line.</ResponseField>
<ResponseField name="region" type="String">The region the instance ran in.</ResponseField>
<ResponseField name="attributes_text" type="String">The parsed attributes as a JSON string. Search it with `lower(attributes_text) LIKE '%...%'`, which the table indexes.</ResponseField>
