---
title: "Store, reviews & issues · Scrape.ly Docs"
description: "Search Store Actors, the public Store API, reviews, issues, and page views."
url: https://scrape.ly/docs/api-store
markdown: https://scrape.ly/docs/api-store.md
---

# Store, reviews & issues

> Search Store Actors, the public Store API, reviews, issues, and page views.

Browse public Actors in the Scrape.ly Store, read and submit reviews, and manage issue reports on public Actors. Paths are relative to `https://api.scrape.ly`. See [API Reference](https://scrape.ly/docs/api-overview.md) for authentication and response conventions, and [Find Actors in the Store](https://scrape.ly/docs/store.md) for the dashboard view.

On endpoints marked "authentication optional", omit the `Authorization` header to call them anonymously. An invalid token still returns `401`.

## List Store Actors

```
GET /v2/store/actors
GET /v2/store
```

Lists published, non-deprecated Actors. `/v2/store` is the same endpoint under the path existing Store API clients use. Authentication optional. Existing Store API clients can send the same parameters.

```bash
curl "https://api.scrape.ly/v2/store/actors?search=scraper&category=ECOMMERCE&sortBy=popularity&limit=20"
```

| Query param | Type | Default | Description |
| --- | --- | --- | --- |
| `search` | string | — | Substring match on Actor `name`, `title`, or `description`. |
| `category` | string | — | Exact category value stored on the Actor. |
| `username` | string | — | Only Actors owned by this username. |
| `sortBy` | string | `popularity` | `popularity` (monthly active users), `newest`, `rating`, `updated`, or `name`. `relevance` and `users` map to `popularity`, `lastUpdate` to `updated`. Unknown values fall back to `popularity`. |
| `desc` | boolean | `false` | `true` or `1` reverses the sort order. |
| `pricingModel` | string | — | `free` for unpaid Actors, or `PAY_PER_EVENT`. |
| `industry`, `outputType`, `site` | string | — | Use-case filters with the values listed for the public store below; the three combine. An unknown industry or output type, or a malformed domain, returns `400 invalid-input`. |
| `facets` | boolean | `false` | `1` adds `facets: { industries, outputTypes, sites }`, each a list of `{ name, count }` over the other filters. |
| `excludeKnown` | boolean | `false` | `true` or `1` hides Actors you own or have run. Ignored without authentication. |
| `includeUnrunnableActors` | boolean | — | Accepted for compatibility with existing clients and has no effect. Non-boolean values return `400 invalid-input`. |
| `allowsAgenticUsers` | boolean | — | `true` lists only Actors whose owners allow AI agents, `false` only Actors that don't. Non-boolean values return `400 invalid-input`. |
| `responseFormat` | string | `full` | `full` returns the response below. `agent` returns compact entries for AI agents and LLM tools (see [Store for AI agents](#store-for-ai-agents)). Other values return `400 invalid-input`. |
| `limit` | number | 20 | Page size, max **100**. |
| `offset` | number | 0 | Pagination offset. |

```json
{
  "data": {
    "total": 128,
    "offset": 0,
    "limit": 20,
    "count": 20,
    "desc": false,
    "items": [
      {
        "id": "eLN5rTB8sYTBWdfzx",
        "userId": "uSeRiDeXaMpLe123",
        "name": "example-scraper",
        "username": "janedoe",
        "title": "Example Scraper",
        "description": "Scrapes example.com",
        "categories": ["ECOMMERCE"],
        "pictureUrl": "https://...",
        "userPictureUrl": "https://...",
        "ownerName": "Jane Doe",
        "ownerImageUrl": "https://...",
        "url": "https://...",
        "readmeSummary": "...",
        "avgRating": 4.5,
        "totalReviews": 12,
        "isUnderMaintenance": false,
        "actorPermissionLevel": "LIMITED_PERMISSIONS",
        "pricingInfos": [{ "pricingModel": "FREE" }],
        "currentPricingInfo": { "pricingModel": "FREE" },
        "stats": {
          "totalRuns": null,
          "totalUsers": null,
          "totalUsers7Days": null,
          "totalUsers30Days": 90,
          "totalUsers90Days": null
        },
        "createdAt": "2026-01-15T10:30:00.000Z",
        "modifiedAt": "2026-08-01T12:00:00.000Z"
      }
    ]
  }
}
```

`stats.totalUsers30Days` is the Actor's monthly active users: every distinct user who started a run in the last 30 days. It is the only usage number the Store publishes. Run counts, total users and bookmark counts are not public: the other `stats` keys are kept for compatibility with existing clients and are always `null`. Owners see their own Actor's numbers in [Insights](https://scrape.ly/docs/api-earnings.md).

`description` is truncated to 280 characters.

The list doesn't include the icon itself. Use `pictureUrl` (`null` when the Actor has no icon) directly as an image URL. It is versioned by the icon's content and can be cached for a long time (see [Get Actor icon and picture](https://scrape.ly/docs/api-actors.md#get-actor-icon-and-picture)).

```api-test
{ "method": "GET", "path": "/v2/store/actors?limit=5" }
```

To bookmark a Store Actor, see [Bookmarks](https://scrape.ly/docs/api-misc.md#bookmarks).

## Store for AI agents

```
GET /v2/store?responseFormat=agent
```

Returns Store entries an AI agent can act on without a second lookup. All the list parameters above work the same way, so an agent can search with `search=...` and page with `limit` and `offset`. Each entry has the Store's standard fields (`id`, `title`, `name`, `username`, `description`, `notice`, `badge`, `categories`, `stats`) plus:

| Field | Description |
| --- | --- |
| `actor` | `username/name`, the identifier `call-actor` and the CLI take. |
| `purpose` | One line on what the Actor does. |
| `inputs` | Up to 8 key input fields, required ones first, each with `name`, `type`, `required`, a short `description`, and an `example` (or `enum`). Secret fields never carry an example. |
| `inputExample` | A complete input to start from: the Actor's example input, or its prefilled values. |
| `outputFields` | The fields each result item has, from the Actor's dataset schema (empty when the Actor doesn't declare one). |
| `pricing` | `model`, a plain-language `summary` (for example "Pay per event: $2.50 per 1,000 results; $0.005 per run start. Plus platform usage (compute, storage, proxy)."), and the `events` with their USD prices for pay-per-event Actors. |
| `rating` | `average` and `count` of user reviews. |
| `health` | `ok`, or `under-maintenance` when the Actor is currently marked under maintenance. `badge` and `notice` say the same in words (`UNDER_MAINTENANCE`, `DEPRECATED`). |
| `run` | How to run it: `api` is the synchronous run endpoint that returns the result items, and `mcp` is the [MCP](https://scrape.ly/docs/mcp.md) `call-actor` call. |
| `url` | The Actor's Store page. |

The response also has a `usage` object with authentication and calling instructions, sent once instead of in every entry.

The agent format only lists Actors whose owners allow AI agents (`allowsAgenticUsers`, on by default). Sending `allowsAgenticUsers=false` with `responseFormat=agent` returns no items.

```bash
curl "https://api.scrape.ly/v2/store?responseFormat=agent&search=google%20maps&limit=3"
```

```json
{
  "data": {
    "total": 4,
    "offset": 0,
    "limit": 3,
    "count": 3,
    "desc": false,
    "items": [
      {
        "id": "eLN5rTB8sYTBWdfzx",
        "title": "Example Scraper",
        "name": "example-scraper",
        "username": "janedoe",
        "actor": "janedoe/example-scraper",
        "description": "Scrapes product listings from example.com. Returns title, price and URL.",
        "purpose": "Scrapes product listings from example.com.",
        "notice": null,
        "badge": null,
        "categories": ["ECOMMERCE"],
        "stats": { "totalUsers30Days": 90 },
        "inputs": [
          { "name": "startUrls", "type": "array of {url}", "required": true, "description": "Category or search pages to scrape.", "example": [{ "url": "https://example.com/shoes" }] },
          { "name": "maxItems", "type": "integer", "required": false, "description": "Stop after this many products.", "example": 50 }
        ],
        "inputExample": { "startUrls": [{ "url": "https://example.com/shoes" }], "maxItems": 50 },
        "outputFields": ["title", "price", "url"],
        "pricing": {
          "model": "PAY_PER_EVENT",
          "summary": "Pay per event: $2.50 per 1,000 results. Plus platform usage (compute, storage, proxy).",
          "events": [{ "event": "scrapely-default-dataset-item", "title": "Result", "priceUsd": 0.0025 }]
        },
        "rating": { "average": 4.5, "count": 12 },
        "health": "ok",
        "run": {
          "api": "POST https://api.scrape.ly/v2/acts/janedoe~example-scraper/run-sync-get-dataset-items",
          "mcp": "call-actor {\"actor\":\"janedoe/example-scraper\",\"input\":{...}}"
        },
        "url": "https://..."
      }
    ],
    "usage": {
      "auth": "Send your Scrape.ly API token as \"Authorization: Bearer $SCRAPELY_TOKEN\".",
      "runApi": "POST the input JSON to an item's run.api URL ...",
      "mcp": "MCP server: https://api.scrape.ly/mcp ...",
      "pricing": "Prices are in USD and are charged to the token owner's prepaid balance."
    }
  }
}
```

```api-test
{ "method": "GET", "path": "/v2/store?responseFormat=agent&limit=3" }
```

### Opting an Actor out

Actors allow AI agents by default. As the owner, turn it off with `PUT /v2/actors/{actorId}` and `{ "allowsAgenticUsers": false }` (see [Update Actor](https://scrape.ly/docs/api-actors.md#update-actor)). The Actor then no longer appears in the agent format, and runs paid with an agent payment protocol (x402) return `403 agentic-users-not-allowed`. This covers every way to start it: direct runs, saved tasks (also creating one), schedules and automations of x402 wallet accounts, Standby requests, metamorph, and resurrect. Calls with an API token aren't affected, and you can always run your own Actor.

## Public Store API

Read-only, unauthenticated endpoints that power the public Store pages. Use them for anonymous browsing, search, and SEO. Responses are cached for up to an hour, so new reviews or stats can take that long to appear.

- No `Authorization` header is needed (or used).
- Responses carry `Cache-Control: public, max-age=300, s-maxage=3600, stale-while-revalidate=86400` and an `ETag`. Send `If-None-Match` to get `304 Not Modified`.
- All JSON responses are wrapped in `data`.

```
GET /v2/public/store/overview
GET /v2/public/store/actors
GET /v2/public/store/actors/{username}/{name}
GET /v2/public/store/sitemap
GET /v2/public/store/examples
GET /v2/public/actors/{actorId}/picture.webp
```

### Actor cards

Lists and sections return **cards** with this shape:

```json
{
  "id": "eLN5rTB8sYTBWdfzx",
  "username": "janedoe",
  "name": "example-scraper",
  "title": "Example Scraper",
  "description": "Scrapes example.com",
  "categories": ["ECOMMERCE"],
  "industries": ["e-commerce"],
  "outputTypes": ["products", "prices"],
  "targetSites": ["example.com"],
  "pictureUrl": "https://api.scrape.ly/v2/actors/eLN5rTB8sYTBWdfzx/picture.webp?v=...",
  "ownerName": "Jane Doe",
  "ownerImageUrl": "https://...",
  "stats": { "totalUsers30Days": 90 },
  "rating": { "average": 4.5, "count": 12 },
  "pricing": { "model": "PAY_PER_EVENT", "usdPer1000": 2.5, "unitName": "result" },
  "isUnderMaintenance": false,
  "createdAt": "2026-01-15T10:30:00.000Z",
  "modifiedAt": "2026-08-01T12:00:00.000Z",
  "consoleUrl": "https://scrape.ly/store/janedoe/example-scraper"
}
```

`industries` and `outputTypes` come from fixed lists, `targetSites` are the domains the Actor scrapes (empty for crawlers that take any site). Search responses also carry `facets` (`industries`, `outputTypes`, `sites`: `{ name, count }` lists over the other filters), and the overview lists the same counts. `description` is truncated to 300 characters. `pictureUrl` is `null` when the Actor has no icon. `pricing.model` is `FREE` or `PAY_PER_EVENT`; `usdPer1000` is the price of 1,000 units of the main event.

### Store overview

```
GET /v2/public/store/overview
```

Returns `total` (number of public Actors), `categories`, `industries`, `outputTypes` and `sites` (each `[{ "name", "count" }]`), `sections`, and `generatedAt`. `sections` has up to 9 cards each:

| Section | Contents |
| --- | --- |
| `top` | Most popular Actors |
| `rising` | Actors created in the last 90 days, by users in the last 30 days |
| `leadGen` | Lead generation and SEO Actors |
| `community` | Newest Actors |

```api-test
{ "method": "GET", "path": "/v2/public/store/overview" }
```

### Search the Store

```
GET /v2/public/store/actors
```

| Query param | Type | Default | Description |
| --- | --- | --- | --- |
| `q` | string | — | Search terms (first 100 characters). Every term must appear in the title, name, username, or description. |
| `category` | string | — | Category name, case-insensitive. |
| `pricing` | string | — | `free`, `paid` (anything not free), or an exact model such as `PAY_PER_EVENT`. |
| `industry` | string | — | Use-case industry: e-commerce, real-estate, jobs-hr, travel, finance, social-media, marketing-seo, news-media, lead-generation, ai-llm, automotive, local-business, food-hospitality, healthcare, education, entertainment, sports, government-legal, developer-tools, general. Repeat the parameter or separate values with commas to match any of them. |
| `outputType` | string | — | What the Actor returns: products, prices, reviews, listings, profiles, posts, comments, jobs, contacts, companies, places, articles, videos, images, search-results, documents, events, files, screenshots. Same repeat and comma rules. |
| `site` | string | — | A domain the Actor scrapes, for example `amazon.com`. Same repeat and comma rules. |
| `sort` | string | `relevance` with `q`, else `popular` | `relevance`, `popular` (monthly active users), `newest`, `rating`, `updated`, or `name`. Other values return `400 invalid-input`. |
| `limit` | number | 24 | 1–100. |
| `offset` | number | 0 | Pagination offset. |

```bash [Request]
curl "https://api.scrape.ly/v2/public/store/actors?q=google+maps&pricing=free&limit=10"
```

```json [Response]
{ "data": { "total": 3, "offset": 0, "limit": 10, "items": [ { "id": "eLN5rTB8sYTBWdfzx", "title": "Example Scraper" } ] } }
```

Each item is a full [card](#actor-cards).

```api-test
{ "method": "GET", "path": "/v2/public/store/actors?limit=5" }
```

### Actor page

```
GET /v2/public/store/actors/{username}/{name}
```

Everything a public Actor page needs, in one call. Unknown or private Actors return `404 actor-not-found`.

| Field | Contents |
| --- | --- |
| `actor` | The card, plus the full `description`, `seoTitle`, `seoDescription`, `pricingInfo` (the current pricing configuration), `successRate` and `successRateWindow`: the [public success rate](https://scrape.ly/docs/api-actors.md), the percentage of the Actor's finished runs (succeeded, failed or timed out; aborted runs are left out) that succeeded since its last successful build (`"currentBuild"`), or over the last 30 days (`"last30Days"`) when the current build has fewer than 10. Every run counts, whoever started it. Both are `null` when the last 30 days also have fewer than 10 finished runs, and no run counts are published with it. |
| `build` | What users need from the [User Build](https://scrape.ly/docs/publishing.md#user-build) (the build their runs use): `readme`, `inputSchema`, `outputSchema`, `datasetSchema`. No build number, date, or changelog: build info is owner only. Only a successful build is shown; `null` if there is none. |
| `exampleRunInput` | Example input object, or `null`. |
| `reviews` | `average`, `count`, `distribution` (`1`–`5`), and the 10 newest `items` (`id`, `username`, `userImageUrl`, `rating`, `text`, `createdAt`). |
| `issues` | `open` and `closed` counts and the 20 newest `items`. |
| `examples` | Published task examples: `slug`, `title`, `description`, `useCase`, `publishedAt`. |
| `related` | Up to 6 cards that share a category. |

```api-test
{ "method": "GET", "path": "/v2/public/store/actors/janedoe/example-scraper" }
```

### Sitemap

```
GET /v2/public/store/sitemap
```

Every public Actor as `{ "username", "name", "title", "modifiedAt" }`, plus `total` and `generatedAt`. Use it to build sitemaps or sync a catalog.

```api-test
{ "method": "GET", "path": "/v2/public/store/sitemap" }
```

### Example tasks

```
GET /v2/public/store/examples
```

Every published example task, newest first (at most 10,000), for sitemaps. Each item is `{ "actorId", "username", "name", "slug", "title", "publishedAt" }`, plus `total` and `generatedAt`.

```api-test
{ "method": "GET", "path": "/v2/public/store/examples" }
```

### Actor picture

```
GET /v2/public/actors/{actorId}/picture.webp
```

Returns a public Actor's current icon, or `404 picture-not-found`. The `pictureUrl` in cards points to [`/v2/actors/{actorId}/picture.webp?v={hash}`](https://scrape.ly/docs/api-actors.md#get-actor-icon-and-picture) instead, which is versioned by the icon's content and cached as immutable.

## Reviews

Users who have run a public Actor can rate it 1–5 stars and leave a comment. Reading reviews is public; submitting one requires authentication.

### Review stats

```
GET /v2/actors/{actorId}/reviews/stats
```

```bash [Request]
curl "https://api.scrape.ly/v2/actors/{actorId}/reviews/stats"
```

```json [Response]
{
  "data": {
    "avgRating": 4.5,
    "totalReviews": 12,
    "distribution": { "1": 1, "2": 0, "3": 1, "4": 3, "5": 7 }
  }
}
```

```api-test
{ "method": "GET", "path": "/v2/actors/{actorId}/reviews/stats" }
```

### List reviews

```
GET /v2/actors/{actorId}/reviews
```

Newest first. `limit` defaults to 20 (max 100); `offset` defaults to 0.

```bash [Request]
curl "https://api.scrape.ly/v2/actors/{actorId}/reviews?limit=20&offset=0"
```

```json [Response]
{
  "data": {
    "total": 12,
    "offset": 0,
    "limit": 20,
    "items": [
      {
        "id": "rev-uuid",
        "userId": "user-uuid",
        "username": "jane",
        "userImageUrl": "https://...",
        "rating": 5,
        "reviewText": "Fast and reliable!",
        "createdAt": "2026-08-01T12:00:00.000Z"
      }
    ]
  }
}
```

```api-test
{ "method": "GET", "path": "/v2/actors/{actorId}/reviews?limit=5" }
```

### Submit or update a review

```
POST /v2/actors/{actorId}/reviews
```

Creates your review, or updates your existing one. Returns `201` on create and `200` on update, with `{ "id", "userId", "username", "rating", "reviewText" }` in `data`.

```bash
curl -X POST "https://api.scrape.ly/v2/actors/{actorId}/reviews" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "rating": 5, "reviewText": "Works great" }'
```

| Body field | Type | Required | Description |
| --- | --- | --- | --- |
| `rating` | integer | Yes | 1–5. |
| `reviewText` | string | No | Max 5000 characters. Profanity-filtered. |

Rules:

- The Actor must be public (`403 forbidden`).
- You can't review your own Actor (`403 forbidden`).
- You must have run the Actor at least once (`403 run-required`).
- If your review was first created less than 24 hours ago, you can't submit or update it yet (`429 rate-limited`).

## Issues

Issues are bug reports, feature requests, and feedback threads on public Actors. Any signed-in user who has run the Actor can open one.

### Issues on Actors you own

```
GET /v2/user/issues
```

Owner inbox: issues filed **on Actors you own**, not issues you opened on other people's Actors.

```bash
curl "https://api.scrape.ly/v2/user/issues?status=open&type=bug" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN"
```

| Query param | Type | Default | Description |
| --- | --- | --- | --- |
| `status` | string | — | `open` or `closed`. |
| `type` | string | — | `bug`, `unexpected`, `feedback`, or `feature`. |
| `limit` | number | 20 | Page size, max 100. |
| `offset` | number | 0 | Pagination offset. |

```json
{
  "data": {
    "total": 4,
    "offset": 0,
    "limit": 20,
    "stats": {
      "totalIssues": 10,
      "openIssues": 4,
      "closedIssues": 6
    },
    "items": [
      {
        "id": "issue-uuid",
        "actorId": "eLN5rTB8sYTBWdfzx",
        "actorName": "example-scraper",
        "actorUsername": "janedoe",
        "userId": "user-uuid",
        "username": "reporter",
        "userImageUrl": null,
        "title": "Times out on large URLs",
        "description": "Steps to reproduce...",
        "actorRunUrl": null,
        "status": "open",
        "type": "bug",
        "createdAt": "2026-08-01T12:00:00.000Z",
        "updatedAt": "2026-08-01T12:00:00.000Z",
        "closedAt": null,
        "closedBy": null
      }
    ]
  }
}
```

`stats` counts all issues on your Actors, ignoring filters. `total` and `items` honor `status` and `type`.

```api-test
{ "method": "GET", "path": "/v2/user/issues?limit=5" }
```

### Hide the removed-from-store notice

```
POST /v2/user/issues/maintenance/{actorId}/hide
```

Hides the "Removed from store" notice of your own deprecated Actor from the Issues tab. Returns `200`:

```json
{ "data": { "actorId": "eLN5rTB8sYTBWdfzx", "hidden": true } }
```

Returns `404 actor-not-found` if the Actor isn't yours or isn't deprecated.

```bash
curl -X POST "https://api.scrape.ly/v2/user/issues/maintenance/eLN5rTB8sYTBWdfzx/hide" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN"
```

### Issue stats for an Actor

```
GET /v2/actors/{actorId}/issues/stats
```

Public. Returns open and closed counts for one Actor.

```json
{
  "data": {
    "totalIssues": 10,
    "openIssues": 4,
    "closedIssues": 6
  }
}
```

```api-test
{ "method": "GET", "path": "/v2/actors/{actorId}/issues/stats" }
```

### List issues for an Actor

```
GET /v2/actors/{actorId}/issues
```

Public. Supports `status`, `type`, `limit` (max 100), and `offset`, the same filters as the owner inbox.

```bash
curl "https://api.scrape.ly/v2/actors/{actorId}/issues?status=open&limit=20"
```

```api-test
{ "method": "GET", "path": "/v2/actors/{actorId}/issues?limit=5" }
```

### Create an issue

```
POST /v2/actors/{actorId}/issues
```

```bash
curl -X POST "https://api.scrape.ly/v2/actors/{actorId}/issues" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Actor times out on large URLs",
    "description": "Steps to reproduce...",
    "type": "bug",
    "actorRunUrl": "https://my.scrape.ly/actors/{actorId}/runs/{runId}"
  }'
```

| Body field | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | string | Yes | Max 255 characters. Profanity-filtered. |
| `description` | string | No | Max 10,000 characters. |
| `type` | string | No | `bug` (default), `unexpected`, `feedback`, or `feature`. |
| `actorRunUrl` | string | No | Link to a related run, max 500 characters. |

Returns `201` with the created issue in `data`. The Actor owner gets a dashboard notification.

Rules:

- The Actor must be public (`403 forbidden`).
- You can't open an issue on your own Actor (`403 forbidden`).
- You must have run the Actor at least once (`403 run-required`).
- One new issue per Actor per 24 hours (`429 rate-limited`).

The reporter and the Actor owner are subscribed to comment notifications automatically.

### Close or reopen

```
PATCH /v2/actors/{actorId}/issues/{issueId}/close
PATCH /v2/actors/{actorId}/issues/{issueId}/reopen
```

**Only the Actor owner** can close or reopen an issue. Closing an already-closed issue, or reopening an open one, returns `400 invalid-action`. Both return `{ "data": { "id", "status", "closedAt", "closedBy" } }`.

```bash
curl -X PATCH "https://api.scrape.ly/v2/actors/{actorId}/issues/{issueId}/close" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN"
```

### Comments

```
GET /v2/actors/{actorId}/issues/{issueId}/comments
POST /v2/actors/{actorId}/issues/{issueId}/comments
DELETE /v2/actors/{actorId}/issues/comments/{commentId}
```

`GET` is public (`limit` default 50, max 100). `POST` requires authentication. `DELETE` is allowed for the comment author or the Actor owner.

```bash
curl -X POST "https://api.scrape.ly/v2/actors/{actorId}/issues/{issueId}/comments" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "body": "I can reproduce this on v1.2.0" }'
```

- `body` is required, max 5000 characters, and profanity-filtered.
- Closed issues reject new comments (`403 issue-closed`).
- The Actor owner and the issue author can always comment. Other users must have run the Actor and can post at most 3 comments per issue per 24 hours (`429 rate-limited`).
- A new comment sends a dashboard notification to everyone subscribed to the issue with `notifyOnComment` on.

### Issue subscriptions

```
GET /v2/actors/{actorId}/issues/{issueId}/subscription
PUT /v2/actors/{actorId}/issues/{issueId}/subscription
DELETE /v2/actors/{actorId}/issues/{issueId}/subscription
```

Authentication required. Subscriptions control comment notifications for one issue.

```bash
curl -X PUT "https://api.scrape.ly/v2/actors/{actorId}/issues/{issueId}/subscription" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "notifyOnComment": true }'
```

`GET` and `PUT` return `{ "data": { "isSubscribed": true, "notifyOnComment": true } }`. `DELETE` unsubscribes and returns `{ "data": { "success": true } }`.

```api-test
{ "method": "GET", "path": "/v2/actors/{actorId}/issues/{issueId}/subscription" }
```

## Record a page view

```
POST /v2/actors/{actorId}/pageview
```

Records a view of an Actor's page for the owner's acquisition analytics. Authentication optional, so anonymous views count. Views are recorded only for public Actors, or by the owner on their own Actor.

| Body field | Type | Description |
| --- | --- | --- |
| `pageType` | string | Required. `detail`, `input`, or `run`. |
| `sessionId` | string | Required. Opaque client session ID, max 64 characters, used to count unique visitors. |
| `referrer` | string | Optional referring URL (first 500 characters stored). |

```bash
curl -X POST "https://api.scrape.ly/v2/actors/{actorId}/pageview" \
  -H "Content-Type: application/json" \
  -d '{"pageType": "detail", "sessionId": "s_8f2c91ab", "referrer": "https://google.com"}'
```

Returns `201` with `{ "data": { "recorded": true } }`.

| Status | Condition |
| --- | --- |
| `400` | `invalid-input`: `pageType` missing or invalid, or `sessionId` missing or longer than 64 characters |
| `403` | `forbidden`: the Actor is private and you are not its owner |
| `404` | `actor-not-found` |
| `429` | Too many page views from one IP address (60 per minute) or for one Actor (300 per minute) |

Generate `sessionId` on the client and reuse it for the visitor's session so repeat views aren't counted as new visitors. Owners read the results through the [acquisition insight endpoints](https://scrape.ly/docs/api-earnings.md#acquisition-insights).

## Errors

| Status | Type | Meaning |
| --- | --- | --- |
| 400 | `invalid-input` | Missing or invalid rating, title, body, or query parameter |
| 400 | `profanity-detected` | Text failed the profanity filter |
| 400 | `invalid-action` | Issue already closed or already open |
| 403 | `forbidden` | Reviewing or opening an issue on your own or a private Actor, or not the owner |
| 403 | `run-required` | Run the Actor first |
| 403 | `issue-closed` | Can't comment on a closed issue |
| 404 | `actor-not-found` / `issue-not-found` / `comment-not-found` | Unknown resource |
| 429 | `rate-limited` | Review, issue, or comment rate limit |

---

- Previous: [Users](https://scrape.ly/docs/api-users.md)
- Next: [Earnings & monetization](https://scrape.ly/docs/api-earnings.md)
- All docs: [Docs index](https://scrape.ly/docs.md), [llms.txt](https://scrape.ly/llms.txt)
