Atomic logo

The Company Search endpoint powers the typeahead experience inside our Transact SDK and is the right primitive for building your own employer picker. This guide covers how to call it, how to interpret results that look like duplicates, and how to differentiate companies that support full authentication from those that only offer manual deposit guidance.

For a snapshot of every company available to your account (useful when building a local index), see Company List. For full metadata on a single company, see Company Details. Once you have a company's _id and want to drop the user straight into its login step, see the Deeplink to Login guide.

POST /company/search performs a fuzzy match on company names using the required query property. The most useful optional filters are:

  • scopes — product suite. Values: user-link, pay-link.
  • product — restrict to companies that support a single product.
  • products — restrict to companies that support all of the listed products. product and products are mutually exclusive; sending both is rejected.
  • tags — include only companies with the listed tags (e.g. payroll-provider, gig-economy).
  • excludedTags — exclude companies with the listed tags. Use this for the franchise pattern below. It matches on a company's stored tags, so it is not a reliable way to filter manual-deposits — see Authentication vs. manual deposit guidance.
  • franchiseParent — return only the franchise children of the given parent company _id. See Franchises.
Example: Company Search (JavaScript/TypeScript)
async function searchForCompany(query) {
  const response = await fetch('https://api.atomicfi.com/company/search', {
    method: 'POST',
    headers: {
      'x-api-key': '<YOUR_API_KEY>',
      'x-api-secret': '<YOUR_API_SECRET>',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      query,
      product: 'deposit'
    })
  });

  if (!response.ok) {
    throw new Error(`API error: ${response.status} ${response.statusText}`);
  }

  const { data } = await response.json();
  return data;
}

Each item in the response data array includes _id, name, status, tags, branding, and a slim connector object with availableProducts and capabilities. Use _id for deeplinking; use tags and connector.availableProducts to branch your UI.

Sample response
{
  "data": [
    {
      "_id": "5d38f1e8512bbf71fb776015",
      "name": "DoorDash",
      "status": "operational",
      "connector": {
        "_id": "5d38f182512bbf0c06776013",
        "availableProducts": ["deposit"],
        "capabilities": {
          "distributionTypes": ["total"]
        }
      },
      "branding": {
        "logo": {
          "url": "https://cdn-public.atomicfi.com/logos/doordash.png",
          "backgroundColor": "#F2F2F2"
        },
        "color": "#9460FE"
      },
      "tags": ["gig-economy"]
    }
  ]
}

Searching for a brand like Sonic Drive-In, Taco Bell, or McDonald's will often return several companies with similar names. This is expected: large brands are modeled as a franchise network, where each operator has its own payroll setup and its own company record.

  • franchise-parent — the brand itself. Treat this as the canonical entry.
  • franchise-child — an individual franchisee. Each child has its own _id and connector, and references the parent via franchiseParent.

You have two recommended ways to present this in your UI:

Option A — Collapse children in the primary search

Exclude franchise children so the typeahead shows a single result per brand. When the user picks the parent, follow up with a secondary "Which location do you work at?" search to disambiguate.

Exclude franchise children
await fetch('https://api.atomicfi.com/company/search', {
  method: 'POST',
  headers: { /* ... */ },
  body: JSON.stringify({
    query: 'sonic',
    excludedTags: ['franchise-child']
  })
});

Option B — Dedicated franchise picker

This is the pattern used inside Transact. When a user selects a parent, route them to a separate screen that lists only the children of that parent. Pass the parent's _id as franchiseParent and the search is scoped to that brand's franchisees server-side — no client-side filtering required. query is still required, so send the brand name (or the user's own search text) alongside it.

Fetch the children of a franchise parent
// parentId is the _id of the company tagged `franchise-parent`
await fetch('https://api.atomicfi.com/company/search', {
  method: 'POST',
  headers: { /* ... */ },
  body: JSON.stringify({
    query: 'sonic',
    franchiseParent: parentId
  })
});
Either pattern is supported by the API. Pick based on how often your users work for franchised employers and how prominent you want that disambiguation step to be.

Some companies in the catalog do not support programmatic authentication. Instead of a login flow, users receive manual instructions to update their direct deposit. These companies carry a manual-deposits tag in the search response.

Whether these companies appear at all is controlled at the account level, not per request. If manual deposit instructions are not enabled for your account, Atomic removes them from your search results server-side and you will never see the tag. If they are enabled, they are returned and it is up to your UI to handle them. Contact us to change this setting for your account.

Because the tag is derived from a company's manual-deposit configuration rather than stored on the company record, excludedTags: ['manual-deposits'] is not a reliable way to filter these out. excludedTags matches stored tags only. Filter on the response instead, or have the account setting turned off.

Branch your UI on the tag

Inspect tags on each result and render a different CTA (for example, "View instructions" instead of "Connect"). This is closest to what Transact does: manual companies stay in the search results, and selecting one routes the user into a manual instructions flow rather than a login.

Branch on the manual-deposits tag
function ctaFor(company) {
  if (company.tags?.includes('manual-deposits')) {
    return { label: 'View instructions', kind: 'manual' };
  }
  return { label: 'Connect', kind: 'auth' };
}

If you would rather not surface them at all, drop them client-side after the request:

Filter manual-deposit companies from the response
const results = await searchForCompany('acme');

// `excludedTags` will not do this for you — the tag is derived, not stored.
const connectable = results.filter(
  (company) => !company.tags?.includes('manual-deposits')
);

Inspect connector capabilities for finer control

Company Search returns a slim connector payload. When you need to know how a company authenticates — headless credentials vs. SSO redirect, which distribution actions are supported — call Company Details and read connector.capabilities:

  • headlessAuthentication — supports username/password login.
  • ssoAuthentication — requires an SSO redirect.
  • authenticationMethods — e.g. ["standard-auth", "true-auth", "true-auth-desktop"].
  • distributionActions / distributionTypes — what the connector can do once authenticated.

If you want to host your own typeahead index instead of calling Company Search per keystroke, pull the full catalog from Company List and re-sync periodically. The semantics described above still apply on your side:

  • The _id for a given employer is stable. The same employer can, however, appear as multiple distinct companies because of franchise modeling — do not assume one row per brand.
  • When you re-sync, treat tags as the source of truth for franchise handling, and use status to suppress under-maintenance or disabled companies from your surfaces.
  • Company List applies the same account-level manual-deposit rule as Company Search, so a locally built index inherits it. Re-sync after any account setting change.
  • Use connector.availableProducts (and capabilities.supportedPaymentMethods for Pay Link) to pre-validate that an employer supports the product your user is trying to use.

status can be operational, under-maintenance, or disabled. Company Search never returns disabled companies — they are excluded server-side — so in practice you only need to handle under-maintenance here. disabled can appear on Company List and Company Details.

Users cannot start an authentication flow against an under-maintenance or disabled company, so surface a disabled state or filter these results before presenting them.

  • Unexpected duplicates — check the tags array. If you see franchise-child, the duplicates are intentional. Apply excludedTags: ['franchise-child'] or build a franchise picker.
  • Search results that can't be authenticated — look for manual-deposits in tags and render a manual-guidance CTA. Filtering with excludedTags will not remove them; see Authentication vs. manual deposit guidance.
  • Unauthorized — verify your x-api-key and x-api-secret headers.