If you're job hunting on an H-1B, OPT, STEM OPT or TN, you know the routine. You find a role that fits, read the whole posting, get excited, and then see the last paragraph: "We are unable to sponsor visas for this position." Or "Applicant must be a U.S. person under ITAR." Do that fifty times a week and it eats hours.
When a company won't sponsor, that sentence is usually right there in the job description. So I built a scraper that reads it for you. This post shows how to pull jobs straight from company job boards (Greenhouse, Lever, Workday and more), flag the work-authorization wording on every posting, and drop the dead ends automatically. All the numbers below come from real runs I did on Oct 18, 2026.
Try it free: ATS Jobs Scraper on the Apify Store. Apify's free plan includes $5 of usage credit every month, no credit card. At $0.001 per job, that's roughly 5,000 jobs a month. There's also a ready-made example for a Workday career site you can run in one click.
Why company job boards, not LinkedIn
Most tech companies post jobs through an applicant tracking system (ATS): Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee or Personio. Every one of these has a public job board, and most have a public JSON feed behind it. The Actor reads those feeds directly. It needs no browser and no login, and the full description comes with every job. The full description is the part you need, because that's where the sponsorship sentence lives.
What makes this different
Other job scrapers exist, so here's the honest comparison. The only AI-based visa sponsorship filter I found on the Apify Store covers Greenhouse and Workday only. None of the five most-used ATS job scrapers there flag US citizenship, ITAR or security clearance requirements.
This one adds a workAuthorization object to every job:
-
sponsorship:offered,not_offered, ornull(the posting doesn't say) usCitizenshipRequired-
usPersonRequired(ITAR / export rules: citizen or green card) exportControlled-
securityClearance:required,preferred, ornull -
evidence: the exact sentences each flag came from
And two switches, excludeNoSponsorship and excludeCitizenshipOrClearance, drop matching jobs before they're saved. Dropped jobs aren't charged.
Copy-paste input (about 10 seconds)
Open the Actor, switch the Input tab to JSON, paste this and click Start:
{ "companies": ["affirm", "spacex", "ramp"], "keywords": ["software engineer"], "maxJobsPerCompany": 10, "excludeNoSponsorship": true, "excludeCitizenshipOrClearance": true } My run of exactly this took 10 seconds. It saved 19 jobs and dropped 11. All 10 SpaceX software roles it checked need US-person status under ITAR, so SpaceX returned 0.
You can give bare slugs (stripe), board URLs (https://jobs.lever.co/spotify) or careers pages (https://ramp.com/careers). Workday always needs the full career-site URL, like https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite. Slugs don't always match company names. anduril came back BOARD_NOT_FOUND in my test, because their Greenhouse board is andurilindustries. When a slug misses, paste the board URL.
What a flagged job looks like
Two real records (trimmed):
[ { "company": "Affirm", "ats": "greenhouse", "title": "Senior Software Engineer, Affirm Bank", "location": "Remote US", "workAuthorization": { "sponsorship": "not_offered", "usCitizenshipRequired": false, "usPersonRequired": false, "exportControlled": false, "securityClearance": null, "evidence": ["Please note that visa sponsorship is not available for this position."] }, "applyUrl": "https://job-boards.greenhouse.io/affirm/jobs/7812982003" }, { "company": "SpaceX", "ats": "greenhouse", "title": "AI Security Software Engineer (Starshield)", "location": "Hawthorne, CA", "workAuthorization": { "sponsorship": null, "usCitizenshipRequired": false, "usPersonRequired": true, "exportControlled": true, "securityClearance": "required", "evidence": [ "- To conform to US Government export regulations, applicant must be a (i) US citizen or national, (ii) US lawful, permanent resident (aka green card holder), (iii) Refugee under 8 USC.", "- Active Top Secret, Top Secret SCI, or DOE Level Q clearance", "ITAR REQUIREMENTS:" ] }, "applyUrl": "https://boards.greenhouse.io/spacex/jobs/8880556002?gh_jid=8880556002" } ] Before and after the filters
The bigger test covered Affirm, Stripe, Ramp, Databricks, SpaceX and NVIDIA's Workday site (Anduril missed, as noted above). It used keywords "software engineer" and "frontend" and maxJobsPerCompany: 50. I ran it once with no filters and once with both filters on:
| Board | ATS | No filters | Both filters on |
|---|---|---|---|
| Affirm | Greenhouse | 50 | 49 |
| Stripe | Greenhouse | 50 | 50 |
| Databricks | Greenhouse | 50 | 50 |
| SpaceX | Greenhouse | 50 | 0 |
| Ramp | Ashby | 22 | 22 |
| NVIDIA | Workday | 50 | 50 |
| Total | 272 | 221 (51 dropped) |
In the unfiltered run, all 50 SpaceX jobs were flagged US-person/ITAR, and 4 also needed a clearance. One Affirm job said no sponsorship, and 4 Affirm jobs said sponsorship is offered. The other 216 jobs said nothing either way.
That last number is the important lesson: most postings never mention sponsorship. Silence doesn't mean yes, so treat null as "ask the recruiter."
Keep only jobs that explicitly offer sponsorship
The filters only remove the clear "no" jobs. To keep only the clear "yes" jobs, filter the dataset on workAuthorization.sponsorship = "offered". You can do that in the dataset view, in a spreadsheet after a CSV export, or in code:
const yes = items.filter(j => j.workAuthorization?.sponsorship === "offered"); Then read the evidence. All 4 of Affirm's "offered" jobs in my run said: "We are able to offer visa sponsorship for this role, but do require that someone is based in Spain for the role." That's real sponsorship, but for Spain, not a US H-1B. The flag can't know which country you need, and the evidence sentence can.
Get new jobs every day
Two inputs turn this into a daily alert: "onlyNewJobs": true and a "stateKey" like "visa-alerts". Each run then returns, and charges for, only jobs it hasn't returned before. Save your input as a Task and add a daily Schedule in the Apify console. To get the results somewhere useful, use Apify's integrations on the Task:
- Slack: get a message in a channel when the run succeeds, with a link to the results.
- Email (Gmail integration): send yourself an email when the run finishes, with the dataset attached as CSV or JSON.
- Google Sheets: connect through Apify's Make, Zapier or n8n integrations, which can add each job as a row.
- Webhooks: POST to your own endpoint when the run finishes.
What it costs
- $0.001 per job saved, plus a $0.00005 start fee per run
- Jobs dropped by the work-auth filters are not charged
- Error records (like a board not found) count as one item each
My unfiltered run saved 273 items (272 jobs + 1 error), about $0.27. The filtered run saved 222, about $0.22. The 51 dropped jobs cost nothing. You can set a max cost per run and the Actor stops cleanly when it hits it.
Honest limits
- Rules, not AI. Flags come from wording rules ("unable to sponsor", "must be a US citizen", "TS/SCI"). Unusual phrasing can be missed, and topic mentions can trip a flag. One NVIDIA data-governance job got
exportControlled: truebecause the description talks about export controls as a product problem. Always checkevidence. - No description, no flags. Jobs without description text get
workAuthorization: nulland are never dropped. KeepincludeDescriptionon (the default). -
maxJobsPerCompanyapplies before the filters. That's why SpaceX went from 50 to 0 instead of being refilled with other jobs. Raise the cap if you filter heavily. - Workday lists about 2,000 jobs per site at most. Add keywords for huge employers.
- This is not legal advice. A flag tells you what a posting says, not what you're eligible for. When it matters, ask the employer or an immigration attorney.
Try it and tell me what's wrong
- Store page: ATS Jobs Scraper (free plan: $5 credit a month, no card)
- One-click example: scrape a Workday career site
It's new, so I'd really like feedback, especially from people on visas. If you find a sponsorship sentence it misses or a flag that's wrong, paste the wording in the comments or open an issue on the Actor page. Those reports are the fastest way to make the rules better.
![Why Object.keys(user) Returns string[], Not (keyof User)[]: TypeScript Means It](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2q4w48b9bzs43tynyhsf.png)
