Hacker News Stories and Comments Scraper avatar

Hacker News Stories and Comments Scraper

Pricing

Pay per event

Go to Apify Store
Hacker News Stories and Comments Scraper

Hacker News Stories and Comments Scraper

Extract Hacker News stories and comments from the official API with bounded feeds, comment depth, keyword filters, source IDs and clear partial results.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Tim Zinin

Tim Zinin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Export Hacker News stories and comments with source IDs, thread relationships and clear limits for research datasets and discussion monitoring.

Evidence status: private build accepted on 2026-09-05. Every input/output example below is an exact dataset row exported from the accepted private acceptance runs on Apify's network, and the tariff in R4 was read back from the listing's pay-per-event configuration after those runs. This page still makes no claim that a scheduled task or a public review history already exists.

R1. What you get

This Actor reads the official Hacker News Firebase API and creates a dataset of complete story or comment items. You can start with specific numeric IDs or a small window from top, new, ask or show. Optional child traversal adds comments while preserving the immediate parent ID, a known root story ID and a depth relative to your starting item. The result is suitable for a research table where every observation can be traced back to its source.

One complete story or comment is an item. A story normally includes its title, author handle, creation timestamp, score, descendant count and submitted URL. A comment normally includes its author handle, creation timestamp, parent ID and original HTML text. Fields that the source does not provide remain null. The Actor does not visit a submitted article, load a linked PDF, follow a comment hyperlink or turn a URL into an article summary.

The dataset also carries free status rows. These distinguish a confirmed JSON null from an HTTP failure, a deleted item from an unsupported item type, and a keyword miss from an unfinished traversal. A free partial row explains when known children remain or the configured depth excluded descendants. A complete paid item remains complete even if the overall run is partial; the partial notice describes the set, not damage to that item.

The default requests one historical story, ID 8863, without comments. Its deliberately stable identity is useful for verifying the input and output contract. It is a 2007 discussion and must not be presented as today's news. For current discovery choose a feed. For a repeat comparison use your own saved IDs, preserve the observation timestamp and compare source fields without pretending that an old post became new merely because you fetched it again.

A bounded run can emit at most 500 complete items. Free status rows are additional rows, so the number of dataset rows can exceed maxItems. The HTTP work cap is 550 attempts, including retries and redirect hops. A run also has a 240-second working deadline inside a 300-second run timeout. These limits keep a large or changing comment tree from silently becoming an unlimited crawl.

R2. Who uses it

Product researchers can collect a small, explicitly selected discussion set around a launch, an interface decision or a recurring user complaint. Each comment retains an author handle and parent relationship so a researcher can separate a reply from the claim it answers. The output supplies evidence for reading and annotation. It does not supply respondent demographics, intent labels, a representative survey sample or a statistically valid estimate of a whole market.

Developer relations teams can maintain a watch list of threads about their tools. They can compare already known IDs across runs, identify new rows in their own database and ask a human to review relevant discussion. Keyword filtering helps reduce a fetched window to likely matches, but it is a substring test. A matching word is not an endorsement, a purchase signal, a support request or consent to be contacted.

Editors and analysts can export story metadata to a spreadsheet before selecting articles for a digest. Submitted URLs remain source values, which makes it possible to queue a separate editorial reading step. This Actor does not verify the linked article, determine whether it is still accessible or license its text for republication. Treat the story's URL as a lead to investigate and keep the Hacker News source ID alongside it.

Data engineers can use the explicit found, status, resultCount, partial and error fields as a stable routing contract. A pipeline should not infer success from a nonempty dataset alone. Error and notice rows make a dataset nonempty without delivering content. Filter found=true for the content table and retain the remaining rows in an operational table, joined by the external Apify run ID that your integration records.

This product is intentionally unsuitable for enumerating every historical Hacker News item, reconstructing complete user profiles or ranking authors for outreach. It has no user endpoint, no arbitrary URL field and no Algolia search mode. If your task needs full historical retrieval, the present bounded feed window is the wrong coverage contract. Increasing maxItems changes the window size, not the nature of the product.

R3. How to run

In the Actor input form, start with itemIds containing 8863. Keep feed at none and includeComments false for the smallest useful check. Start the run, wait for a terminal status and open the default dataset. Expect a story row with id 8863 and found=true if the source is available. Check the OUTPUT record for the run summary. A fresh checkedAt does not change the old source time stored in the row.

To retrieve a current feed, set itemIds to an empty array and choose top, new, ask or show. The two modes are mutually exclusive. If you keep the default ID while also choosing top, runtime validation rejects the input rather than quietly deciding which mode you intended. In an API request you may omit itemIds when a non-none feed is selected; the Actor then uses an empty array automatically. In the form, clear the prefilled ID explicitly.

To inspect replies to a known story, turn on includeComments and begin with maxDepth 1 and a small maxItems value. The story is depth 0, its direct children are depth 1, and the next layer would be depth 2. Children are processed breadth-first within the configured window. Source child order is preserved as encountered, but the resulting dataset is a traversal snapshot, not a guarantee that it matches every visual ordering choice of the Hacker News website.

Open the dataset in JSON when inspecting nested warnings or comment HTML. CSV is convenient for metadata but requires careful handling of commas, quotation marks and embedded line breaks. Import a downloaded CSV as data with the correct parser. Do not split rows manually on commas, and do not paste an untrusted source string into a spreadsheet formula context. The Actor preserves evidence; the consumer controls how that evidence is rendered.

Before using a scheduled integration, perform one small manual run with your actual selection and check found=true content, identity fields, status notices and the summary. Then configure a schedule in your own account if needed. Creating the Actor input does not create a schedule. A previous successful manual run also does not prove that a later run completed, used the same tariff or saw the same feed ordering.

R4. Pricing

Pricing: $0.005 per start plus $0.0003 per complete item. One item means one complete story or comment delivered through the result-found event. This is the listing's configured pay-per-event tariff, read back after the accepted private runs on 2026-09-05: the start event is apify-actor-start and the result event is result-found. No other event is priced, so null, filtered, error and partial-notice rows never carry a charge.

At this rate, a start with one item is $0.0053. A start with 20 items is $0.011. A start with 100 items is $0.035. A start with 500 items is $0.155. These examples assume the configured 256 MB run and the platform's minimum start-event count of one. The start event is charged once per gigabyte of run memory: the default 256 MB run counts as one start unit ($0.005), while a run launched with 4 GB counts as four ($0.020). This was observed in the acceptance runs, which were deliberately launched at 4 GB and recorded four start events each. Account-specific platform costs and taxes, if applicable, belong to the platform bill rather than these simple event examples.

No result-found event is attached to null, deleted, dead, unsupported-type, filtered, input-error, source-error or partial-notice rows. Duplicate IDs within the same run do not create another complete item. An empty result can still incur the automatic start event. A failed source request does not make a run free of that start charge, so a budget estimate must include starts as well as successful items.

A buyer charge cap limits paid work. Before a complete row is written, the Actor reads the remaining budget inside the same lock as the linked dataset-and-charge call. If the remaining amount cannot cover another item, that item is withheld and remaining work stops. A last row that fits exactly still counts as delivered and paid even if the SDK indicates that the event limit is now reached. The cap is not an instruction to delete results that already fitted.

If pricing cannot be read, result-found has no positive price on the platform, or automatic dataset writes themselves carry a price, the Actor stops and fails. It reports through the run log and status rather than risking a paid explanation row. If budget reading fails later, the run also fails instead of labelling the problem ordinary exhaustion. The automatic start event may already have been charged before code executes.

Repeating a run is a new request for work. There is no persistent replay ledger that makes the same source ID free across runs. Store your own id-to-observation history and decide whether a refresh is useful. The dataset key id deduplicates one run; it does not mean the source content is immutable, the score never changes or a second run will be refunded. A failed write/charge with uncertain delivery requires reconciliation before retrying the whole input.

R5. Input contract

The input is a JSON object with six supported fields. Unknown fields are rejected. Omitted fields receive the defaults described here, but explicit values must have the correct JSON type. Numeric strings such as "8863" are not numeric IDs. A string "false" is not a boolean. Rejecting ambiguous input makes API and UI runs comparable and prevents a misspelled setting from silently changing the scope.

FieldDefault or prefillRuntime boundaryEffect
itemIds[8863] in ID modeAt most 20 positive safe integersSelect explicit story/comment IDs; duplicates removed
feednonenone, top, new, ask, showSelect one current feed window instead of IDs
includeCommentsfalseJSON booleanTraverse child comments when true
maxDepth2Integer 0 through 3Relative child depth; root input is 0
maxItems20Integer 1 through 500Maximum complete items and feed seed window
keywords[]At most 5 strings, each 1–80 charactersCase-insensitive OR substring match in fetched title/text

With feed=none, at least one valid item ID is required. With a non-none feed, itemIds must be empty or omitted. The Actor does not guess whether a story URL contains an ID; supply the integer itself. An input such as https://news.ycombinator.com/item?id=8863 is a browser URL and is not accepted in itemIds. There is no direct route to arbitrary buyer-selected hosts.

maxDepth is relative to each seed. When you request a comment directly, that comment is depth 0 even if it is several levels deep on the website. The Actor does not climb its ancestors just to invent a root. Its rootId can therefore be null while parent is a real ID. When a comment is reached through a fetched story, rootId identifies that story. Join parent relationships by ID; do not interpret relative depth as global thread depth.

maxItems limits complete delivered content, not the number of source attempts or the total number of dataset rows. A removed item can require one request and produce one free row while consuming no complete-item allowance. A keyword miss behaves similarly. The independent 550-request cap and deadline still apply, so heavily filtered or error-heavy inputs can finish with fewer complete items than requested. This is visible through statuses and partial information.

In feed mode, maxItems also bounds the initial seed window. The Actor reads the selected feed's ID list and takes its first maxItems IDs. If only two of twenty fetched seeds match your keywords, it returns those matches and free filtered rows; it does not keep scanning hundreds of later stories to fill the result count. With comment traversal enabled, children of the chosen seeds may also be examined within the same overall work and item caps.

keywords uses OR: an item passes if any configured substring occurs in its title or text after case normalization. It is not a regular expression, a token search, a stemmer, an exact phrase index or a language detector. Text is the source HTML, so markup and entities remain part of the searched string. A word split by tags or represented by a different HTML entity may not match your literal spelling. Test representative source text before relying on narrow filters.

includeComments controls children even when the seed itself is a comment. Setting it false still allows a directly requested comment to be returned; it simply stops descendant traversal. Setting it true with maxDepth 0 returns eligible seeds while declaring known excluded child branches through a partial notice. A story without children does not need such a notice merely because includeComments is false or the depth cap is zero.

The runtime validates array lengths before allocating traversal work. You cannot bypass the schema by posting a million duplicate IDs or by putting 501 into maxItems. Request frequency is at most five per second and actual processing is sequential in this build. The upper concurrency boundary in the specification is three; the current implementation uses one, which preserves deterministic traversal and stays inside that limit.

R6. Happy, partial and failure output

The following examples are exact dataset rows exported from the accepted private acceptance runs on 2026-09-05, executed on Apify's network with the pay-per-event tariff active. Only checkedAt will differ in your own run; score and descendants are live snapshot values that can move over time. Example inputs are valid JSON and can be used directly.

Example A — one historical story (accepted run: one result-found event charged).

{"itemIds":[8863],"feed":"none","includeComments":false,"maxDepth":2,"maxItems":20,"keywords":[]}
{"input":8863,"sourceUrl":"https://hacker-news.firebaseio.com/v0/item/8863.json","found":true,"status":"ok","resultCount":1,"partial":false,"error":"","warnings":[],"checkedAt":"2026-09-05T11:15:20.819Z","schemaVersion":"1.0","id":8863,"rootId":8863,"depth":0,"type":"story","by":"dhouston","time":1175714200,"title":"My YC app: Dropbox - Throw away your USB drive","text":null,"url":"http://www.getdropbox.com/u/2/screencast.html","parent":null,"score":104,"descendants":71,"deleted":false,"dead":false}

The title and type are appropriate identity assertions for this historical test. score and descendants are snapshot values and should not be used as permanently fixed live assertions. checkedAt records retrieval, while time records creation. This example's submitted URL is old; the Actor has not visited it and makes no claim about its present availability.

Example B — confirmed null (accepted run: zero result-found events charged).

{"itemIds":[999999999],"includeComments":false,"maxItems":1}
{"input":999999999,"sourceUrl":"https://hacker-news.firebaseio.com/v0/item/999999999.json","found":false,"status":"not_found","resultCount":0,"partial":false,"error":"","warnings":[],"checkedAt":"2026-09-05T11:15:35.560Z","schemaVersion":"1.0","id":999999999,"rootId":null,"depth":0}

The source answered HTTP 200 with JSON null for this ID both in the local probe and in the accepted Apify run on 2026-09-05. That is narrower than proving the ID can never exist in the future. A live negative golden should be rechecked when the source changes. A network timeout is never converted into this row: it must have status source_error and a nonempty error.

Example C — complete item plus limited set (accepted run: one result-found event charged, two dataset rows).

{"itemIds":[8863],"includeComments":true,"maxDepth":2,"maxItems":1}
{"input":8863,"sourceUrl":"https://hacker-news.firebaseio.com/v0/item/8863.json","found":true,"status":"ok","resultCount":1,"partial":false,"error":"","warnings":[],"checkedAt":"2026-09-05T11:15:38.916Z","schemaVersion":"1.0","id":8863,"rootId":8863,"depth":0,"type":"story","by":"dhouston","time":1175714200,"title":"My YC app: Dropbox - Throw away your USB drive","text":null,"url":"http://www.getdropbox.com/u/2/screencast.html","parent":null,"score":104,"descendants":71,"deleted":false,"dead":false}
{"input":null,"sourceUrl":"https://hacker-news.firebaseio.com/v0/","found":false,"status":"partial","resultCount":0,"partial":true,"error":"","warnings":["Unprocessed known items: 33; omitted children at depth/work cap: 0; source errors: 0."],"checkedAt":"2026-09-05T11:15:39.008Z","schemaVersion":"1.0","unprocessed":33,"omittedChildren":0}

The accepted run's dataset contained exactly these two rows: the complete story and a separate free row with status partial, found=false, resultCount=0, partial=true and error="". The number of remaining children (33 at that snapshot) depends on the source state and the result cap. Do not hard-code it from an old run. The story itself retains partial=false because its own API object was complete; the set is incomplete because the requested traversal could not continue within the result cap.

Failure shape (contract; not triggered in the accepted runs because the source answered). A refused source connection or malformed JSON response produces a free source_error row with the requested ID, sourceUrl and checkedAt, found=false and resultCount=0. Its error contains the concrete failure category. If source errors leave no complete content, the run fails and OUTPUT.fatal explains why. When some complete content survives, it remains usable with the run's partial state and source-error rows preserved.

R7. Field dictionary

The common envelope appears on content and status rows. Fields specific to a complete item are not fabricated on every negative row. A consumer should first branch on found and status, then read the relevant payload. Checking only whether title exists would discard comments and would also confuse absent data with an empty story title. Use schemaVersion to detect an intentional future contract change.

FieldType and meaningConsumer guidance
inputInteger, feed name or nullRequested item identity; null for a run-level notice
sourceUrlStringAPI endpoint or API base for a set notice; provenance, not an arbitrary crawl target
foundBooleanTrue only for complete delivered story/comment content
statusStringok, not_found, deleted, dead, unsupported_type, filtered, source_error, input_error or partial
resultCountInteger1 for one complete item, 0 for a free status row
partialBooleanWhether this row explains incomplete coverage; complete content rows normally remain false
errorStringEmpty for clean absence/filtering/limits; nonempty for actual failure
warningsArray of stringsScope explanations; preserve when reviewing a partial run
checkedAtISO UTC stringTime the Actor prepared this source observation
schemaVersionStringCurrent row contract version, 1.0
idIntegerExact Hacker News item identity; primary key within a run
typeStringstory or comment for paid content; another source type may appear on a free unsupported row
byStringSource author handle; not verified personal identity
timeIntegerSource creation time in Unix seconds, not milliseconds
titleString or nullSource story title; null is normal for a comment
textString or nullSource HTML content; preserve or sanitize before display
urlString or nullSubmitted story URL, never fetched by this Actor
parentInteger or nullImmediate comment parent; not necessarily the root story
rootIdInteger or nullFetched root story identity when known; no ancestor inference for direct comment seeds
depthIntegerDepth relative to the selected input item, starting at zero
scoreInteger or nullSource score when supplied; null is not zero
descendantsInteger or nullSource reported comment count; not the number delivered by this run
deletedBooleanDeleted-source marker; deleted items are never paid content
deadBooleanDead-source marker; dead items are never paid content
unprocessedInteger on noticesKnown queued items not attempted or not delivered before stopping
omittedChildrenInteger on noticesKnown immediate child references excluded by depth/work bounds

Treat time as seconds when creating a JavaScript Date: multiply by 1000. A 2007 source time with a 2026 checkedAt is expected for historical retrieval. Neither value records when a comment was last edited. The API object does not supply a universal last-edited timestamp, so the Actor does not invent one. To detect changes, compare canonical field values between your own snapshots.

rootId and parent solve different problems. parent answers which item a comment replies to. rootId answers which fetched story led the traversal to that comment. If rootId is null, preserve the unknown value rather than copying parent into it. A parent's own type might be comment. Incorrectly promoting parent to root would merge or mislabel branches when your integration later fetches more context.

The OUTPUT key-value record reports requested, requestedInputs, processed, unprocessed and requestCount alongside delivered, paid, free, failed, partial and fatal. requested counts discovered unique work targets; requestedInputs counts the chosen seed window. processed counts source item attempts, while retries are counted by requestCount. With comments enabled, processed can exceed requestedInputs. Feed-list retrieval consumes HTTP work without being a story or comment item.

paid counts confirmed linked result events seen by this process. delivered counts confirmed complete rows after a successful linked call. free counts written status rows. failed counts free rows with an error. deliveryUncertain records write/charge outcomes that could not be reconciled, and the run fails in that condition. The cloud chargedEventCounts and actual dataset remain the final evidence when a connection breaks between delivery and charge acknowledgement.

R8. Evidence and boundaries

The source is the public v0 Hacker News API on hacker-news.firebaseio.com. The documentation describes item and feed endpoints, and its robots response permits the JSON paths used here. The Actor confines every request and redirect to that host and the documented item/feed route shapes. A source URL stored inside an item is treated as evidence only, so a malicious or internal link in a comment cannot turn into an outbound request.

Evidence comes from two layers. Local build checks exercised the actual processInput function against the live API and returned a historical story, a confirmed null and a comment. The accepted private runs of 2026-09-05 on Apify's network then confirmed the same rows from the platform, the pay-per-event charge counts (one result-found event per complete item, none for the null and partial rows) and the default 256 MB memory setting. The examples in R6 are those cloud rows, not local probe output.

Feed order and comment structure can change while the run is in progress. Each API object is a separate retrieval, so the dataset is not a transactionally frozen copy of the whole discussion. A story's descendants count may disagree with the number of fetched comments even without a code defect: depth limits, removed items, filtering and concurrent source changes all affect the comparison. Preserve the scope settings next to each exported batch.

The Actor supports stories and comments. Job, poll and poll-option items are reported as unsupported types, without result billing. That distinction matters when an ID is manually supplied or a general feed contains another item type. A returned unsupported row is not a parser failure and not evidence that the source ID is missing. If a later release adds a type, its contract should define its paid completeness requirements explicitly.

A complete row must have a valid matching ID, supported type, author, positive source time and the required title or comment text. Optional fields remain optional, but invalid typed fields or malformed child IDs prevent a corrupt object from being sold as complete. A response body that exceeds the byte ceiling is rejected as incomplete instead of parsing and emitting the available prefix. A page with HTTP 200 and invalid JSON remains an error.

HTTP errors, malformed source shapes and clean absence carry different retry behavior. Transient network failures, HTTP 429 and HTTP 5xx can receive at most two retries, with one- and three-second base pauses and Retry-After respected within the remaining deadline. A clean null, an ordinary 4xx or malformed 200 response is not retried. Repeatedly asking for the same invalid response would spend time without improving the evidence.

R9. Decision routing

ConditionRouteReason
found=true and status=okContent tableA complete item is available for reading or comparison
status=not_found and error emptyAbsence logSource explicitly returned null for this ID at this check
status=deleted or deadExclusion logSource marked the item unavailable for this content contract
status=filteredFilter diagnosticsFetched item did not pass the configured substring test
status=unsupported_typeScope reviewThe source object exists but is outside story/comment support
status=partialCoverage reviewKnown work was excluded or left unfinished
status=source_errorRetry or incident queueAccess/format failure prevents an absence conclusion
OUTPUT.fatal nonemptyFailed-run reviewMoney, delivery, input or all-content failure needs attention

For a monitoring dashboard, start with the run's terminal status and OUTPUT. If the run failed, retain its evidence and inspect the fatal reason before re-running. A dataset may already contain valid rows from the first part of a failed run. Blindly importing them and immediately retrying the same input can create duplicate downstream records and repeat result charges. Upsert by your chosen source identity and keep run provenance.

A partial set can still answer a narrow question. For example, the first direct comments on a launch might be enough for a human to triage whether deeper reading is useful. It cannot establish that no contrary comment exists farther down the tree. Document the depth, result cap and keyword window in the downstream report. Readers need those limits beside the conclusion, not hidden in an operations log they will never see.

When a keyword filter returns no paid items, distinguish filtered rows from nulls and errors. Filtered means the fetched text did not contain the configured substrings; it does not mean the topic was absent from Hacker News. If the intended phrase is represented by HTML entities or split by tags, broaden the query or inspect the source text. Do not silently replace a deterministic substring contract with an undocumented language model classifier.

When the remaining budget is exhausted, choose whether more content is worth another run. Increasing maxItems alone does not increase the buyer's charge cap. Increasing the charge cap does not widen maxDepth or remove the HTTP deadline. These are separate controls, and the summary tells you which restriction was encountered. Keep automatic retries bounded so a large discussion cannot trigger an endless sequence of paid refreshes.

R10. Commercial playbooks

Launch discussion review. A product team records the Hacker News story ID when a launch appears. It requests that ID with includeComments=true, maxDepth=1 and a modest complete-item cap. A human reads the resulting comments, groups concrete issues in the team's existing tracker and attaches source IDs to each note. The team revisits selected IDs only when a follow-up review is useful. The Actor supplies source observations; it does not create issue tickets or contact authors.

Define an evidence policy before the first run. Keep original text and author handles in a restricted research table if needed, but quote only what your editorial and reuse policy permits in a public report. Separate your own tags from the source fields so later readers can see which statement came from the source and which was an analyst interpretation. A category such as “pricing concern” should be a downstream annotation, not a changed status value.

Developer-tool digest. An editor takes a small show or top feed window and optionally supplies broad keywords for a technology area. The complete story rows become a shortlist. The editor then checks the linked material separately, selects relevant stories and writes an original summary with source attribution. Free filtered rows document what the Actor actually examined. The resulting digest must not claim comprehensive coverage of the day's discussions when it used only a limited feed window.

Set a frequency that fits the editorial cycle. A weekly selection may need only a few deliberate runs and saved IDs; a near-real-time desk may need more frequent windows and a persistent deduplication table. Neither arrangement guarantees every story is captured, because stories can enter and leave a feed between observations. If recall is essential, measure it against a separate reference instead of assuming frequent polling makes the dataset complete.

Research comparison. An analyst can keep the same explicit story IDs across repeated snapshots and compare score, descendants and comment content. Use a source-ID plus snapshot-time table for history, and a separate latest-item table for current values. Changes in score are observations, not direct proof of readership, revenue or product adoption. Deleted/dead status should remain visible in the history so a missing later content row does not look like an import accident.

For all three processes, start with a small accepted input and estimate the number of starts and complete items. Review actual platform charges periodically. A workflow that creates many empty starts can cost more than an occasional larger deliberate batch under this tariff. This is a planning consideration, not a claim that one frequency is universally optimal. Your selection, source volatility and review capacity determine the useful cadence.

R11. Integration recipes

Dataset to a SQL content table. Store id as a sufficiently wide integer or decimal string, type as a constrained text field, time as an integer and checkedAt as a timezone-aware timestamp. Use found=true to select content. Keep sourceUrl, parent, rootId and the run ID supplied by your orchestrator. A latest table may upsert by id; a history table should use id plus the run ID or your own immutable snapshot key. Do not overwrite the historical source time with the import time.

For status rows, create a separate run-events table with input, status, error, warnings and checkedAt. It is normal for input to be null on a set-level partial notice. A database schema that requires a non-null content ID on every row will reject those notices. Handle them explicitly instead of dropping them during a generic “clean rows” step. Dropping notices makes a limited dataset appear complete to downstream users.

n8n or another workflow engine. Use the platform's Actor run operation with the exact JSON input. Wait for completion, retrieve the dataset and OUTPUT, then branch on fatal/partial before processing content. Add an IF or filter step for found=true. Upsert content into your destination, store status rows in an operations record, and only then mark the workflow batch imported. Keep credentials in the workflow engine's secret store rather than in itemIds, source text or exported JSON.

A generic webhook notification indicates that something happened to a run; it is not necessarily the full dataset. Treat the notification's run ID as a lookup key and retrieve the actual result through the authorized platform API. Validate that the event belongs to the Actor and run your workflow started. A retried webhook should not create another Actor run merely because the same completion notification arrived twice.

Python JSON import. Download JSON through the platform API or export UI and parse it with the standard JSON library. Iterate over objects, place found=true records in the content path and retain other statuses in a log path. Convert Unix seconds using a UTC-aware constructor. Handle null before numeric operations: a comment's score is often absent and must not be converted into zero and then averaged with story scores as though both were measured values.

JavaScript HTML display. Treat text and title as untrusted source content. Assign textContent for a plain-text preview, or use a maintained HTML sanitizer if your interface intentionally renders formatting. Never use eval, insert the text into a script block or treat a source link as an instruction. The Actor's transport guard prevents fetching arbitrary links; it does not sanitize every possible frontend rendering context for your application.

CSV and spreadsheets. Prefer JSON for the authoritative archive because arrays and nulls survive without ambiguous cell conventions. If a spreadsheet is the review surface, import a properly generated CSV, keep IDs as text where integer precision could be lost, and retain checkedAt with its timezone. Comment text may contain line breaks, commas and quote characters. Those are data, not extra records. Apply your normal formula-injection handling before exposing source text in cells.

Downstream language-model analysis. Send only the content required for the analysis and describe it as quoted source evidence. Keep source text out of system instructions and preserve item IDs in the model's output format. If the model creates themes, sentiment labels or a summary, store those as derived fields with the model/version and processing time. They are separate from this Actor's deterministic source fields and must not be presented as API facts.

R12. Operating guide

Begin with a baseline run using one ID, then expand one control at a time. First enable comments at depth 1, then adjust maxItems, and only then decide whether deeper traversal is useful. This makes a partial notice easier to interpret. Changing feed, keyword set, depth and item cap simultaneously can make two datasets incomparable even if the code and source remain unchanged.

Keep a copy of the effective input with each run. The default can change in a future version, so an omitted setting is less reproducible than an explicitly saved one. Record the Actor version/build ID through the platform run metadata, the run ID, start time, terminal status and the OUTPUT record. These operational fields belong to your batch ledger and complement the row's source ID and checkedAt.

Use requestCount to understand HTTP work, not as the billable item count. One source object can involve retries or redirect hops, and feed discovery consumes a request without yielding an item. Use paid or the platform's result-found event count for result billing, while retaining the automatic start event separately. The number of rows downloaded from a dataset is not an invoice because free explanations also occupy rows.

When the source returns 429, allow the Actor's bounded retry behavior to finish. If repeated runs show throttling, reduce your schedule frequency and concurrent runs across the account. The per-run five-request-per-second cap does not coordinate multiple independent runs that you launch at the same time. A modest schedule is preferable to an external loop that immediately restarts after every throttled response.

When a source format changes, preserve the failing run and the precise input. A malformed object is reported as source_error with no result charge for that unit. Avoid an automated fallback that substitutes an HTML page or a search-engine result into the same schema: that changes provenance and completeness. A repaired release should be tested with the affected source shape and with the existing money and negative controls before you widen usage again.

When the run ends with delivery uncertainty, compare the dataset and platform event counts before deciding whether to retry. The SDK writes before charging, so a thrown linked call can mean the row already landed while charge acknowledgement failed. The Actor stops at that point and does not claim the row was definitely withheld. Your importer should tolerate a valid row in a failed run without interpreting the run as wholly successful.

A strict working deadline may leave queued items even when maxItems is high. Increasing the platform timeout alone does not remove the Actor's 240-second work deadline. A smaller, more deliberate set of explicit IDs is often easier to resume than a changing feed. Resuming with another run can bill repeated IDs again; use your saved history to choose a useful continuation rather than blindly repeating the entire input.

R13. FAQ

Does this search all of Hacker News? No. Explicit IDs retrieve those objects, and feed mode examines a bounded current window. keywords filters fetched title/text. There is no historical search index, pagination through all past submissions or promise that every matching discussion will be discovered. A zero-match window is a statement about that window and those substring rules only.

Can I pass a Hacker News browser URL? Supply its numeric item ID instead. The input deliberately accepts typed IDs and selected feed names. It does not accept arbitrary start URLs or let a source comment introduce another crawl target. If your upstream system stores browser URLs, extract and validate the integer in that system before constructing this input.

Why did I receive more dataset rows than maxItems? maxItems counts complete story/comment items. Free null, filtered, error and partial rows are extra. Count found=true for content and use resultCount as a row-level unit indicator. Keep notices when assessing completeness even if your main table displays only content.

Why did I receive fewer items than maxItems? The feed window may contain removed or unsupported objects; keyword filtering may exclude fetched text; the comment depth may stop traversal; or the request, deadline or buyer budget cap may stop work. Review status rows and OUTPUT together. maxItems is an upper bound, not a promise to find that many matching source objects.

Is a direct comment's rootId always present? No. The Actor does not fetch ancestors for a directly requested comment, so its root story can be unknown. parent remains the immediate parent from the API and depth starts at zero for that input. A comment reached from a known story traversal receives that story's rootId. Null accurately preserves the difference.

Can it retrieve deleted comments? It does not recover removed text or query an archive. A deleted or dead marker produces a free status row and no paid content. The Actor does not use cached third-party copies to fill the gap. Treat this as source state at the observation time, not proof of what the author originally wrote.

Are source links opened? No. The url field is exported as source data. Its target may be old, unavailable, redirected or outside the source's control. Your editorial workflow decides whether to inspect it separately and under what access rules. Do not describe this Actor's output as verified article text.

Are author handles verified people? No. by is the source's handle. The Actor does not resolve real-world identities, collect private contact details or confirm that a handle represents a company. Avoid turning a discussion export into identity or outreach claims that the evidence does not support.

Will rerunning the same ID be free? No. Deduplication is within one run only. A new run can fetch and bill a complete item with the same ID again, even if it appears unchanged. Persistent change detection belongs to your integration. OUTPUT explicitly marks that cross-run replay safety is not provided.

Can an empty run still cost money? Yes, the automatic start event can be charged even when no complete item is returned. The result event is limited to complete items; the accepted null-ID run charged start events and zero result events. Check your account bill for the exact start charge, which scales with the memory you select for the run.

Does a successful run mean a complete thread? No. A run can succeed with complete individual items and a partial set notice. Depth, result cap and source state constrain coverage. Inspect partial, unprocessed and omittedChildren before calling an export a full thread. Source descendants is not a receipt for the number of comments delivered.

Where should I report a problem? Use the Actor's platform issue channel when the listing is available, with the run ID, effective input, status and a redacted failing row. Do not post account tokens or unnecessary personal data. A reproducible source ID and the exact error category are more useful than a screenshot claiming only that the dataset looked small.

R14. Sources and rights

The machine source is the official Hacker News API under its documented Firebase v0 routes. The Actor retrieves public story/comment objects and selected public feed lists. Its fixed endpoint scope does not include linked article sites, user profiles or a third-party archive. The API documentation and the live endpoint should be checked again if a future release changes that scope.

Public access to an API does not give this Actor ownership of comments or submitted article content. The documentation repository's software license should not be represented as a blanket content license for every author's text. Preserve source IDs, source links and author handles where appropriate, apply your organization's reuse policy, and obtain any additional rights needed for publication or redistribution. This page makes no promise that every downstream use is permitted.

Hacker News and the associated service names identify the source. This Actor is an independent extraction tool and does not imply endorsement by the source operator. Its listing assets should use the factory's own geometric icon rather than copying the source's logo. The examples above are accepted-run evidence; this page embeds no placeholder screenshots presented as live proof.

Related tools for adjacent workflows in data pipelines for AI applications.

ActorWhat it does
Live Price OraclePair it in the data pipelines for AI applications workflow: Get timestamped DexScreener market observations for up to 100 token contracts or bounded ticker searches
RAG Dataset BuilderPair it in the data pipelines for AI applications workflow: Turn buyer-authorized public website sitemaps into bounded, source-linked text chunks for RAG, search,...
Structured Data ExtractorPair it in the data pipelines for AI applications workflow: Extract title, meta, JSON-LD, headings, links, emails, and price observations from up to 100 authorized...
URL to Markdown ConverterPair it in the data pipelines for AI applications workflow: Convert up to 100 authorized public HTML pages into clean Markdown for RAG, AI agents, SEO research, and...