SOURCE_ID: find_companies NAME: Find Companies CATEGORY: List Building Build a static list of B2B companies using location, firmographic, industry, and technographic filters. Imported records become available to your Floqer workflows for enrichment, scoring, outreach, and the like. INDEX: 1. Endpoints 2. Search model 3. Import lifecycle (one-time only) 4. Body shape (preview + create) 5. Filter fields catalogue 6. Dynamic options 7. How to configure end-to-end 8. Key notes 9. Where it fits 10. When to use ================================================================================ 1. ENDPOINTS ================================================================================ Source identifier (used in every endpoint path): `find_companies`. POST /api/v1/sources/find_companies/preview Scope: sources:read Returns up to 100 companies that WOULD be imported for the given filters, without creating anything. Use this to validate filter combinations and inspect row shape before committing. POST /api/v1/sources/find_companies Scope: sources:write Creates the source and queues an import of matching companies (up to `max_count`). Returns `source_instance_id` (the new source's UUID). The import runs asynchronously. 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. There is no dynamic-options endpoint for this source today (see §6). ================================================================================ 2. SEARCH MODEL ================================================================================ Every preview/create call is a filter-based company search. There is no "import mode" switch — you always pass a `filters` object. Filter groups: location countries, region_country_codes, city_region_country, include_operating_locations company company_sizes, revenue_ranges, company_age, number_of_locations industry (pick ONE taxonomy per request) linkedin_categories OR google_categories OR naics_categories When multiple industry taxonomies are sent, LinkedIn takes priority. technology company_tech_stack_category, company_tech_stack_tech keywords company_name, website_keywords Location mutual exclusion: Prefer `countries` OR `region_country_codes`, not both. Treat the two as mutually exclusive. Sending both is not rejected by the API but may produce unexpected matches. At least one key inside `filters` is required. An empty `{}` is rejected with 400. ================================================================================ 3. IMPORT LIFECYCLE (ONE-TIME ONLY) ================================================================================ Find Companies is always a one-time static import — there is no `pull_mode`, `schedule`, or recurring re-pull. max_count Caps how many companies are imported on create. Default 100. Min 1, max 60000. Ignored on preview (preview always caps at 100 rows regardless of `max_count`). type Optional string; defaults to `"filters"` when omitted. Set `max_count` no higher than the `metadata.total_results` returned by the matching Preview call — that's the true number of matching records, so a larger `max_count` just imports all of them and gains nothing. ================================================================================ 4. BODY SHAPE (PREVIEW + CREATE) ================================================================================ Preview accepts: filters required: object with at least one filter key (see §5) type optional: string; defaults to "filters" on create; ignored on preview 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 100 Example create body: { "name": "US Mid-Market SaaS", "max_count": 1000, "type": "filters", "filters": { "countries": ["us"], "company_sizes": ["51-200", "201-500"], "linkedin_categories": ["software development"] } } Preview response envelope: { "status": 200, "data": { "data": [ { "name": "Acme Corporation", "website": "https://www.acme.com", "country_name": "United States", "logo": "https://...", "linkedin_url": "https://...", ... } ], "metadata": { "total_results": 12345 } } } Rows are plain key/value objects (not `{label, value}` wrappers). Preview returns up to 100 rows; `metadata.total_results` is the total match count (best-effort). Create response envelope: { "status": 201, "data": { "source_instance_id": "", "name": "US and Canada Companies", "created_at": "2026-05-26T12:00:00.000Z" } } ================================================================================ 5. FILTER FIELDS CATALOGUE ================================================================================ All filter keys live inside the `filters` object. Array filters accept multiple values (OR semantics within the field). ──────────────────────────────────────────────────────────────────── GROUP: location ──────────────────────────────────────────────────────────────────── countries (string[]) ISO-3166 alpha-2 country codes. Case-insensitive. Example: ["us", "ca", "gb"] region_country_codes (string[]) State/region codes in "cc-rr" form (lowercase). Example: ["us-ca", "us-ny", "ca-on"] Prefer countries OR regions, not both. city_region_country (string[]) City strings. Formats: "San Francisco, US" (city + country) "Miami, FL, US" (city + region + country) "Paris, FR" When only a city name is given, country/region context from other location filters may be inferred. include_operating_locations (boolean) When true, location filters match operating locations as well as HQ. When false (the default), HQ-only matching. Example: false ──────────────────────────────────────────────────────────────────── GROUP: company ──────────────────────────────────────────────────────────────────── company_sizes (string[]) Employee-count buckets: "1-10", "11-50", "51-200", "201-500", "501-1000", "1001-5000", "5001-10000", "10001+" revenue_ranges (string[]) Annual revenue buckets: "0-500K", "500K-1M", "1M-5M", "5M-10M", "10M-25M", "25M-75M", "75M-200M", "200M-500M", "500M-1B", "1B-10B", "10B-100B" company_age (string[]) Company age buckets: "0-3", "3-6", "6-10", "10-20", "20+" number_of_locations (string[]) Office-count buckets: "0-1", "2-5", "6-20", "21-50", "51-100", "101-1000", "1001+" ──────────────────────────────────────────────────────────────────── GROUP: industry (use ONE taxonomy per request) ──────────────────────────────────────────────────────────────────── linkedin_categories (string[]) LinkedIn industry category names (lowercase). Example: ["software development", "marketing services"] Priority: if present, google_categories and naics_categories are ignored. google_categories (string[]) Google business category names (lowercase). Example: ["software company", "marketing agency"] Used only when linkedin_categories is empty. naics_categories (string[]) NAICS industry codes or labels. Example: ["51", "54"] Used only when linkedin_categories and google_categories are empty. ──────────────────────────────────────────────────────────────────── GROUP: technology ──────────────────────────────────────────────────────────────────── company_tech_stack_category (string[]) Tech stack categories. Example: ["CRM", "Cloud Services", "Marketing Automation"] company_tech_stack_tech (string[]) Specific technology/tool names (free text). Example: ["Salesforce", "Snowflake", "React"] ──────────────────────────────────────────────────────────────────── GROUP: keywords ──────────────────────────────────────────────────────────────────── company_name (string[]) Company name keywords (case-insensitive). Example: ["Acme", "Globex"] website_keywords (string[]) Keywords found on company websites. Example: ["machine learning", "sustainability"] Examples: US + Canada: { "filters": { "countries": ["us", "ca"], "include_operating_locations": false }, "type": "filters", "max_count": 60000 } US mid-market SaaS using CRM: { "filters": { "countries": ["us"], "company_sizes": ["51-200", "201-500"], "revenue_ranges": ["1M-5M", "5M-10M"], "linkedin_categories": ["software development"], "company_tech_stack_category": ["CRM"] } } California + New York by region: { "filters": { "region_country_codes": ["us-ca", "us-ny"] } } Companies using Salesforce or Snowflake: { "filters": { "countries": ["us"], "company_tech_stack_tech": ["Salesforce", "Snowflake"] } } Output fields on each row: Preview row: business_id, name, country_name, logo (+ domain, website when present) Imported row (Get Source Data): the same public fields the preview returns, plus type, created_at Filter-only fields — the list builder uses these to SELECT companies, but they do NOT come back on the returned/imported row: company_sizes, revenue_ranges, company_age, number_of_locations, linkedin_categories / google_categories / naics_categories, company_tech_stack_category / company_tech_stack_tech, website_keywords, region_country_codes, city_region_country, include_operating_locations. If a downstream workflow needs to route or score on industry, headcount, revenue, tech stack, etc., chain a separate enrichment action after sync (e.g. enrich_company_linkedin_profile) — the list-builder rows themselves won't carry those values. ================================================================================ 6. DYNAMIC OPTIONS ================================================================================ No dynamic-options endpoint is registered for this source today. Filter values (country codes, size buckets, revenue ranges, industry categories, tech categories) are fixed enumerations — use the allowed values listed in §5 directly. Maintain your own option lists from §5 until a public options surface ships. ================================================================================ 7. HOW TO CONFIGURE END-TO-END ================================================================================ Typical flow for an AI agent building a Find Companies source: Step 1 — Preview before committing POST /api/v1/sources/find_companies/preview Body: { "filters": { "countries": ["us", "ca"], "include_operating_locations": false }, "type": "filters", "max_count": 60000 } Body (narrower ICP): { "filters": { "countries": ["us"], "company_sizes": ["51-200"], "linkedin_categories": ["software development"] } } Check the returned `data[]` and `metadata.total_results`. Iterate the filters until you're happy — preview never creates anything. Step 2 — Create the source POST /api/v1/sources/find_companies Body: { "name": "US Mid-Market SaaS", "max_count": 1000, "filters": { "countries": ["us"], "company_sizes": ["51-200", "201-500"], "linkedin_categories": ["software development"] } } Set `max_count` ≤ the Preview `metadata.total_results`. Response: { "status": 201, "data": { "source_instance_id": "", "name", "created_at" }} Step 2b — (Optional) Poll imported rows while the backfill runs GET /api/v1/sources//data?page_no=1&page_size=20 ( = UUID from Step 2) Row keys match Preview. `total_count` grows until import finishes. Step 3 — Sync the source into a workflow (so its records flow downstream) POST /api/v1/sources//sync ( = the UUID from Step 2 ) First build `field_mapping` (two lookups): a. GET /api/v1/workflows → pick the destination workflow_id. b. GET /api/v1/workflows//sheets//inputs → each input has a `reference` like `{{input.website}}`. c. Map each workflow input (reference WITHOUT braces) to a source field — common mappings: "input.company_name" → "name" "input.website" → "website" "input.country" → "country_name" Body: { "workflow_id": "", "field_mapping": { "input.company_name": "name", "input.website": "website", "input.country": "country_name" }, "push_existing": true, "run": "all" } See concepts.txt §10 for the full sync semantics (source-agnostic). The initial import runs asynchronously — the create call returns as soon as the source is created and the import is queued, not when records have finished arriving. Expect records to appear over seconds to minutes depending on `max_count`. ================================================================================ 8. KEY NOTES ================================================================================ - No connection required. Unlike CRM sources, this endpoint does NOT return 424 for a missing integration. - Preview does not import anything and does not consume credits. Create queues the real import work. - Preview always caps at 100 rows regardless of `max_count` on the request body. `metadata.total_results` still reflects the full match count. - `max_count` defaults to 100 on create when omitted. Hard max is 60000. - Industry taxonomies are mutually exclusive: if `linkedin_categories` is non-empty, `google_categories` and `naics_categories` are ignored. Pick one taxonomy per request. - Country codes are case-insensitive ("us" and "US" both work). - The public API accepts only the flat snake_case body shown in §4. - Preview rows are plain key/value objects (not `{label, value}` wrappers). - Total counts are best-effort. `metadata.total_results` may lag slightly behind live data. - Search errors surface with their HTTP status on preview: 402 (insufficient credits), 429 (rate limit), 502 (search service failure). On create the import runs asynchronously, so a failure there surfaces on the source's status rather than as a synchronous create error. ================================================================================ 9. WHERE IT FITS ================================================================================ DATA Company firmographic, technographic, and location filters evaluated when you preview or create. THIS SOURCE Creates a static Find Companies source. On creation it starts an import of up to `max_count` matching companies. There is no recurring schedule — re-run create for a fresh pull. WORKFLOWS Imported companies become available to your Floqer workflows — wire the source into a workflow via Sync to enrich, score, or run outreach on each company automatically. ================================================================================ 10. WHEN TO USE ================================================================================ - Build a TAM list from firmographics: "US SaaS companies, 51-200 employees, $1M-$10M revenue": filters: countries + company_sizes + revenue_ranges + linkedin_categories. - Technographic targeting: "US companies using Salesforce": filters: countries + company_tech_stack_tech. - Geo-specific prospecting: "Companies in California and New York": filters: region_country_codes. - Named-account discovery: "Companies whose name contains Acme": filters: company_name. - Combine with Sync to push imported companies into an enrichment workflow (LinkedIn scrape → email find → LLM outreach). When you instead want to enrich a single known company (by domain or LinkedIn URL), use workflow action templates such as `enrich_company_linkedin_profile` or `floqer_company_firmographics` inside a workflow — see the action catalog in llms-full.txt. ================================================================================ This file is maintained manually. Last updated: 2026-05-28. Full interactive reference: https://floqer.com/docs/reference