SOURCE_ID: find_job_postings NAME: Find Job Postings CATEGORY: List Building Build a static list of open job postings — one row per role — filtered by job title, technologies named in the ad, location, salary, and the firmographics of the company posting. Imported records become available to your Floqer workflows for enrichment, scoring, outreach, and the like. INDEX: 1. Endpoints 2. Body shape (preview + create) 3. Filter fields catalogue 4. Dynamic options 5. How to configure end-to-end 6. Key notes 7. When to use ================================================================================ 1. ENDPOINTS ================================================================================ Source identifier (used in every endpoint path): `find_job_postings`. POST /api/v1/sources/find_job_postings/preview Scope: sources:read Returns a sample of job postings that WOULD be imported for the given filters, without creating anything. Use this to validate filter combinations and inspect row shape before committing. `metadata.total_results` reports the full match count. POST /api/v1/sources/find_job_postings Scope: sources:write Creates the source and starts a one-time import of matching postings (up to `max_count`). Returns `source_instance_id` (the new source's UUID). The import runs asynchronously. POST /api/v1/sources/find_job_postings/options/ Scope: sources:read Resolves dynamic option values for a filter field. Three field names are available: `company_technology_slug`, `job_technology_slug`, `industry_id` (see §4). GET /api/v1/sources//data Scope: sources:read Paginated rows imported into the created source. `` is the UUID returned by Create. Query: `page_no` (default 1), `page_size` (default 20, max 200). Source-agnostic; see concepts.txt §10. POST /api/v1/sources//sync Scope: sources:write Connects the created source to a workflow and (by default) backfills it with the source's current rows. `` here is the UUID returned by Create. Body: { workflow_id, field_mapping, push_existing?, run? } — `field_mapping` keys are `input.` references on the target workflow, values are the source fields to pull. This step is source-agnostic; see concepts.txt §10 for the full shape. No connection is required. ================================================================================ 2. BODY SHAPE (PREVIEW + CREATE) ================================================================================ Find Job Postings is always a one-time static import — there is no `pull_mode`, `schedule`, or recurring re-pull. `max_count` caps the import size. Preview accepts (all snake_case; unknown filter keys rejected with 400): filters required: object with at least one filter key (see §3) max_count optional: integer | string; ignored on preview Create accepts all the preview fields plus: name required: display name for the new source max_count optional: cap on rows imported; default 25. Hard max 10000 — values above are rejected with 400. Values < 1 are rejected with 400. The `filters` object is a flat bag of filter keys. Each filter key is a field base (see §3) combined with an operator suffix or range prefix — the full key documents one field + operator pair. The schema validates the field BASE of every key: an unrecognized base (a typo, or a field not in the catalogue) is rejected up front with a 400, so check §3 carefully. Example create body: { "name": "Remote senior backend roles, US", "max_count": 2000, "filters": { "job_title_or": ["Senior Backend Engineer"], "job_country_code_or": ["US"], "remote": true, "posted_at_max_age_days": 30 } } Preview response envelope: { "status": 200, "data": { "data": [ { "job_title": "...", "company": "...", "final_url": "...", ... } ], "metadata": { "total_results": 5678 } } } Rows are plain key/value objects describing each job posting (title, company, location, posted date, salary, application URL, and the technologies/company firmographics that matched). Preview returns a sample; `metadata.total_results` is the full match count. Create response envelope: { "status": 201, "data": { "source_instance_id": "", "name": "Remote senior backend roles, US", "created_at": "2026-06-02T12:00:00.000Z" } } ================================================================================ 3. FILTER FIELDS CATALOGUE ================================================================================ All filter keys live inside the `filters` object. Keys combine a FIELD BASE (below) with an OPERATOR SUFFIX or RANGE PREFIX. The validator rejects any key whose base is not listed here. Unlike Find Companies Hiring, job and company filters are FLAT (no `job_filters.` / `tech_filters.` dotted prefix). OPERATOR SUFFIXES (append to a text/array field base): _or match ANY of the given values (default OR) _not exclude rows matching ANY of the values _and match ALL of the given values _case_insensitive_or case-insensitive ANY match _partial_match_or partial (substring) ANY match _partial_match_not exclude on partial match _pattern_or regex ANY match _pattern_and regex ALL match _pattern_not regex exclude _exists presence check (boolean value) _or_null include rows with no value (numeric min_/max_) RANGE PREFIXES / SUFFIXES (numeric and date field bases): min_ / max_ range bounds min__or_null / max__or_null bound + include unknowns _max_age_days "within the last N days" (date fields) _gte / _lte explicit date bounds (ISO YYYY-MM-DD) ──────────────────────────────────────────────────────────────────── GROUP: company (firmographics of the company posting the role) ──────────────────────────────────────────────────────────────────── company_country_code Company HQ country, ISO-3166 alpha-2. Ops: _or, _not. company_technology_slug Technologies the company uses (slugs — §4). Ops: _or, _and, _not. company_name Company name keyword. Ops: _or, _case_insensitive_or, _not, _partial_match_or, _partial_match_not. company_description Company description regex. Ops: _pattern_or, _pattern_not. company_location Company city regex. Op: _pattern_or. company_domain Company domain. Ops: _or, _not. industry_id Numeric industry id (§4). Ops: _or, _not. revenue_usd Revenue (USD). Use min_/max_revenue_usd. funding_usd Funding (USD). Use min_/max_funding_usd. funding_stage Funding stage label, e.g. "Series A". Op: _or. company_type "recruiting_agency" | "direct_employer" | "all". Plain value. company_linkedin_url Company LinkedIn URL. Ops: _or, _exists. revealed_company_data Boolean — only postings with non-anonymised company data. employee_count Headcount. Use min_/max_employee_count (+ _or_null variants). ──────────────────────────────────────────────────────────────────── GROUP: job posting ──────────────────────────────────────────────────────────────────── discovered_at When the posting was discovered. Use discovered_at_max_age_days: N or discovered_at_gte/_lte. posted_at When the role was posted. Use posted_at_max_age_days: N or posted_at_gte/_lte. A default recency cap is applied server-side when omitted (see §6). only_jobs_with_hiring_managers Boolean — only postings with a known hiring manager. reports_to Use reports_to_exists: true to require a "reports to" field. easy_apply Boolean — only easy-apply postings. remote Boolean — only remote roles. final_url Use final_url_exists: true to require an application URL. job_title Role title. Ops: _or, _not, _pattern_or, _pattern_and, _pattern_not. Example: job_title_or: ["VP of Sales"] job_description Job-ad text regex. Ops: _pattern_or, _pattern_not. job_country_code Job location country, ISO-3166 alpha-2. Ops: _or, _not. job_location Job city/state regex. Ops: _pattern_or, _pattern_not. salary_usd Annual salary (USD). Use min_/max_salary_usd. job_technology_slug Technologies named in the ad (slugs — §4). Ops: _or, _and, _not. scraper_name Job board / source (e.g. "Indeed", "LinkedIn"). Use scraper_name_pattern_or. job_id Specific posting id. Op: _or. ──────────────────────────────────────────────────────────────────── STRUCTURAL KEY (not a filter) ──────────────────────────────────────────────────────────────────── order_by Result ordering: array of { field, desc }. Invalid fields are stripped server-side. Examples: Remote senior backend roles in the US, posted in the last 30 days: { "filters": { "job_title_or": ["Senior Backend Engineer"], "job_country_code_or": ["US"], "remote": true, "posted_at_max_age_days": 30 } } Roles mentioning Snowflake at companies with 200-5000 employees: { "filters": { "job_technology_slug_or": ["snowflake"], "min_employee_count": 200, "max_employee_count": 5000 } } Sales roles paying $120k+ at direct employers (no recruiting agencies): { "filters": { "job_title_pattern_or": ["(?i)sales"], "min_salary_usd": 120000, "company_type": "direct_employer" } } ================================================================================ 4. DYNAMIC OPTIONS ================================================================================ POST /api/v1/sources/find_job_postings/options/ Body: { "context"?: { ... } }. No connection required. company_technology_slug Context (optional): { "name_pattern": "" } to filter by name. Returns {value: , label: }. Feed the `value`s into `company_technology_slug_or` / `_and` / `_not`. job_technology_slug Same catalogue as above; feed into `job_technology_slug_*` keys. industry_id Context: none. Returns {value: , label: }. Feed the `value`s into `industry_id_or` / `industry_id_not`. Country codes are ISO-3166 alpha-2 (not a dynamic-options field). Revenue, funding, salary, and employee_count are open numeric ranges. ================================================================================ 5. HOW TO CONFIGURE END-TO-END ================================================================================ Step 1 — (Optional) Resolve technology slugs / industry ids POST /api/v1/sources/find_job_postings/options/job_technology_slug Body: { "context": { "name_pattern": "snow" } } Pick the slugs you want for the *_technology_slug filters. Step 2 — Preview before committing POST /api/v1/sources/find_job_postings/preview Body: { "filters": { "job_title_or": ["Senior Backend Engineer"], "job_country_code_or": ["US"], "posted_at_max_age_days": 30 } } Check `data[]` and `metadata.total_results`. Iterate until happy — preview never creates anything. Step 3 — Create the source POST /api/v1/sources/find_job_postings Body: { "name": "Remote senior backend roles, US", "max_count": 2000, "filters": { ...same filters as preview... } } Set `max_count` ≤ the Preview `metadata.total_results` (hard max 10000). Response: { "status": 201, "data": { "source_instance_id": "", "name", "created_at" }} Step 3b — (Optional) Poll imported rows while the backfill runs GET /api/v1/sources//data?page_no=1&page_size=20 ( = UUID from Step 3) Step 4 — Sync the source into a workflow POST /api/v1/sources//sync ( = the UUID from Step 3 ) Build `field_mapping`: a. GET /api/v1/workflows → pick the destination workflow_id. b. GET /api/v1/workflows//sheets//inputs → each input has a `reference` like `{{input.job_title}}`. c. Map each workflow input (reference WITHOUT braces) to a source field. Body: { "workflow_id": "", "field_mapping": { "input.job_title": "job_title", "input.url": "final_url" }, "push_existing": true, "run": "all" } See concepts.txt §10 for the full sync semantics (source-agnostic). The initial import runs asynchronously — Create returns once the source is created and the import has started, not when records have finished arriving. ================================================================================ 6. KEY NOTES ================================================================================ - No connection required. This endpoint does NOT return 424. - Unknown filter keys are rejected. The schema validates the field base of every key in `filters`; a key whose base is not in §3 returns 400 with the offending key named. Operator suffixes and min_/max_ prefixes are understood, so `company_country_code_or` and `min_salary_usd` are valid; `company_country_codes` (typo) is not. - `max_count` defaults to 25, hard max 10000. A non-positive or over-cap `max_count` is rejected up front with 400 (it is NOT silently clamped). - One-time static import. There is no `pull_mode` or `schedule`; re-run Create for a fresh pull. - Posted-date policy: a default recency cap is applied server-side when `posted_at_*` is omitted, so you don't pull stale postings. Pass an explicit `posted_at_max_age_days` or `posted_at_gte`/`_lte` to control it. - Preview does not import anything and does not consume credits. Create starts the real import and consumes credits; insufficient balance → 402. - Total counts are best-effort. `metadata.total_results` may lag slightly. ================================================================================ 7. WHEN TO USE ================================================================================ - Build a list of open roles for outreach/recruiting: "Remote senior backend roles in the US": job_title_or + job_country_code_or + remote. - Technographic role targeting: "Roles mentioning Snowflake": job_technology_slug_or. - Compensation-filtered roles: "Sales roles paying $120k+": job_title_pattern_or + min_salary_usd. - Combine with Sync to enrich each posting (company lookup, contact find, LLM message) inside a workflow. When you instead want one row PER COMPANY that is hiring (deduped to the employer, not the posting), use `find_companies_hiring`. ================================================================================ Last updated: 2026-06-02. Reference: https://floqer.com/docs/reference