Skip to main content
Search log lines with the Public API (V2). This is the way to query logs programmatically, including structured (JSON) log data. The GraphQL API does not expose log lines.
If you’re building an AI agent or assistant, AppSignal MCP exposes log search and other monitoring data to agents directly, without calling this API yourself.

Finding your site and source IDs

A log search needs a site_id and one or more source_ids. Both appear in the URL of a log source in AppSignal. Open a log source and read them from the path:
https://appsignal.com/<organization>/sites/<SITE_ID>/logs/sources/<SOURCE_ID>

Search log lines

POST /api/v2/logs/lines

Request body

FieldTypeRequiredDescription
site_idstringYesSite to query.
source_idsstring[]YesLog source IDs to search. At least one is required.
querystringYesQuery string that filters log lines. See query language.
use_expressionsbooleanNoWhen true, parse query with the expression-based query language.
fromstring (ISO 8601)NoTime range start. Defaults to the retention lower bound.
tostring (ISO 8601)NoTime range end. Defaults to the current time.
paginationobjectYesPagination cursor and direction (see the following section).
Set use_expressions to true to use the expression-based query language documented on this page. pagination takes per_page (number), order (ASC or DESC), and a cursor with a time field (an ISO 8601 timestamp, or null for the first page). To fetch the next page, send the timestamp of the last line you received as the cursor.time of the next request.

Example request

curl --request POST \
  --url "https://appsignal.com/api/v2/logs/lines" \
  --header "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  --header "Content-Type: application/json" \
  --data @body.json
{
  "site_id": "YOUR-SITE-ID",
  "source_ids": ["YOUR-LOG-SOURCE-ID"],
  "query": "severity=error status_code=401",
  "use_expressions": true,
  "from": "2026-06-22T00:00:00Z",
  "to": "2026-06-22T23:59:59Z",
  "pagination": {
    "per_page": 100,
    "order": "DESC",
    "cursor": { "time": null }
  }
}

Response

The endpoint returns an array of log lines. Each line includes these fields:
FieldTypeDescription
idstringLog line identifier.
timestampstring (ISO 8601)When the log line was recorded.
source_idstringLog source the line belongs to.
severitystringSeverity level, such as info or error.
messagestringThe log message body.
hostnamestringHost that produced the line.
groupstringLog group.
jsonstringThe raw structured (JSON) body of the log line, when available.
attributesobjectLegacy typed key-value attributes. Being phased out — prefer json.
Structured fields such as status_code, path, or detail live in the json field, not in attributes. Query them by their exact key as it appears in your log’s JSON (for example, status_code=401 or detail.message:"required").

Query language

When use_expressions is true, the query string supports boolean logic over log line fields and the structured json body.

Operators

OperatorNameBehavior
=EqualsExact match.
!=Not equalsExact non-match.
:ContainsSubstring match.
!:Not containsSubstring non-match.
>Greater thanNumeric comparison.
>=Greater or equalNumeric comparison.
<Less thanNumeric comparison.
<=Less or equalNumeric comparison.
String operators (=, !=, :, !:) compare both sides as strings. Numeric operators (>, >=, <, <=) cast both sides to a number; if a value cannot be parsed as a number, that log line is skipped.

Combining expressions

  • AND / OR: severity=error AND hostname:web, or severity=info OR severity=warn.
  • Implicit AND: a space between expressions means AND. severity=error hostname:web is the same as severity=error AND hostname:web.
  • Nesting with parentheses: message:"API request" AND (severity=info OR severity=warn).
  • Precedence: AND binds tighter than OR.
Any operator can be used inside an OR group, not only equality — for example, message:"API request" AND (user.id>1000 OR user.email:"example.com").

Fields

Given a log line with this JSON body:
{
  "message": "API request completed",
  "severity": "info",
  "duration": 156.7,
  "status_code": 200,
  "user": {
    "id": 12345,
    "roles": ["admin", "developer"],
    "location.country": "US"
  }
}
  • Base fields: message, severity, hostname, and group match the log line’s top-level fields.
  • Nested JSON: use dot notation to reach into the json body, such as status_code=200 or user.id=12345.
  • JSON arrays: index array elements, such as user.roles.0=admin.
  • Escaped dots: to match a literal dot in a key name, escape it. user.location\.country=US matches the key location.country inside user, rather than a nested country object.
  • Default field: a bare term with no field matches message with the contains operator. timeout is the same as message:timeout.
You do not need to prepend attributes. or append a type suffix to query a field — reference it directly by name. See legacy query syntax if you have older queries.

Quoting values

Wrap values that contain spaces or parentheses in double quotes. Otherwise the space splits the value into separate expressions joined by implicit AND.
message:"hello world"           matches message containing "hello world"
message:hello world             parsed as message:hello AND message:world
severity=error message:"oh no"  severity equals error AND message contains "oh no"
Only double quotes are recognized. Inside a quoted value, escape a quote with \" and a backslash with \\. For example, message:"value with \"quotes\"" matches the text value with "quotes".

Legacy query syntax

Older queries stored attributes as typed fields, so they had to prepend attributes., append a type suffix (_string, _int), and use a [ ] list for multiple values. The current query language removes all three — reference fields directly and use OR instead of a list:
message:"API request" attributes.user_id_int=12345 severity=[info, warn]
A query that still uses the legacy syntax returns a 422 with the legacy_log_query_syntax error slug.