Skip to content
Last9
Book demo

Logs Query API

Use Logs Query API for searching and retrieving logs programmatically from your services.

Last9 provides a powerful API for querying logs from your services. This document explains how to use the Logs Query API to search and retrieve logs programmatically.

Prerequisites

You can find necessary credentials on the API Access page. For detailed instructions on generating tokens, see Getting Started with API.

Base URL

https://app.last9.io/api/v4/organizations/{org}

Replace {org} with your organization slug.

Authentication

The API requires a Bearer token in the X-LAST9-API-TOKEN header.

X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>

See Getting Started with API for instructions on generating tokens.


Query Logs

The primary endpoint for querying log data with pipeline-based filtering.

POST /logs/api/v2/query_range/json

Full URL:

https://app.last9.io/api/v4/organizations/{org}/logs/api/v2/query_range/json

Query Parameters

ParameterTypeRequiredDescription
startintegerYesStart time in Unix nanoseconds
endintegerYesEnd time in Unix nanoseconds
limitintegerNoMaximum number of logs to return
directionstringNoSort order: forward or backward
stepstringNoTime step for aggregations
offsetintegerNoPagination offset
regionstringNoCloud region to query logs from

Request Body

The request body contains the JSON pipeline:

{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [{ "$eq": ["service", "api-gateway"] }]
}
}
]
}

Example Request

curl -X POST 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v2/query_range/json?start=1743500000000000000&end=1743510000000000000&limit=100&direction=backward&region=ap-south-1' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [
{ "$eq": ["service", "api-gateway"] }
]
}
}
]
}'

Replace {org} with your organization slug, <ACCESS_TOKEN> with your token, and region with your cloud region (e.g., ap-south-1).

Response

{
"status": "success",
"data": {
"resultType": "streams",
"result": [
{
"stream": {
"service": "api-gateway",
"level": "error",
"env": "production"
},
"values": [
["1743505000000000000", "Connection timeout after 30s"],
["1743504990000000000", "Request failed: upstream unavailable"],
["1743504980000000000", "Error processing request"]
]
}
],
"stats": {
"summary": {
"bytesProcessedPerSecond": 1048576,
"linesProcessedPerSecond": 500,
"totalBytesProcessed": 2097152,
"totalLinesProcessed": 1000,
"execTime": 0.25
}
}
}
}

Discover Available Labels

Retrieve the list of labels available for filtering logs.

GET /logs/api/v1/labels

Query Parameters

ParameterTypeRequiredDescription
startintegerYesStart time in Unix nanoseconds
endintegerYesEnd time in Unix nanoseconds

Example Request

curl -X GET 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v1/labels?start=1743000000000000000&end=1743600000000000000' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>'

Response

{
"status": "success",
"data": ["service", "level", "env", "host", "namespace"]
}

Discover Label Values

Retrieve possible values for a specific label.

GET /logs/api/v1/label/{labelName}/values

Path Parameters

ParameterTypeDescription
labelNamestringThe label name

Query Parameters

ParameterTypeRequiredDescription
startintegerYesStart time in Unix nanoseconds
endintegerYesEnd time in Unix nanoseconds

Example Request

curl -X GET 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v1/label/service/values?start=1743000000000000000&end=1743600000000000000' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>'

Response

{
"status": "success",
"data": [
"api-gateway",
"user-service",
"payment-service",
"notification-service"
]
}

Pipeline Syntax

The Logs API uses a JSON pipeline format for filtering log data. Multiple stages can be chained together.

Filter Stage

Filter logs based on conditions:

{
"type": "filter",
"query": {
"$and": [{ "$eq": ["service", "api-gateway"] }]
}
}

Where Stage

Add additional filter conditions with OR/NOT logic:

{
"type": "where",
"query": {
"$or": [{ "$eq": ["level", "error"] }, { "$eq": ["level", "warn"] }]
}
}

Parse Stage

Extract fields from log body:

{
"type": "parse",
"parser": "json",
"field": "body",
"labels": {
"user_id": null,
"request_id": null
}
}

Filter Operators

OperatorDescriptionExample
$eqExact match{ "$eq": ["service", "api-gateway"] }
$neqDoes not match{ "$neq": ["level", "debug"] }
$containsSubstring match{ "$contains": ["body", "error"] }
$notcontainsDoes not contain substring{ "$notcontains": ["body", "test"] }
$regexRegular expression match{ "$regex": ["path", "/api/v[0-9]+"] }
$notregexDoes not match regex{ "$notregex": ["path", "health"] }
$gtNumeric greater than{ "$gt": ["status_code", "400"] }
$ltNumeric less than{ "$lt": ["status_code", "500"] }
$gteNumeric greater than or equal{ "$gte": ["duration", "1000"] }
$lteNumeric less than or equal{ "$lte": ["duration", "5000"] }

Logical Operators

Combine multiple conditions using logical operators:

AND (All conditions must match)

{
"$and": [
{ "$eq": ["service", "api-gateway"] },
{ "$contains": ["body", "error"] }
]
}

OR (Any condition can match)

{
"$or": [{ "$eq": ["level", "error"] }, { "$eq": ["level", "fatal"] }]
}

NOT (Negate conditions)

{
"$not": [{ "$and": [{ "$eq": ["level", "debug"] }] }]
}

Combining Logical Operators

Nest logical operators to build complex queries:

{
"$and": [
{ "$eq": ["service", "api-gateway"] },
{ "$or": [{ "$eq": ["level", "error"] }, { "$eq": ["level", "fatal"] }] }
]
}

Common Query Patterns

Basic Service Query

Query logs from a specific service:

curl -X POST 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v2/query_range/json?start=1743500000000000000&end=1743510000000000000&limit=100&direction=backward&region=ap-south-1' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [
{ "$eq": ["service", "api-gateway"] }
]
}
}
]
}'

Filter by Multiple Conditions

Query logs with multiple conditions (service AND log level):

curl -X POST 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v2/query_range/json?start=1743500000000000000&end=1743510000000000000&limit=100' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [
{ "$eq": ["service", "payment-service"] },
{ "$eq": ["level", "error"] }
]
}
}
]
}'

Search for logs containing specific text:

curl -X POST 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v2/query_range/json?start=1743500000000000000&end=1743510000000000000&limit=50' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [
{ "$eq": ["service", "api-gateway"] },
{ "$contains": ["body", "timeout"] }
]
}
}
]
}'

Regex Pattern Matching

Use regex to match complex patterns:

curl -X POST 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v2/query_range/json?start=1743500000000000000&end=1743510000000000000&limit=100' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [
{ "$eq": ["service", "api-gateway"] },
{ "$regex": ["body", "error.*timeout|connection.*refused"] }
]
}
}
]
}'

OR Conditions with Where Stage

Query logs matching any of multiple conditions using a where stage:

curl -X POST 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v2/query_range/json?start=1743500000000000000&end=1743510000000000000&limit=100' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [
{ "$eq": ["service", "api-gateway"] }
]
}
},
{
"type": "where",
"query": {
"$or": [
{ "$eq": ["level", "error"] },
{ "$eq": ["level", "fatal"] }
]
}
}
]
}'

Response Format

Success Response Structure

A successful response returns a JSON object with a status field and a data object containing the results:

{
"status": "success",
"data": {
"resultType": "streams",
"result": [
{
"stream": {
"service": "api-gateway",
"level": "error",
"env": "production"
},
"values": [
["1743505000000000000", "Connection timeout after 30s"],
["1743504990000000000", "Request failed: upstream unavailable"],
["1743504980000000000", "Error processing request"]
]
}
],
"stats": {
"summary": {
"bytesProcessedPerSecond": 1048576,
"linesProcessedPerSecond": 500,
"totalBytesProcessed": 2097152,
"totalLinesProcessed": 1000,
"execTime": 0.25
}
}
}
}

Empty Results

If no logs are found, the result field will be null:

{
"status": "success",
"data": {
"resultType": "streams",
"result": null,
"stats": {
"summary": {
"bytesProcessedPerSecond": 0,
"linesProcessedPerSecond": 0,
"totalBytesProcessed": 0,
"totalLinesProcessed": 0,
"execTime": 0
}
}
}
}

Advanced Usage

Time Range Conversion

The API uses Unix nanoseconds for timestamps. To convert:

# Current time in nanoseconds
echo $(($(date +%s) * 1000000000))
# Time from 1 hour ago in nanoseconds
echo $((($(date +%s) - 3600) * 1000000000))
# Convert seconds to nanoseconds
SECONDS=1743500000
NANOSECONDS=$((SECONDS * 1000000000))

Querying From Specific Indices

To query logs from a specific index, add the index parameter:

For Physical Indices

curl -X POST 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v2/query_range/json?start=1743500000000000000&end=1743510000000000000&limit=100&index=physical_index:Pt_prod_k8s' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [
{ "$eq": ["service", "api-gateway"] }
]
}
}
]
}'

For Rehydration Indices

curl -X POST 'https://app.last9.io/api/v4/organizations/{org}/logs/api/v2/query_range/json?start=1743500000000000000&end=1743510000000000000&limit=100&index=rehydration_index:Rh_prod_archive' \
-H 'X-LAST9-API-TOKEN: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [
{ "$eq": ["service", "api-gateway"] }
]
}
}
]
}'

The format for the index parameter is:

  • physical_index:<index_name> for physical indices
  • rehydration_index:<index_name> for rehydration indices

Error Handling

Error Response Format

{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message"
}
}

Common Errors

HTTP StatusErrorDescription
400Bad RequestInvalid query parameters or pipeline
401Authorization token is expiredAccess token has expired
403ForbiddenInsufficient permissions
404Not FoundResource not found
500Internal Server ErrorServer-side error

Debugging Common Scenarios

No Data Returned

If your query returns no data ("result": null), check the following:

  1. Field names: Verify the field names are correct. Field names are case-sensitive. Use Discover Available Labels to find valid fields.
  2. Time range: Ensure your start and end timestamps cover a period where logs exist. Remember timestamps are in nanoseconds.
  3. Data retention: Check if the queried time range is within your organization’s data retention period.
  4. Pipeline syntax: Make sure your JSON pipeline is correctly formatted.

Invalid Pipeline Error

If you receive a pipeline parsing error:

  1. JSON syntax: Ensure your pipeline is valid JSON.
  2. Operator format: Each operator should be an array with exactly 2 elements: ["field", "value"].
  3. Stage type: Verify the type field is one of: filter, where, parse.

Authorization Issues

If you receive authentication errors:

  1. Token validity: Verify your access token is valid and not expired. Tokens expire in 24 hours.
  2. Token scope: Ensure your access token has the required permissions.
  3. Header format: The header must be X-LAST9-API-TOKEN: Bearer <TOKEN> (not Authorization).
  4. Token refresh: If expired, generate a new token using the refresh token. See Getting Started with API.

API Endpoints Summary

MethodEndpointDescription
POST/logs/api/v2/query_range/jsonQuery logs with pipeline
GET/logs/api/v1/labelsDiscover available labels
GET/logs/api/v1/label/{labelName}/valuesDiscover label values

Troubleshooting

Please get in touch with us on Discord or Email if you have any questions.