{
  "openapi": "3.0.1",
  "info": {
    "title": "Website Job Extractor (HTTP)",
    "description": "Scrape job listings directly from company websites / career pages / ATS systems. Unlike job portals, letting you identify hiring intent the moment it happens. The freshest signal for B2B targeting. AI-extracted with automatic ATS detection and anti-hallucination validation.",
    "version": "2.6",
    "x-build-id": "HBlcbbs7Y2P3bwQx1"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/santamaria-automations~website-job-extractor/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-santamaria-automations-website-job-extractor",
        "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/santamaria-automations~website-job-extractor/runs": {
      "post": {
        "operationId": "runs-sync-santamaria-automations-website-job-extractor",
        "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/santamaria-automations~website-job-extractor/run-sync": {
      "post": {
        "operationId": "run-sync-santamaria-automations-website-job-extractor",
        "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",
        "required": [
          "companies"
        ],
        "properties": {
          "companies": {
            "title": "Companies",
            "type": "array",
            "description": "List of companies to extract jobs from. Provide either a website_url (auto-discovers career pages) or specific career_urls.",
            "items": {
              "type": "object",
              "properties": {
                "company_id": {
                  "title": "Company ID",
                  "type": "string",
                  "description": "Your internal company identifier (UUID or any string)"
                },
                "company_name": {
                  "title": "Company Name",
                  "type": "string",
                  "description": "Name of the company (used for context in AI extraction)"
                },
                "website_url": {
                  "title": "Website URL",
                  "type": "string",
                  "description": "Company website URL. The actor will auto-discover career pages from the homepage navigation. Use this when you don't have specific career page URLs."
                },
                "career_urls": {
                  "title": "Career URLs",
                  "type": "array",
                  "description": "List of specific career page URLs to extract jobs from. Optional if website_url is provided.",
                  "items": {
                    "type": "string"
                  },
                  "default": []
                },
                "career_page_url": {
                  "title": "Career Page URL",
                  "type": "string",
                  "description": "Single pre-discovered career page URL (added as priority URL before career_urls)"
                },
                "website_domain": {
                  "title": "Website Domain",
                  "type": "string",
                  "description": "Website domain for cross-subdomain career discovery (e.g., tertianum.ch). Tries common career subdomains like jobs.domain.ch, karriere.domain.ch etc."
                }
              },
              "required": [
                "company_id",
                "company_name"
              ]
            }
          },
          "outputLanguage": {
            "title": "Output Language",
            "enum": [
              "en",
              "de",
              "fr",
              "it",
              "es",
              "pt",
              "nl",
              "auto"
            ],
            "type": "string",
            "description": "Language for text output fields (description, department, employment_type). 'auto' = match website language. Default: keep original language.",
            "default": "auto"
          },
          "managedLlmBudgetUsd": {
            "title": "Managed LLM Budget (USD)",
            "minimum": 0.01,
            "maximum": 50,
            "type": "number",
            "description": "Maximum USD to spend on the managed OpenRouter LLM (deepseek-chat-v3-0324) within this run. Free-tier users are hard-capped at $0.30 regardless of this value. Paying users default to $10.00 if left blank; maximum $50.00. Has no effect when you supply your own API key (BYOK mode).",
            "default": 10
          },
          "llmProvider": {
            "title": "LLM Provider",
            "enum": [
              "gemini",
              "groq",
              "openrouter"
            ],
            "type": "string",
            "description": "Primary AI provider for job extraction. Leave all API key fields blank to use managed mode (recommended for first-time users). Supply your own key here to use BYOK mode.",
            "default": "gemini"
          },
          "fallbackProvider": {
            "title": "Fallback LLM Provider",
            "enum": [
              "gemini",
              "groq",
              "openrouter"
            ],
            "type": "string",
            "description": "Second-level fallback if primary fails (e.g., rate limit exceeded)."
          },
          "fallback2Provider": {
            "title": "Third-Level Fallback",
            "enum": [
              "gemini",
              "groq",
              "openrouter"
            ],
            "type": "string",
            "description": "Third-level fallback if both primary and first fallback fail."
          },
          "llmModel": {
            "title": "LLM Model (optional override)",
            "type": "string",
            "description": "Override the model identifier for the selected provider. Free-text so you can pick any current model without waiting for us to rebuild. Defaults: Gemini -> gemini-3.6-flash (workhorse — was gemini-2.5-flash, restricted to existing users in Aug 2026). Groq -> openai/gpt-oss-120b (was llama-3.3-70b-versatile, decommissioned by Groq on 2026-08-16). OpenRouter -> meta-llama/llama-3.1-8b-instruct. Provider catalogs: Gemini https://ai.google.dev/gemini-api/docs/models, Groq https://console.groq.com/docs/models, OpenRouter https://openrouter.ai/models. This field takes any model string the provider supports. Defaults may change without notice as providers retire models."
          },
          "llmApiKey": {
            "title": "LLM API Key (Groq/OpenRouter)",
            "type": "string",
            "description": "API key for Groq or OpenRouter. Not needed for Gemini if GEMINI_API_KEY env var is set."
          },
          "geminiApiKey": {
            "title": "Gemini API Key",
            "type": "string",
            "description": "OPTIONAL -- leave blank to use managed mode (see above). API key for Google Gemini (free tier: 1M tokens/minute). Get one at https://aistudio.google.com/apikey"
          },
          "groqApiKey": {
            "title": "Groq API Key",
            "type": "string",
            "description": "OPTIONAL -- leave blank to use managed mode (see above). API key for Groq (fast inference, free tier available). Get one at https://console.groq.com/keys"
          },
          "openrouterApiKey": {
            "title": "OpenRouter API Key",
            "type": "string",
            "description": "OPTIONAL -- leave blank to use managed mode (see above). API key for OpenRouter (many free models available). Get one at https://openrouter.ai/keys"
          },
          "maxResultsPerCompany": {
            "title": "Max Results Per Company",
            "minimum": 0,
            "maximum": 10000,
            "type": "integer",
            "description": "Maximum jobs pushed per company. Filters (keywords, location, postedWithinDays) apply BEFORE this cap and before charging, so the cap counts only matching jobs. 0 means all jobs: the actor pages to the end of the company's job list, bounded by a hard limit of 10,000 jobs per company, the run's time limit and the page cap. Rows are pushed and charged one by one as they are read, so a run that stops early keeps every row already delivered. Default 200.",
            "default": 200
          },
          "includeJobDetails": {
            "title": "Include job details",
            "type": "boolean",
            "description": "On: the actor reads every job's detail page. ATS career sites (Workday, Oracle, Phenom, Eightfold, SuccessFactors, iCIMS, Taleo, Greenhouse and more) get the full description from the job's detail page or API (included in the normal job price). For jobs on the employer's own site the actor also opens the detail page, stores the posting text as markdown in description_md and extracts structured fields: employment type, posting and closing dates, salary (min, max, currency, period), location parts, workload percent (for example 80-100%), department, contact person, email and phone, application deadline, remote or hybrid, required languages, reference number (JSON-LD JobPosting first, then the page text, then one LLM call per job for what is still missing; the LLM may only return values the page text contains). Each job whose employer-site detail page delivered data is charged one extra job-detail-result event (job-detail-result-managed when no API key is supplied). Off: no detail pages are read at all, rows come straight from the listing pages (title, location, link, posted date), which is the fast mode for 'all jobs' on huge career sites. Not set (default in code): ATS descriptions on, employer-site detail pages off, exactly as before this option existed. Replaces the older includeDescription and includeFullPostingText."
          },
          "includeDescription": {
            "title": "Include job descriptions (deprecated alias)",
            "type": "boolean",
            "description": "Deprecated alias of the ATS part of includeJobDetails, used only while includeJobDetails is not set. false = ATS rows are pushed straight from the listing pages with no per-job detail fetches. Default true.",
            "default": true
          },
          "includeFullPostingText": {
            "title": "Include full posting text (deprecated alias)",
            "type": "boolean",
            "description": "Deprecated alias of the employer-site part of includeJobDetails, used only while includeJobDetails is not set. true = open the detail pages on the employer's own site (text and structured fields). Default false.",
            "default": false
          },
          "maxFullTextChars": {
            "title": "Max job-detail text characters",
            "minimum": 500,
            "maximum": 50000,
            "type": "integer",
            "description": "Only with includeJobDetails. Maximum characters of posting text stored in description_md per job, and the maximum sent to the LLM when it fills missing detail fields. Longer texts are cut at a paragraph boundary.",
            "default": 12000
          },
          "fullTextLlmFill": {
            "title": "LLM fill of missing job-detail fields",
            "type": "boolean",
            "description": "Only with includeJobDetails. On (default): when fields such as location, posting date, workload, department, contact person or application deadline are still missing after the JSON-LD and text pass, ONE LLM call per job fills them, and only with values written in the posting text. Off: the text and the deterministic fields are still read but no LLM call is made, so detail_llm_calls stays 0. Local runs can also set the environment variable WJE_FULLTEXT_LLM_FILL=0.",
            "default": true
          },
          "keywords": {
            "title": "Keywords",
            "type": "array",
            "description": "Only keep jobs whose title contains at least one of these keywords (case and accent insensitive). Applied on the company's own job list: server-side where the ATS supports search (Workday, Oracle, Eightfold, Phenom, SuccessFactors, iCIMS), otherwise page by page while listing, before descriptions are fetched and before any charge. Leave empty for all jobs.",
            "items": {
              "type": "string"
            }
          },
          "location": {
            "title": "Location",
            "type": "string",
            "description": "Only keep jobs in this city, region or country (for example London, Texas, United Kingdom, GB, Germany, DE). Country names and ISO codes match both ways. Remote jobs match when they do not name a different country. Applied server-side where the ATS exposes location filters (Workday, Oracle), otherwise locally before descriptions are fetched and before any charge. A job whose location cannot be read is not kept."
          },
          "postedWithinDays": {
            "title": "Posted within days",
            "minimum": 1,
            "maximum": 3650,
            "type": "integer",
            "description": "Only keep jobs posted within the last N days. Jobs whose posting date is unknown are kept. Leave empty for no date filter."
          },
          "sortBy": {
            "title": "Sort by",
            "enum": [
              "newest",
              "relevance"
            ],
            "type": "string",
            "description": "Newest first uses the ATS's own newest-first sort where it has one; otherwise rows come in the site's own order. Relevance only differs from newest when keywords are set and the ATS supports relevance ranking. Rows are pushed as they are read (never buffered to sort). Workday career sites list newest first by posting day, and rows within the same day come in site order, so the posted date shown can differ slightly between neighbouring rows. Sites without a date sort return their own order.",
            "default": "newest"
          },
          "maxPagesPerCompany": {
            "title": "Max Pages Per Company",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "Hard cap on pages fetched per company (career page, pagination and ATS pages combined). Independent of maxResultsPerCompany, which caps jobs. Defaults to 20 when omitted. A company also stops early after 3 consecutive pages that yield no jobs."
          },
          "companyTimeBudgetSecs": {
            "title": "Company Time Budget (seconds)",
            "minimum": 30,
            "maximum": 3600,
            "type": "integer",
            "description": "Upper bound of wall-clock time per company. Each company gets a fair share of the remaining run time (never more than this); when it is used up the company stops (remaining pages are skipped, rows already extracted are kept and charged) and its job_extraction_done row carries reason 'company_time_budget'. No company fee is charged when no rows were delivered. Default 240.",
            "default": 240
          },
          "maxConcurrency": {
            "title": "Max Concurrency",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Maximum number of concurrent HTTP requests",
            "default": 3
          },
          "webhookUrl": {
            "title": "Webhook URL",
            "type": "string",
            "description": "URL to POST results to when extraction completes (e.g., your API endpoint)"
          },
          "skipAtsExtraction": {
            "title": "Skip LLM for Known ATS Platforms",
            "type": "boolean",
            "description": "When a supported ATS platform is detected (Lever, Greenhouse, Workday, Ashby, etc.), skip LLM extraction and output the ATS URL instead. Feed these URLs into the Career Site Jobs Scraper for faster, cheaper, structured extraction without hallucination risk. Supports 16 ATS platforms.",
            "default": false
          },
          "handoverSharedBoards": {
            "title": "Hand over shared job boards (with employer)",
            "type": "boolean",
            "description": "A company whose job system is an onlyfy tenant that serves several employers (a group board such as VITREA, five rehab clinics on one tenant) is only handed over when this is on: the summary row then ends ats_handover_available with ats_url = the tenant root and ats_employer = the company name, and the consumer must pass ats_employer to career-site-jobs-scraper as the query field employer so only that employer's jobs are read. Off (default): such a company is still read from its own pages. A company's own tenant (spitex-zuerich for Spitex Zuerich) is always handed over, whatever this setting is.",
            "default": false
          },
          "enableBrowserFallback": {
            "title": "Enable Browser Fallback",
            "type": "boolean",
            "description": "Automatically run browser-based extraction for companies flagged with JS rendering issues. Starts a separate browser actor run (additional cost). A 'browser_fallback_spawned' sentinel row is pushed to this dataset with the sub-run ID and dataset URL so you can poll the browser run for real results.",
            "default": false
          },
          "_managedKeyProfile": {
            "title": "Managed key profile (internal)",
            "type": "string",
            "description": "Internal. Selects which managed OpenRouter key is used for cost attribution (\"default\" or \"jobfeed\"). Ignored when you supply your own API key. Never changes pricing."
          },
          "enablePlaywrightFallback": {
            "title": "[DEPRECATED — use enableBrowserFallback] Enable Playwright browser fallback",
            "type": "boolean",
            "description": "⚠️ DEPRECATED — this field will be removed on 2027-09-18. Please switch to `enableBrowserFallback`. Automatically run browser-based extraction for companies flagged with JS rendering issues. Starts a separate actor run (additional cost).",
            "default": false
          },
          "jobKeywords": {
            "title": "Job Keywords (alias of Keywords)",
            "type": "array",
            "description": "Alias of keywords, kept for existing integrations. Ignored when keywords is set. Only extract jobs matching these keywords. When set, each job gets a relevance score (high/medium/low). Leave empty to extract all jobs.",
            "items": {
              "type": "string"
            }
          },
          "llmBaseUrl": {
            "title": "Local LLM base URL (local runs only)",
            "type": "string",
            "description": "Local runs only. Base URL of an OpenAI-compatible server (for example http://localhost:1234/v1) used when llmProvider is openai_compatible. Not available on the Apify platform; ignored for every other provider."
          },
          "llmTimeoutSecs": {
            "title": "Local LLM request timeout in seconds (local runs only)",
            "minimum": 10,
            "type": "integer",
            "description": "Local runs only. Per-request timeout for the openai_compatible provider (default 180)."
          },
          "llmConcurrency": {
            "title": "Local LLM concurrent requests (local runs only)",
            "minimum": 1,
            "type": "integer",
            "description": "Local runs only. Maximum parallel requests to the openai_compatible server (default 1)."
          }
        }
      },
      "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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}