OXYGENOxygen/ Docs
Guides

Source your first leads

Get real target companies or people into an Oxygen table — sourced from your ICP, or uploaded from a file you already have.

Rows land in a Table either way: Oxygen buys them from a provider, or you upload them.

Start a table in the app

Open Data → Tables, then New table at the top of the table rail (/tables/new). Choose Import a CSV to upload your list, Import from a link to fetch a public CSV, JSON or XLSX file or a Google Sheet (preview first, free), Import from HubSpot to bring CRM companies or contacts into Records, or Blank table to start with an empty grid. The same picker offers company sources (Company Search, lookalikes, local businesses on Google Maps, web search, a known page), a people search, the signal feeds and a webhook; every card shows its providers and per-row credit estimate before anything runs. Two cards read LinkedIn through accounts you have already connected rather than buying data: LinkedIn Post Engager Scraper collects the people who engage with posts you watch, and LinkedIn Profile Viewers collects the people who viewed your own profiles — you tick which connected accounts to read, they fill one table in the background at your accounts' safe daily read budget, and it is free. The same account list, with each account's status, sits behind the LinkedIn chip on that table, where unticking an account pauses it and keeps its rows. LinkedIn hides anonymous and private-mode viewers, so that one is always a partial sample of real viewers. Scrape LinkedIn Company Followers is different: it reads no account of yours. Paste a company page — a competitor's, say — and a follower count, and the free Check this page quotes the exact order (per follower, 100 minimum) before you buy. Our data provider fulfils the order on its side, so the table is created at once and fills when the followers arrive; there is no published delivery time. The same order runs from the terminal as oxygen tables followers check --company <page URL> --records <n> and then oxygen tables followers order --approved with the quoted --max-credits. From the terminal, oxygen tables sources lists the same catalog.

Under People, the People — Blitz card sources everyone who works at the companies you already have. Pick the table holding your companies, then the column holding each company's LinkedIn URL, narrow by job level, job function, connections, country, continent and sales region, and Oxygen fetches the first 50 people from the first company for 1 credit so you can look before buying the rest. Pages per company in the footer sets how deep each company goes — 50 people per page, 1 credit per page.

The Total addressable market and Signals source groups are temporarily hidden while their setup is redesigned. To source companies or signals, use the CLI paths below and the Signals guide.

From your ICP, from the CLI

Planning is free — the only provider call is the people planner's zero-credit count probe, which --no-estimate skips.

oxygen companies search plan \
  --prompt "Seed-stage B2B SaaS in the US hiring their first AE" \
  --target-count 500 --json

oxygen people search plan \
  --prompt "Heads of Sales at US B2B SaaS companies" \
  --titles "Head of Sales,VP Sales" --employees 20-200 --countries US --json

The plan returns ordered routes — each with estimated_credits — plus estimated_match_count, filter_application (which of your filters the provider applies, and which it drops), and the table_blueprint it would create. --target-count is capped at 50,000 per plan; larger asks return a clamp warning.

Not sure whether you want accounts or contacts? oxygen sourcing plan --prompt <file|text> and oxygen lead-sourcing plan --prompt <file|text> classify the request and recommend routes without executing any paid tool.

Dry-run next — still free, and --mode defaults to dry_run:

oxygen companies search run --prompt "Seed-stage B2B SaaS in the US" --target-count 500 --json

Then live. This spends credits:

oxygen companies search run \
  --prompt "Seed-stage B2B SaaS in the US" --target-count 500 \
  --mode live --approved --max-credits 2500 --json

Both flags are mandatory. Without --approved the run fails approval_required (409); without --max-credits, spend_cap_required (400); with a ceiling under the route estimate, spend_cap_too_low (400), and the error names the estimated_max_credits you need.

Omit --table and Oxygen creates the table for you. Company rows dedupe on domain, people rows on linkedin_url, so re-running the same search updates rows instead of duplicating them.

Everyone at the companies you already have

When the account list is already a table, point people search at it instead of describing the companies again. --from-table names the table, --linkedin-url-column names the column holding each company's LinkedIn URL, and Oxygen runs Blitz Employee Finder once per company: 1 credit per company per page, 50 people per page. --prompt is optional here — the table is the request — but pass one to steer the persona further.

oxygen people search plan \
  --from-table accounts --linkedin-url-column company_linkedin_url \
  --job-functions Sales,Marketing --seniorities VP,Director \
  --countries US --sales-regions EMEA --min-connections 200 --json

The plan's source.companies_planned is the real company count the run will spend against. Buy the first company first, then source the rest into the same table:

oxygen people search run --plan-json ./plan.json --route-id employee-roster-from-table \
  --company-limit 1 --mode live --approved --max-credits 10 --json

oxygen people search run --plan-json ./plan.json --route-id employee-roster-from-table \
  --table <table-from-the-first-run> --company-offset 1 \
  --pages-per-company 2 --mode live --approved --max-credits 1000 --json

--max-pages means pages per company on a --from-table run; --pages-per-company is the same ceiling spelled the way it reads. The people table's default columns are First Name, Last Name, LinkedIn URL and a Company link back to the row the person was found at, so every person stays attached to their account.

From a file you already have

Free — no provider call, no credits.

In the app: open the table and click Import rows in the top bar (an icon button; the tooltip names it), or pick Import a CSV in the wizard. The dialog is Import file, or Create table from file when there is no table yet: Click to upload or drag and drop, CSV, JSON, JSONL, or XLSX. Set Table name and Folder when it is creating the table, press Preview to check the parsed rows — importing into an existing table then draws Column mapping — then Import.

oxygen tables import --create "Starter TAM" --file leads.csv --background --json
oxygen tables import <table> --file leads.csv --upsert-key domain --json

--format is inferred from the extension (json, jsonl, csv, xlsx). Files over 500 rows load in the background and the command returns before the rows land — wait on the table-ingestions wait <id> it prints before you trust a row count. The per-file row ceiling equals your plan's rows-per-Table limit (oxygen limits show). The file-size ceiling is 500 MiB on free, 1 GiB on $49–$99, 2 GiB on $199–$499 and 5 GiB on $999 and up (table and signal limits); an import into a non-empty Table must also fit its remaining capacity. JSON and XLSX are read whole, so they stop at 100 MiB on every plan: save a larger file as CSV or JSONL. A CSV or JSONL file over 64 MiB uploads straight from disk to file storage and always loads in the background.

Watch it fill

A live search returns a durable ingestion run, not rows. The envelope carries ingestion_run and a web_url for /tables/<table-id>/ingestions/<run-id>, which shows Items, Rows, Inserted, Updated, and the Pending / Running / Completed / Failed breakdown.

oxygen table-ingestions get <run-id> --json
oxygen table-ingestions wait <run-id> --json   # polls until terminal; 600s default
oxygen tables preview <table> --limit 25 --json

counts.rows at 0 while the status is still queued or running is normal — the envelope's message says so explicitly.

If the run ends completed_with_errors or failed, the table holds whatever landed first; oxygen table-ingestions items <run-id> --status failed --json names the items that broke.

If a live search returns search_ingestion_enqueue_failed (503), the table was created but nothing was queued into it. The error's next_step says whether Oxygen already archived the empty table or you must archive it before retrying.

On this page