Skip to content
Last9 named a Gartner Cool Vendor in AI for SRE Observability for 2025! Read more →
Last9

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.

API Access

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.

Endpoint

The endpoint for querying logs is:

POST /logs/api/v2/query_range/json

Full URL:

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

Authentication

The API requires a Bearer token in the X-LAST9-API-TOKEN header. Use your Read Access Token for querying logs.

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

See Getting Started with API for instructions on generating tokens.

Query Parameters

The endpoint accepts the following URL parameters:

ParameterDescriptionRequiredExample
startStart time in Unix nanosecondsYes1743500000000000000
endEnd time in Unix nanosecondsYes1743510000000000000
limitMaximum number of logs to returnNo100
directionSort order: forward or backwardNobackward
stepTime step for aggregationsNo200ms
offsetPagination offsetNo0

Request Body

The request body contains the JSON pipeline:

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

Filter Operators

The JSON pipeline supports the following filter operators:

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

Logic Operators

Combine multiple conditions using logic 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"] }] }
]
}

Example Queries

Basic Query

This example queries logs from a service named “api-gateway”:

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' \
-H 'X-LAST9-API-TOKEN: Bearer <READ_ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"pipeline": [
{
"type": "filter",
"query": {
"$and": [
{ "$eq": ["service", "api-gateway"] }
]
}
}
]
}'

Replace {org} with your organization slug and <READ_ACCESS_TOKEN> with your token.

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

Query logs matching any of multiple conditions:

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

A successful response returns a JSON object with the following structure:

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

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

Discovering Available Labels

To discover what labels are available for filtering:

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 <READ_ACCESS_TOKEN>'

Discovering Label Values

To discover what values exist for a specific label:

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 <READ_ACCESS_TOKEN>'

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

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

Pipeline Stages

The JSON pipeline supports multiple stage types that can be chained together:

Filter Stage

Filter logs based on conditions:

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

Where Stage (Advanced Filters)

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

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.
  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 you’re using a Read Access Token for querying logs.
  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.

Troubleshooting

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