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

# List Messages

> List and filter sent messages, with cursor pagination and an updated_since reconciliation watermark.

Lists messages for the account the key belongs to. By default rows are returned newest-first for browsing. Pass `updated_since` to turn the endpoint into an incremental [reconciliation](/api-reference/v2/messages/reconciliation) feed.

## Query Parameters

<ParamField query="sending_list_id" type="integer">
  Filter to one sending list by its ID.
</ParamField>

<ParamField query="list_number" type="string">
  Filter by sending-list phone number (E.164). Ignored when `sending_list_id` is set; an unmatched number returns zero rows.
</ParamField>

<ParamField query="to" type="string">
  Filter by recipient phone number (E.164).
</ParamField>

<ParamField query="status" type="string">
  One of `queued`, `sent`, `failed`, `dropped`, `orphaned`, `delivered`, `undelivered`.
</ParamField>

<ParamField query="date_from" type="string">
  Lower bound on `timestamp` (ISO-8601, **timezone-aware** — include `Z` or an offset). A naive datetime is rejected.
</ParamField>

<ParamField query="date_to" type="string">
  Upper bound on `timestamp` (ISO-8601, timezone-aware).
</ParamField>

<ParamField query="updated_since" type="string">
  Reconciliation watermark (ISO-8601, timezone-aware). When set, returns rows with `updated_at` greater than this value, sorted oldest-change-first and keyset-paginated on `(updated_at, id)`. See [Reconciliation](/api-reference/v2/messages/reconciliation).
</ParamField>

<ParamField query="external_location_id" type="string">
  Scope results to an owned account (yourself or a child) carrying this external location ID. An unowned location returns an empty result, never another tenant's data.
</ParamField>

<ParamField query="cursor" type="string">
  Opaque pagination cursor from a previous response's `next_cursor`.
</ParamField>

<ParamField query="limit" type="integer" default="50">
  Page size, max 200.
</ParamField>

## Response Fields

<ResponseField name="messages" type="array">
  Array of message rows.

  <Expandable title="message row">
    <ResponseField name="id" type="string">Opaque message ID (variable length).</ResponseField>
    <ResponseField name="from_number" type="string">Sending list number (E.164).</ResponseField>
    <ResponseField name="to_number" type="string">Recipient (E.164).</ResponseField>
    <ResponseField name="body" type="string">Message body.</ResponseField>
    <ResponseField name="status" type="string">Pipeline status.</ResponseField>
    <ResponseField name="status_detail" type="string">Extra status context, if any.</ResponseField>
    <ResponseField name="segment_count" type="integer">SMS segments.</ResponseField>
    <ResponseField name="encoding" type="string">`GSM-7` or `UCS-2`.</ResponseField>
    <ResponseField name="sending_list_id" type="integer">The list this was sent from.</ResponseField>
    <ResponseField name="timestamp" type="string">Send time (ISO-8601, UTC).</ResponseField>
    <ResponseField name="updated_at" type="string">Last-change time (ISO-8601, UTC); the reconciliation watermark field.</ResponseField>
    <ResponseField name="dlr_status" type="string">Normalized delivery-receipt status.</ResponseField>
    <ResponseField name="is_terminal" type="boolean">Whether the delivery status is final.</ResponseField>
    <ResponseField name="is_delivered" type="boolean">Whether delivery is confirmed.</ResponseField>
    <ResponseField name="error_code" type="string">Carrier/pipeline error code, if failed.</ResponseField>
    <ResponseField name="error_description" type="string">Human-readable error, if failed.</ResponseField>
    <ResponseField name="provider_message_id" type="string">Carrier-route message reference. Present only for providers whose IDs are globally unique; `null` otherwise.</ResponseField>
    <ResponseField name="reply_id" type="string">ID of the linked inbound reply, if any.</ResponseField>
    <ResponseField name="replied_at" type="string">When the recipient replied, if they did.</ResponseField>
    <ResponseField name="cost_state" type="string">`estimated`, `settled`, `billed`, or `refunded`.</ResponseField>
    <ResponseField name="external_ids" type="object">External IDs of the owning account (e.g. `external_location_id`).</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="pagination" type="object">
  `limit`, `has_more` (boolean), and `next_cursor` (opaque, `null` on the last page).
</ResponseField>

## Examples

<RequestExample>
  ```bash cURL theme={null}
  curl -G https://api.tracklysms.com/api/v2/messages \
    -H "X-Api-Key: trk_your_api_key" \
    --data-urlencode "updated_since=2026-07-26T00:00:00Z" \
    --data-urlencode "limit=200"
  ```

  ```python Python theme={null}
  import requests

  resp = requests.get(
      "https://api.tracklysms.com/api/v2/messages",
      headers={"X-Api-Key": "trk_your_api_key"},
      params={"status": "delivered", "limit": 200},
  )
  data = resp.json()
  for msg in data["messages"]:
      print(msg["id"], msg["status"])
  print("next:", data["pagination"]["next_cursor"])
  ```
</RequestExample>

<ResponseExample>
  ```json Success (200) theme={null}
  {
    "messages": [
      {
        "id": "a1b2c3d4",
        "from_number": "+18005551234",
        "to_number": "+14155551234",
        "body": "Hi",
        "status": "delivered",
        "status_detail": null,
        "segment_count": 1,
        "encoding": "GSM-7",
        "sending_list_id": 88,
        "timestamp": "2026-07-20T14:03:11.000000Z",
        "updated_at": "2026-07-20T14:05:00.000000Z",
        "dlr_status": "delivered",
        "is_terminal": true,
        "is_delivered": true,
        "error_code": null,
        "error_description": null,
        "provider_message_id": "O3iFf1Eu",
        "reply_id": null,
        "replied_at": null,
        "cost_state": "billed",
        "external_ids": { "external_location_id": "loc_west" }
      }
    ],
    "pagination": { "limit": 200, "has_more": true, "next_cursor": "MjAyNi0wNy..." }
  }
  ```
</ResponseExample>

## Error Codes

| HTTP Status | Error Code      | Description                                                                     |
| ----------- | --------------- | ------------------------------------------------------------------------------- |
| 400         | `invalid_param` | A query parameter is malformed (e.g. a naive datetime, bad status, bad cursor). |
| 500         | `query_failed`  | The query could not be executed (transient); retry.                             |

Also `401 invalid_credentials`.

## Next Steps

<CardGroup cols={2}>
  <Card title="Reconciliation" icon="rotate" href="/api-reference/v2/messages/reconciliation">
    Keep a local mirror in sync with the watermark.
  </Card>

  <Card title="Get a message" icon="envelope" href="/api-reference/v2/messages/get-message">
    Fetch a single message by ID.
  </Card>
</CardGroup>
