> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://api-docs.papertracc.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://api-docs.papertracc.com/_mcp/server.

# List Comments

GET https://api.papertracc.com/v1/store/articles/{handle}/comments

Lists the approved comments on an article, oldest first, each with the merchant's reply if there is one. Comments the merchant has moved to pending, marked as spam or deleted are not returned. Returns an empty list when comments are turned off for the blog.

**Keys:** public key or secret key.

Reference: https://api-docs.papertracc.com/storefront/blog/comments/list-comments

## Authentication

- `Authorization` header (bearer token, required) — Bearer authentication of the form `Bearer <token>`, where token is your auth token.
- `identity` header (required) — The identity for the active workspace.

## Servers

- `https://api.papertracc.com/v1` (Production, default)
- `https://api.papertracc.com/rc-1` (Release Candidate 1)

## Request

### Path parameters

- `handle` (string, required) — The record's handle.

### Query parameters

- `page` (integer, optional, default: 1) — Page number, starting at 1. Ignored when `last_id` and `keyset` are sent.
- `size` (integer, optional, default: 20) — Records per page. Defaults to 20, capped at 50.
- `last_id` (string, optional) — The `last_id` from the previous response. Send it with `keyset` to get the next page. Omit both for the first page.
- `keyset` (string, optional) — The `keyset` from the previous response. Send it with `last_id`. Copy it as it is; don't build or change it.

## Response

### 200

Successfully fetched comments

- `status` (boolean, required) — Always `true` on success.
- `message` (string, required) — Human-readable outcome.
- `count` (integer, required) — Records that match the request across all pages.
- `pages` (integer, required) — Total pages at the requested size.
- `page` (integer, required) — The page returned. `0` when the request used `last_id` and `keyset`.
- `keyset` (string, required, nullable) — Send back as `keyset`, with `last_id`, to get the next page. `null` when there are no more records.
- `last_id` (string, required, nullable) — Send back as `last_id`, with `keyset`, to get the next page. `null` when there are no more records.
- `data` (list of Comment, required)

## Errors

### 400 Bad Request Error

The `channel` header is missing, the body is not valid JSON, or a field failed validation.

- `status` (boolean, required) — Always `false` on a failed request.
- `message` (string, required) — What went wrong, written for the shopper or the developer.
- `error_code` (string, optional) — Present on `402` responses.

### 401 Unauthorized Error

The API key is missing or invalid, the key owner is no longer an active member of the organisation, or the key owner is not a member of the workspace in the `identity` header.

- `status` (boolean, required) — Always `false` on a failed request.
- `message` (string, required) — What went wrong, written for the shopper or the developer.
- `error_code` (string, optional) — Present on `402` responses.

### 402 Payment Required Error

The workspace's subscription plan does not include the Open API.

- `status` (boolean, required) — Always `false` on a failed request.
- `message` (string, required) — What went wrong, written for the shopper or the developer.
- `error_code` (string, optional) — Present on `402` responses.

### 404 Not Found Error

The sales channel, or the record the request names, does not exist or is not published to this channel.

- `status` (boolean, required) — Always `false` on a failed request.
- `message` (string, required) — What went wrong, written for the shopper or the developer.
- `error_code` (string, optional) — Present on `402` responses.

## Types

### Comment

- `id` (string, required)
- `name` (string, required) — Commenter's name.
- `body` (string, required) — Plain-text comment.
- `reply` (string, required, nullable) — The merchant's reply to the comment, in plain text. `null` when there is none.
- `replied_at` (datetime, required, nullable) — When the reply was last saved. `null` when there is no reply.
- `created_at` (datetime, required)

## Examples

**Response**

```json
{
  "status": true,
  "message": "Successfully fetched comments",
  "count": 1,
  "pages": 1,
  "page": 1,
  "keyset": "string",
  "last_id": "string",
  "data": [
    {
      "id": "string",
      "name": "Bola",
      "body": "string",
      "reply": "string",
      "replied_at": "2024-01-15T09:30:00Z",
      "created_at": "2024-01-15T09:30:00Z"
    }
  ]
}
```

**SDK Code**

```python
import requests

url = "https://api.papertracc.com/v1/store/articles/handle/comments"

headers = {
    "channel": "channel",
    "Authorization": "Bearer <apiKey>"
}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.papertracc.com/v1/store/articles/handle/comments';
const options = {method: 'GET', headers: {channel: 'channel', Authorization: 'Bearer <apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.papertracc.com/v1/store/articles/handle/comments"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("channel", "channel")
	req.Header.Add("Authorization", "Bearer <apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.papertracc.com/v1/store/articles/handle/comments")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["channel"] = 'channel'
request["Authorization"] = 'Bearer <apiKey>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.papertracc.com/v1/store/articles/handle/comments")
  .header("channel", "channel")
  .header("Authorization", "Bearer <apiKey>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.papertracc.com/v1/store/articles/handle/comments', [
  'headers' => [
    'Authorization' => 'Bearer <apiKey>',
    'channel' => 'channel',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.papertracc.com/v1/store/articles/handle/comments");
var request = new RestRequest(Method.GET);
request.AddHeader("channel", "channel");
request.AddHeader("Authorization", "Bearer <apiKey>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "channel": "channel",
  "Authorization": "Bearer <apiKey>"
]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.papertracc.com/v1/store/articles/handle/comments")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```