{
  "openapi": "3.0.1",
  "info": {
    "title": "Hacker News Scraper — Who Is Hiring Jobs + HN Search",
    "description": "Turn the monthly Ask HN: Who is hiring? thread into structured job JSON, or full-text search ALL of Hacker News by keyword. Optional AI enrichment (BYO key) extracts company, role, salary, stack, remote & visa; AI trend digest summarizes the whole thread. Delta mode for alerts.",
    "version": "0.1",
    "x-build-id": "MTUWhiqcuM99x7Hrx"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/nomad-agent~hackernews-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-nomad-agent-hackernews-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/nomad-agent~hackernews-scraper/runs": {
      "post": {
        "operationId": "runs-sync-nomad-agent-hackernews-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/nomad-agent~hackernews-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-nomad-agent-hackernews-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "threadType": {
            "title": "Thread type",
            "enum": [
              "hiring",
              "seeking",
              "freelancer"
            ],
            "type": "string",
            "description": "Which of the three monthly HN \"whoishiring\" threads to scrape. <code>hiring</code> = \"Ask HN: Who is hiring?\" (companies posting jobs, the default). <code>seeking</code> = \"Ask HN: Who wants to be hired?\" (people looking for work). <code>freelancer</code> = \"Ask HN: Freelancer? Seeking freelancer?\".",
            "default": "hiring"
          },
          "searchQuery": {
            "title": "Search query (full-text HN search)",
            "type": "string",
            "description": "Optional. When set, the Actor IGNORES the monthly thread and instead full-text searches ALL of Hacker News (every story and comment) for this phrase via the official Algolia HN Search API, newest first — e.g. <code>rust remote</code>, your product name, a competitor. Combine with “Max age (hours)” to bound recency and “Only new since last run” for mention alerts. Up to 1000 newest matches per run (the API's ceiling). Leave empty for the classic Who-is-hiring mode."
          },
          "searchScope": {
            "title": "Search scope",
            "enum": [
              "all",
              "stories",
              "comments"
            ],
            "type": "string",
            "description": "What to search when “Search query” is set: <code>all</code> (stories + comments, default), <code>stories</code> only, or <code>comments</code> only.",
            "default": "all"
          },
          "keyword": {
            "title": "Keyword filter",
            "type": "string",
            "description": "Optional case-insensitive substring match on the full comment text (e.g. <code>remote</code>, <code>rust</code>, <code>senior</code>). Non-matching comments are dropped before billing, so you only pay for matches. Leave empty to return every comment in the thread."
          },
          "maxItems": {
            "title": "Max items",
            "minimum": 0,
            "type": "integer",
            "description": "Maximum number of job postings to return. Set 0 for no limit (all postings in the thread). Applied before AI enrichment, so you never pay to enrich rows you capped out.",
            "default": 100
          },
          "maxAgeHours": {
            "title": "Max age (hours)",
            "minimum": 0,
            "type": "integer",
            "description": "Only return postings newer than this many hours. Set 0 to disable age filtering and return all postings in the thread regardless of age.",
            "default": 0
          },
          "onlyNewSinceLastRun": {
            "title": "Only new since last run",
            "type": "boolean",
            "description": "Delta / alert mode: only output comments not seen on a previous run that also had this flag on. Already-seen comments are dropped before push (not billed), so this is the cheapest way to run this Actor on a cron schedule and only pay for genuinely new postings. State is tracked per Actor in a dedicated key-value store, keyed by HN comment id. See README “Delta mode / alerts”.",
            "default": false
          },
          "aiEnrichment": {
            "title": "AI enrichment",
            "type": "boolean",
            "description": "Adds aiCompany / aiRole / aiLocation / aiSalary / aiTechStack / aiRemote / aiVisa / aiEmploymentType to every posting via the Anthropic, Mistral or OpenAI API (pick which below). Unlike regex-based scrapers that guess from the pipe convention, the model returns null / “unknown” / empty whenever the prose doesn’t clearly state a value — it never fabricates. Requires your own API key for the provider you pick — billed separately by that provider, not by this Actor (see README “AI enrichment”). If turned on without a matching key, enrichment is skipped (with a warning) and postings are still returned normally, without the ai* fields.",
            "default": false
          },
          "aiTrendDigest": {
            "title": "AI trend digest",
            "type": "boolean",
            "description": "Adds ONE extra summary row (id <code>trend-digest</code>, never billed) produced by a single LLM call across every posting in the run: prose summary, top technologies with mention counts, salary observations, remote/hybrid/onsite split, top locations and notable trends. Great for a monthly hiring-market report from the Who-is-hiring thread. Needs the same BYO API key as AI enrichment (works with either add-on independently). The digest only reports what the postings literally say — counts and figures are never invented.",
            "default": false
          },
          "aiProvider": {
            "title": "AI provider",
            "enum": [
              "anthropic",
              "mistral",
              "openai"
            ],
            "type": "string",
            "description": "Which AI provider runs enrichment (only relevant when “AI enrichment” is on). <code>anthropic</code> (default) uses Claude via anthropicApiKey. <code>mistral</code> uses a Mistral model via mistralApiKey instead — pick this if you’d rather bring a Mistral key than an Anthropic one. <code>openai</code> uses a GPT model via openaiApiKey.",
            "default": "anthropic"
          },
          "anthropicApiKey": {
            "title": "Anthropic API key",
            "type": "string",
            "description": "Your Anthropic API key (sk-ant-…). Only used when “AI enrichment” is on and aiProvider is <code>anthropic</code>; billed separately by Anthropic. Not required unless aiEnrichment is on."
          },
          "aiModel": {
            "title": "AI enrichment model (Anthropic)",
            "enum": [
              "claude-haiku-4-5-20251001",
              "claude-sonnet-4-5"
            ],
            "type": "string",
            "description": "Claude model used for AI enrichment when aiProvider is <code>anthropic</code> (only relevant when “AI enrichment” is on). Haiku is fast and inexpensive; Sonnet gives higher-quality extraction on longer, more nuanced comments.",
            "default": "claude-haiku-4-5-20251001"
          },
          "mistralApiKey": {
            "title": "Mistral API key",
            "type": "string",
            "description": "Your Mistral API key. Only used when “AI enrichment” is on and aiProvider is <code>mistral</code>; billed separately by Mistral. Not required unless aiEnrichment is on with aiProvider=mistral."
          },
          "mistralModel": {
            "title": "AI enrichment model (Mistral)",
            "enum": [
              "mistral-small-latest",
              "mistral-medium-latest",
              "mistral-large-latest"
            ],
            "type": "string",
            "description": "Mistral model used for AI enrichment when aiProvider is <code>mistral</code> (only relevant when “AI enrichment” is on). Small is the default — it matches larger Mistral models on this well-scoped extraction task at a fraction of the cost.",
            "default": "mistral-small-latest"
          },
          "openaiApiKey": {
            "title": "OpenAI API key",
            "type": "string",
            "description": "Your OpenAI API key (sk-…). Only used when “AI enrichment” is on and aiProvider is <code>openai</code>; billed separately by OpenAI. Not required unless aiEnrichment is on with aiProvider=openai."
          },
          "openaiModel": {
            "title": "AI enrichment model (OpenAI)",
            "type": "string",
            "description": "OpenAI model used for AI enrichment when aiProvider is <code>openai</code> (only relevant when “AI enrichment” is on). Defaults to <code>gpt-4.1-mini</code> — cheap, fast and ample for this extraction task. Any chat-completions model works, including the gpt-5 family.",
            "default": "gpt-4.1-mini"
          },
          "cacheTtlSeconds": {
            "title": "Cache TTL (seconds)",
            "minimum": 0,
            "type": "integer",
            "description": "Cache the upstream thread lookup in the key-value store for this many seconds; re-runs within the window skip the network call. Set 0 to disable.",
            "default": 1800
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}