# Hacker News Stories & Comments Scraper (`cliqtomedia/hacker-news-stories-comments-scraper`) Actor

Export Hacker News lists or exact item IDs with bounded replies, parent IDs, original HTML and plain text. Check which comment trees are complete. No source API key.

- **URL**: https://apify.com/cliqtomedia/hacker-news-stories-comments-scraper.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (community)
- **Categories:** News, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.27 / 1,000 useful hn entity saveds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

Export Hacker News stories and comments as rows with stable IDs and parent links. Choose Top, New, Best, Ask HN, Show HN or Jobs, or add exact item IDs. No Hacker News API key is needed.

Start with the small example already in the Input form. Open the Dataset to read the results and the run summary to check missing or partial trees. Pricing is $0.05 per start plus $0.00027 per saved useful entity. A comment is a separate entity. Comment limits can make a tree partial.

![Hacker News Stories & Comments Scraper: stories, comment trees, stable IDs and export](https://api.apify.com/v2/key-value-stores/WNeotSr8CLjK5hL2m/records/readme-hero-20261010.png)

### Table of contents

- [What it does](#what-it-does)
- [Data you get](#data-you-get)
- [Quick start](#quick-start)
- [Input](#input)
- [Input examples](#input-examples)
- [Output example](#output-example)
- [Output fields](#output-fields)
- [Pricing](#pricing)
- [Use cases](#use-cases)
- [Repeated exports](#repeated-exports)
- [API and export](#api-and-export)
- [Limits and partial results](#limits-and-partial-results)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Related Actors](#related-actors)
- [Support](#support)

### What it does

The Actor reads the [public Hacker News API](https://github.com/HackerNews/API). It saves each root item, then its bounded replies. Stories, jobs, self posts and comments keep their source values. Replies keep their IDs, so you can rebuild a discussion tree.

It fetches only the fixed HN API origin. Article links are returned as data. It does not open those articles. Full-text search, user profiles, votes, posts and a full historical archive are outside this product's scope.

### Data you get

- Story title, external link, HN discussion link, author, time, score and source comment count.
- Comment ID, parent ID, root ID and depth.
- Original HTML in `textHtml` and readable plain text in `text`.
- Deleted and dead flags, plus clear rows for items that are unavailable.
- A summary of each root tree, skipped edges, requests, retries and run limits.

### Quick start

1. Open **Input** in Apify Console.
2. Keep the new form as it is: Top stories, 3 roots, up to 5 comments per root and depth 2.
3. Keep memory at 256 MB and timeout at 300 seconds. Set a maximum cost of $0.15 for this small example.
4. Click **Start**.
5. Open **Dataset**. Look for a `story` row and `comment` rows linked by `rootId` and `parentId`.
6. Open **Key-value store → RUN_SUMMARY**. A current busy story can be `partial` because the example uses small caps.

Repeat the same input to get a fresh export. The Actor has no saved global baseline, so it does not hide unchanged items.

### Input

All fields are optional. API input `{}` uses the runtime defaults below. The new form uses schema prefills with the same values. **Restore example input** is a separate Console action.

| Key | Type | Runtime default / UI prefill | Allowed values | Effect and recommendation |
| --- | --- | --- | --- | --- |
| `listMode` | string | `top` | `top`, `new`, `best`, `ask`, `show`, `job` | Choose the official list. Use `ask` for questions or `show` for projects. |
| `itemIds` | integer array | `[]` | Up to 100 positive safe integers | Exact IDs replace the list when this array is nonempty. Use `[8863]`, not URLs or string IDs. Duplicates are fetched once. |
| `maxStories` | integer | `3` | 1–100 | Maximum distinct root IDs, in list or input order. This also applies to exact comments and jobs. Start small. |
| `includeComments` | boolean | `true` | `true` / `false` | Fetch replies as separate rows. Use `false` for a story-only export. |
| `maxCommentsPerStory` | integer | `5` | 0–200 | Cap all descendant observations per root, including deleted, unavailable and error nodes. Zero means zero comments. |
| `maxDepth` | integer | `2` | 0–10 | Root depth is 0. Direct reply depth is 1. Zero reads roots only. Raise it when nested replies matter. |
| `maxNodes` | integer | `30` | 1–1000 | Total distinct roots and descendants reserved for fetch. Other time, request and byte caps can stop sooner. |

Empty `itemIds` selects `listMode`. Unknown fields, null input fields, invalid types and IDs are rejected. IDs must be integers from 1 to 9007199254740991. The value must also exist on HN to return a live entity.

When caps interact, the first reached cap wins. `maxStories` selects roots before fetching. `maxNodes` includes roots. A small node cap may leave later roots unread. The summary shows `selected`, fetched counts and the run reason. It never adds a fake row for an unread item.

### Input examples

#### Current discussions

```json
{
  "listMode": "top",
  "itemIds": [],
  "maxStories": 3,
  "includeComments": true,
  "maxCommentsPerStory": 5,
  "maxDepth": 2,
  "maxNodes": 30
}
```

Returns up to 18 observed nodes. Busy trees can be partial. List membership and live scores change between runs.

#### One known story and its replies

```json
{
  "itemIds": [8863],
  "maxStories": 1,
  "includeComments": true,
  "maxCommentsPerStory": 100,
  "maxDepth": 10,
  "maxNodes": 110
}
```

Returns the historical Dropbox story and its bounded comments. Read `treeStatus` to see whether all observed children were fetched. The source's `descendants` can differ from `collectedComments` because deleted or dead nodes are also observed.

#### Jobs without replies

```json
{
  "listMode": "job",
  "maxStories": 10,
  "includeComments": false,
  "maxNodes": 10
}
```

Returns up to 10 job items. Job `descendants` and `text` can be null. A direct comment ID is also supported; `storyId` is null because the Actor does not fetch its ancestors.

### Output example

These are selected fields from one real run with the known-story example. The live reply below has ID 8917 and links to the story with `parentId`. Export the full rows to keep every field.

```json
[
  {
    "entityType": "story",
    "id": 8863,
    "type": "story",
    "rootId": 8863,
    "storyId": 8863,
    "parentId": null,
    "depth": 0,
    "title": "My YC app: Dropbox - Throw away your USB drive",
    "hnUrl": "https://news.ycombinator.com/item?id=8863",
    "url": "http://www.getdropbox.com/u/2/screencast.html",
    "author": "dhouston",
    "time": 1175714200,
    "score": 104,
    "descendants": 71,
    "status": "complete",
    "treeStatus": "complete",
    "collectedComments": 72
  },
  {
    "entityType": "comment",
    "id": 8917,
    "rootId": 8863,
    "storyId": 8863,
    "parentId": 8863,
    "depth": 1,
    "status": "complete"
  }
]
```

The output keeps Unix seconds in `time` and adds UTC ISO time in `createdAt`. Null means the source did not provide that value. Each row has every documented field.

### Output fields

| Field | Type | Null? | Example | Meaning |
| --- | --- | --- | --- | --- |
| `entityType` | string | No | `comment` | `story` includes story/job/poll roots; other values: `comment`, `item`, `unavailable`, `error`. |
| `id` | integer | No | `8863` | Stable HN item ID. |
| `type` | string | Yes | `story` | Source type: story, job, comment, poll or pollopt. A deleted or unavailable item can have no type. |
| `status` | string | No | `partial` | `complete`, `partial`, `not_found` or `error`. |
| `reason` | string | Yes | `comment_cap` | Reason for an incomplete or unavailable result. Null for no row-level issue. |
| `rootId` | integer | No | `8863` | Input/list item whose subtree was visited. |
| `storyId` | integer | Yes | `8863` | Story/job/poll root ID. Null for a direct comment or unknown root. |
| `parentId` | integer | Yes | `8863` | Source parent ID. An unavailable child uses its expected edge parent. A missing source parent is flagged. |
| `depth` | integer | No | `1` | Distance from the selected root, which has depth 0. |
| `title` | string | Yes | `My YC app: Dropbox...` | Source title. Comments usually have no title. |
| `hnUrl` | string | No | `https://news.ycombinator.com/item?id=8863` | HN discussion or item link. |
| `url` | string | Yes | `http://www.getdropbox.com/u/2/screencast.html` | Source external link. Self posts and comments may have none. The Actor does not fetch it. |
| `author` | string | Yes | `dhouston` | Public HN username from `by`. |
| `time` | integer | Yes | `1175714200` | Source Unix seconds, not milliseconds. |
| `createdAt` | string | Yes | `2007-04-04T19:16:40.000Z` | Source time converted to UTC ISO. |
| `score` | integer | Yes | `104` | Source score. It can change during and after a run. |
| `descendants` | integer | Yes | `71` | Source comment count. This is not the number of rows collected. |
| `kids` | integer array | No | `[9224,8917]` | Source child IDs in ranked order. This example is shortened. Empty if not provided. |
| `textHtml` | string | Yes | `Hello<p>HN` | Original source HTML, kept as data. |
| `text` | string | Yes | `Hello\nHN` | Plain text with block boundaries and one entity decode. Script/style content is omitted. |
| `deleted` | boolean | No | `true` | Source deleted flag; false when absent. |
| `dead` | boolean | No | `false` | Source dead flag; false when absent. |
| `retrievedAt` | string | No | `2026-10-07T03:53:28.715Z` | Time this node was observed, in UTC ISO. |
| `treeStatus` | string | No | `partial` | Root fetch completeness: complete, partial, disabled or not_applicable. Non-root rows use not_applicable. |
| `treeReasons` | string array | No | `["depth_cap"]` | Root tree reasons. Empty for no tree issue. |
| `collectedComments` | integer | Yes | `72` | Observed descendants, including status/deleted/dead rows, for a traversed root. Null for non-root/unsupported items. |

Replies use breadth-first order within each root: source children first, then their children. Parent rows appear before children in a normal export. IDs are deduplicated across the whole run. A malformed edge or cycle is marked partial rather than fetched in a loop.

#### Where to find results

| Surface | Contents |
| --- | --- |
| Default Dataset | Saved entity and status rows. Export JSON or CSV. |
| KVS `OUTPUT` | `{ "rows": [...], "summary": {...} }`; rows match the Dataset. |
| KVS `RUN_SUMMARY` | The same summary, without the rows. |

The summary has `status`, `reason`, `sourceCommit`, `input`, `requested`, `selected`, `duplicatesRemoved`, `fetched`, `failed`, `notFound`, `savedRows`, `usefulRows`, `requests`, `attempts`, `retries`, `responseBytes`, `internalDeadlineSecs`, `startedAt`, `finishedAt`, `durationSecs`, `limits`, `resourceError` and `billing`.

`requested` is the number of IDs in the selected source list or exact input. `selected` is the number after deduplication and `maxStories`. `requests` counts logical fetch calls; `attempts` counts actual HTTP attempts, including retries. `fetched` counts successful item responses, including null. `failed` counts technical failures. `notFound` counts null responses. `usefulRows` excludes dead, deleted, unavailable and error rows.

`roots` has one entry for each observed root: `id`, `status`, `treeStatus`, `reasons`, `collectedComments`, `observedNodes`, `unavailableNodes`, `failedNodes` and `skippedEdges`. Caps can skip edges without fetching them. `billing` has `entityEvents` and `chargeLimitReached`. `resourceError` is separate from an input error. Early validation failures have a shorter error summary and keep the original input.

### Pricing

The price is **$0.05 per Actor start + $0.00027 per useful entity saved** ($0.27 per 1,000 useful entities). Platform usage is included in these event prices for standard runs. Memory is fixed at 256 MB.

A story, job or comment is one useful entity if it is live and saved. Deleted, dead, unavailable and error rows do not get an entity fee. The start fee still applies when no useful rows are returned. A result cap does not make source work free. A partial run is charged for its start and useful rows already saved. A failed or cancelled run may also have the start fee and saved entity fees.

| Useful entities saved in one run | Start | Entity fee | Total |
| ---: | ---: | ---: | ---: |
| 10 | $0.05 | $0.00270 | $0.05270 |
| 100 | $0.05 | $0.02700 | $0.07700 |
| 1,000 | $0.05 | $0.27000 | $0.32000 |
| 0 | $0.05 | $0 | $0.05 |

The 1,000-entity line is a price calculation at the node cap. A real 1,000-node export may include uncharged status/deleted/dead rows. Use the actual useful count for its price. There are no content filters or an unchanged-only monitoring mode.

Set **Maximum cost per run** before starting. The Actor stops saving paid entities when the remaining budget cannot cover one more event. It does not charge for unsaved entities. Fetch work may already have happened before this stop. Dataset writing and event charging are separate platform operations: an interrupted acknowledgement can leave a saved row without a charge; the Actor does not replay the write itself.

### Use cases

| Goal | Input choice | Fields to use |
| --- | --- | --- |
| Track current tech discussions | `top`, `new` or `best` | `id`, `title`, `score`, `descendants`, `retrievedAt` |
| Study project feedback | `show` or exact `itemIds` | `rootId`, `parentId`, `depth`, `text`, `treeStatus` |
| Build a public discussion corpus | Exact IDs, bounded comments | `id`, `textHtml`, `text`, `deleted`, `dead`, summary |
| Export HN jobs | `job`, comments off | `title`, `url`, `author`, `createdAt` |

### Repeated exports

Run the same input again and compare exports by `id`. Every run reads current API values. Use `retrievedAt` to separate observation time from post time. A partial export cannot prove that a missing row was removed from HN.

If you choose to set an Apify schedule, use the same small input and a cost cap. The Actor creates no schedules and keeps no global baseline. For a complete setup guide, see [Apify schedules](https://docs.apify.com/platform/schedules).

### API and export

Use your Apify account token to start the Actor through the Apify API. You do not need a Hacker News token. Do not put your Apify token in the input.

```bash
curl -X POST \
  'https://api.apify.com/v2/acts/cliqtomedia~hacker-news-stories-comments-scraper/runs?timeout=300&memory=256&maxTotalChargeUsd=0.15' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"listMode":"top","maxStories":3,"maxCommentsPerStory":5,"maxDepth":2,"maxNodes":30}'
```

Read the run's `defaultDatasetId` to download saved rows. Read `defaultKeyValueStoreId` for `OUTPUT` and `RUN_SUMMARY`. Your HTTP client needs an outbound connection to Apify. Use the [Apify API reference](https://docs.apify.com/api/v2) for run polling and Dataset export.

JSON preserves arrays and nulls. CSV is useful for flat entity fields. Use the same input object in an SDK, MCP Actor tool or your own automation, then join rows by `id`, `rootId` and `parentId`. Setup and permissions depend on that integration; no integration or webhook is created by this Actor.

### Limits and partial results

- At most 100 roots, 200 descendant observations per root, depth 10 and 1,000 total reserved nodes. A live Cloud export reached 1,000 observed nodes within the configured limits; this is a cap, not a guaranteed amount.
- Only the six official lists are supported. Lists can be smaller than `maxStories` and can include jobs. Their order can change while the run is active.
- Source work has a 90-second deadline, at most 1,300 HTTP attempts, concurrency 4, an 8-second request timeout, a 2 MiB response cap and an 8 MiB total response cap. Temporary transport, 429 and 5xx errors are retried up to three attempts within these caps.
- Set platform timeout to 300 seconds. A longer finite timeout also works but does not increase the 90-second source deadline. A smaller timeout reduces the source window to leave time for saved output.
- `comment_cap`, `depth_cap`, `node_cap`, `request_cap`, `byte_cap`, `response_byte_cap`, `deadline` and `charge_cap` show a limit. `unavailable_child`, `missing_parent`, `parent_mismatch`, `unexpected_child_type` and `duplicate_or_cycle` show an observed source/graph issue.
- `treeStatus` describes what was fetched. If the charge cap stops saving rows, use run-level `status=partial` to see that the export is incomplete.
- Original HTML is data. Do not render `textHtml`, titles or external URLs as trusted HTML in your own app. Use escaped text for display. The Dataset table displays plain text.
- Scores and comments are live. The API is not an atomic snapshot. Dead/deleted/null states may change after a run. A hard platform kill can prevent final KVS writes; rows already saved remain in the Dataset.

### Troubleshooting

| Symptom | Likely reason | Check | Action |
| --- | --- | --- | --- |
| `partial` tree | A cap stopped children | Root `treeReasons`, source `kids` and run summary | Raise the relevant input cap within allowed values, then run again. |
| Only `unavailable` rows | HN returned null for the IDs | `id`, `status`, `notFound` | Check the HN item link or use a current list. |
| Fewer comments than `descendants` | Caps, dead/deleted items or a changing source | `collectedComments`, flags and tree reasons | Keep source count and collected count separate. |
| Run failed before source work | Invalid input or unsupported resources | `reason`, `resourceError` | Use numeric IDs, known fields and 256 MB memory. |
| Source error rows | Request failures or malformed source data | Row reason, `failed`, `retries` | Retry a small input. Do not treat this as no matching stories. |
| Export stopped at budget | Not enough budget for the next entity | `billing.chargeLimitReached` | Check saved rows and raise the next run's cap if needed. |

### FAQ

#### Do I need an API key?

No HN API key, cookie or login is needed. You need your Apify account to run the Actor. API clients use an Apify token in an authorization header, never in this Actor's input.

#### Does it return every comment?

Only when every reachable child is observed within the caps and `treeStatus` is complete. The new form intentionally uses small limits. Check the summary for each root. `descendants` is the source count, not a completeness check.

#### Why are HTML and plain text different?

`textHtml` keeps the source. `text` removes tags, adds block boundaries and decodes entities once. `&amp;lt;` becomes `&lt;`, not an HTML tag. Deleted items can have neither value.

#### Does it fetch the linked article?

No. `url` is the external link from HN. This product exports HN items and discussions.

#### What do deleted, dead and null mean?

Deleted and dead are source flags. A null response means the item was unavailable at fetch time. It does not prove why. These observations are kept; they are not silently dropped and do not have an entity fee.

#### Why is there a start fee?

The scan still makes requests and saves a summary when there are no useful entities. The start fee covers this bounded work. Each fresh run has its own start fee.

#### Is it a replacement for another HN scraper?

Use a field mapping before switching. This Actor uses numeric IDs, `url` for the external link and `hnUrl` for HN. Comments are flat rows with stable IDs, not nested message arrays.

### Related Actors

- [RSS & Atom Feed Reader / Monitor](https://apify.com/cliqtomedia/rss-atom-feed-reader-monitor) — collect feed entries alongside HN stories.
- [Link Route Audit — Bulk HTTP Status & Redirects](https://apify.com/cliqtomedia/bulk-url-status-redirect-checker) — check selected article URLs in a separate, bounded run.

### Support

Use this Actor's **Issues** tab in Apify Store for questions or defects. Include the run ID, input mode and a short error reason. Keep tokens and private account information out of the report.

# Actor input Schema

## `listMode` (type: `string`):

Choose an official HN list. Non-empty itemIds take precedence. No source API key is needed.

## `itemIds` (type: `array`):

Optional positive integer HN IDs, at most 100. Non-empty array replaces the list; \[] uses listMode. Duplicates are fetched once.

## `maxStories` (type: `integer`):

Read the first N distinct IDs in list or input order. Maximum 100. Start with 3.

## `includeComments` (type: `boolean`):

Return bounded replies as separate rows. Source descendants is not the number collected. Turn off for stories only.

## `maxCommentsPerStory` (type: `integer`):

Limit all descendant observations per root, including deleted, unavailable and error nodes. Zero means zero comments, never unlimited.

## `maxDepth` (type: `integer`):

Root is depth 0, direct reply is 1. Zero fetches roots only. A limit marks the tree partial when children remain.

## `maxNodes` (type: `integer`):

Total distinct roots and descendants reserved for fetch. At most 1000. Other request, time and byte caps may stop sooner.

## Actor input object example

```json
{
  "listMode": "top",
  "itemIds": [],
  "maxStories": 3,
  "includeComments": true,
  "maxCommentsPerStory": 5,
  "maxDepth": 2,
  "maxNodes": 30
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `output` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "listMode": "top",
    "itemIds": [],
    "maxStories": 3,
    "includeComments": true,
    "maxCommentsPerStory": 5,
    "maxDepth": 2,
    "maxNodes": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/hacker-news-stories-comments-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "listMode": "top",
    "itemIds": [],
    "maxStories": 3,
    "includeComments": True,
    "maxCommentsPerStory": 5,
    "maxDepth": 2,
    "maxNodes": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/hacker-news-stories-comments-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "listMode": "top",
  "itemIds": [],
  "maxStories": 3,
  "includeComments": true,
  "maxCommentsPerStory": 5,
  "maxDepth": 2,
  "maxNodes": 30
}' |
apify call cliqtomedia/hacker-news-stories-comments-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cliqtomedia/hacker-news-stories-comments-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Q3xOnbYVzybMoZca8/builds/xiUGKunMTUQ494Lv5/openapi.json
