USCIS H-1B Employer Data: Approvals and Denials by Company
Pricing
from $1.40 / 1,000 employer rows
USCIS H-1B Employer Data: Approvals and Denials by Company
Official USCIS H-1B employer files as rows: fiscal year, employer, initial and continuing approvals and denials, totals and denial rate, NAICS sector with its name, tax-ID last four, city, state and ZIP. Filter by employer, state, city, industry, ZIP or petition volume. FY2009-FY2023, UTC dates.
Pricing
from $1.40 / 1,000 employer rows
Rating
0.0
(0)
Developer
Samat Makatov
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Security
Limited permissionsShare
Official USCIS H-1B employer files as rows: fiscal year, employer, initial and continuing approvals and denials, totals and denial rate, NAICS sector with its name, tax-ID last four, city, state and ZIP. Filter by employer, state, city, industry, ZIP or petition volume. FY2009-FY2023, UTC dates.
This actor reads the H-1B Employer Data Hub files published by U.S. Citizenship and Immigration Services — one CSV per fiscal year, no key and no account — and turns them into filtered, sorted dataset rows. One published row is one employer in one fiscal year with the counts of workers USCIS first decided on: initial employment and continuing employment, approvals and denials. On top of the published columns every row gets the totals, the number of decisions and the denial rate, the plain name of the NAICS sector and the full name of the state, so a dataset reads without a code table. Fiscal years 2009 through 2023 are published as files; a fiscal year runs 1 October to 30 September. No API key, no proxy, no browser.
Need it tailored to your workflow? Missing a field, a filter, another source or a different output format? Open an issue and describe what you need — we read every request and adapt the actor.
Use cases
- One company's H-1B record —
employers: ["microsoft"]with a fiscal year: every entity whose filed name contains that text, with approvals, denials and the denial rate. Case, punctuation and spaces are ignored, sowalmartfindsWAL MART ASSOCIATES INC. For journalists and analysts after a news story. - The biggest sponsors in a state —
states: ["CA"],minTotalApprovals: 50,sort: "approvalsDesc": the long alphabetical tail of one-petition employers disappears and the employers that sponsor at scale come first. For recruiters, relocation services and sales teams. - Where decisions went against the employer —
naics: ["54"],minTotalDenials: 1,sort: "denialsDesc", ormaxDenialRate: 0.05for the other direction. For immigration lawyers and in-house mobility teams. - A five-year sponsor profile —
mode: "employerTotals"with five fiscal years: one row per employer withyearCount,firstFiscalYear,lastFiscalYearand the summed counts. For investor and policy research. - Sponsors in one city —
cities: ["Seattle"]: who files H-1B petitions from a city, largest first. For job seekers and local press. - A watch on a state or an industry —
onlyNew: true: each run writes only rows that were not written before, so you never pay twice for the same employer when you re-run a watch with wider filters, take the nextmaxItemsrows of a long list, or add a fiscal year to it. The published files do not change, so a schedule on the same input writes rows until the list is exhausted; a fiscal year USCIS publishes later has to be chosen infiscalYearsonce this actor lists it.
Input
Every field is optional. A run with nothing but fiscalYears returns the top maxItems employers of that year by
approvals.
| Field | Type | Default | Allowed values / notes |
|---|---|---|---|
mode | string (enum) | byYear | byYear = one row per employer per fiscal year, as published. employerTotals = one row per employer with the selected fiscal years summed up (adds yearCount, firstFiscalYear, lastFiscalYear, fiscalYearsCovered, and has no fiscalYear). Strict: the platform refuses any other value before the run starts. |
fiscalYears | string[] (enum) | prefill ["2023"], empty = FY2023 | One file per year: 2023 … 2009 (all years). Strict enum — FY2024 and later are refused before the run starts, because they are not published as files (see Limits). Max 15 values. |
employers | string[] (free text) | prefill ["microsoft"] | Matched inside the employer name as filed: microsoft, deloitte, mayo clinic. Case, punctuation and spaces are ignored — walmart finds WAL MART ASSOCIATES INC; several values are OR. The value that found a row is written to matchedEmployer. A value that matched nothing is named in the run's status message; a value written in another script (日本電気株式会社) matches nothing, because the files hold the names in ASCII capitals. Max 20 values. |
states | string[] (enum) | — | Two-letter codes of the employer's mailing address (all 56 codes), OR-combined. Strict enum. stateName is written to every row. |
cities | string[] (free text) | — | Whole city name: Seattle, NEW YORK, san jose. Case and extra spaces are ignored, several values are OR; york does not match NEW YORK. Unmatched values are named in the status message. Max 20. |
zipPrefixes | string[] (free text) | — | A full ZIP (98101) or its first 2-5 digits (100 keeps every 100xx ZIP). Several values are OR; combined with states/cities as AND. Anything that is not 2-5 digits is refused with a message. Max 20. |
naics | string[] (enum) | — | Two-digit NAICS sectors (all 25), OR-combined. Strict enum. 99 is the file's own "industry unknown". naicsLabel is written to every row. |
minTotalApprovals | integer | — | Keep rows with at least this many approved workers (totalApprovals). In employerTotals the threshold applies to the summed row. |
minTotalDenials | integer | — | Keep rows with at least this many denied workers (totalDenials). 1 = every row where a decision went against the employer. |
maxDenialRate | number | — | Keep rows whose denialRate is at most this share (0.1 = refused in at most a tenth of the decisions). Rows with no decision at all have no rate and are dropped by this filter. |
sort | string (enum) | approvalsDesc | approvalsDesc, denialsDesc, denialRateDesc, employer, fiscalYear. Strict enum. In byYear the order is applied inside each fiscal year (years are written newest first); in employerTotals to the whole result. |
maxItems | integer | 50 | 1-5000. Hard cap on the rows this run writes, and therefore on what it costs. |
onlyNew | boolean | false | Remember the rows written and, on the next run of the same actor or task, write only rows that were not written before — a rerun therefore returns the next rows of the list, not nothing. The published files do not change, so this is a way to avoid paying twice, not a freshness window: the files carry no per-row timestamp and a new fiscal year has to be chosen in fiscalYears. |
fields | string[] (free text) | all columns | Keep only these columns, in this order; fiscalYear, employer, totalApprovals, totalDenials, url and fetchedAt are always kept (first, unless you list them yourself, and then where you put them). Column names are matched ignoring case and underscores, and a few short names work (approvals, denials, decisions, rate, industry, year, taxid). A name that is none of these is reported in the run's status message with the closest column; if no name is a column at all, the run stops at once and names them. |
Reference
Fiscal years
2023, 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012, 2011, 2010, 2009.
A fiscal year runs from 1 October of the previous calendar year to 30 September (FY2023 = 1 October 2022 to 30 September 2023). Each year is a separate file and a separate download. FY2023 is the newest year published as a file; a value outside this list is refused by the platform before the run starts, with the list of allowed values.
NAICS sectors
| Code | Sector |
|---|---|
11 | Agriculture, forestry, fishing and hunting |
21 | Mining, quarrying, oil and gas extraction |
22 | Utilities |
23 | Construction |
31 | Manufacturing: food, textiles, apparel |
32 | Manufacturing: wood, paper, chemicals, plastics |
33 | Manufacturing: metals, machinery, electronics, vehicles |
42 | Wholesale trade |
44 | Retail trade: vehicles, building, food, health stores |
45 | Retail trade: clothing, sports, general and online stores |
48 | Transportation |
49 | Postal service, couriers and warehousing |
51 | Information: software publishing, telecoms, media |
52 | Finance and insurance |
53 | Real estate, rental and leasing |
54 | Professional, scientific and technical services (IT consulting, engineering, research) |
55 | Management of companies and enterprises |
56 | Administrative and support services (staffing agencies) |
61 | Educational services (universities, schools) |
62 | Health care and social assistance (hospitals, clinics) |
71 | Arts, entertainment and recreation |
72 | Accommodation and food services |
81 | Other services except public administration |
92 | Public administration |
99 | Industry unknown, as the file records it |
naicsLabel in every row carries the sector name from this table. Code 99 covers a large share of the smaller
employers: the file itself does not know their industry.
States and territories
AL Alabama · AK Alaska · AZ Arizona · AR Arkansas · CA California · CO Colorado · CT Connecticut ·
DE Delaware · DC District of Columbia · FL Florida · GA Georgia · HI Hawaii · ID Idaho · IL Illinois ·
IN Indiana · IA Iowa · KS Kansas · KY Kentucky · LA Louisiana · ME Maine · MD Maryland ·
MA Massachusetts · MI Michigan · MN Minnesota · MS Mississippi · MO Missouri · MT Montana · NE Nebraska ·
NV Nevada · NH New Hampshire · NJ New Jersey · NM New Mexico · NY New York · NC North Carolina ·
ND North Dakota · OH Ohio · OK Oklahoma · OR Oregon · PA Pennsylvania · RI Rhode Island ·
SC South Carolina · SD South Dakota · TN Tennessee · TX Texas · UT Utah · VT Vermont · VA Virginia ·
WA Washington · WV West Virginia · WI Wisconsin · WY Wyoming · PR Puerto Rico · GU Guam ·
VI U.S. Virgin Islands · AS American Samoa · MP Northern Mariana Islands.
The state is the employer's mailing address on the petition, not the worksite. Some rows of the oldest files carry no state at all; those rows can never match a state filter.
Sort orders
approvalsDesc— most approved workers first (the default; the published files are alphabetical).denialsDesc— most denied workers first.denialRateDesc— highest share of denials first; rows without a single decision go last.employer— employer name, A to Z.fiscalYear— newest fiscal year first, then most approvals.
Examples
One company in the newest published year
{ "mode": "byYear", "fiscalYears": ["2023"], "employers": ["microsoft"], "sort": "approvalsDesc", "maxItems": 25 }
Every Microsoft entity in FY2023 with its approvals, denials and denial rate.
The biggest sponsors in California, with chosen columns
{"fiscalYears": ["2023"],"states": ["CA"],"minTotalApprovals": 50,"sort": "approvalsDesc","maxItems": 25,"fields": ["employer", "state", "totalApprovals", "denialRate"]}
Californian employers with 50 or more approved workers, largest first, narrowed to four columns (plus the always-kept ones).
Denials in technical services
{ "fiscalYears": ["2023"], "naics": ["54"], "minTotalDenials": 1, "sort": "denialsDesc", "maxItems": 25 }
IT consulting, engineering and research employers with at least one denied worker, most denials first.
Five fiscal years of one employer group, summed
{ "mode": "employerTotals", "fiscalYears": ["2023", "2022", "2021", "2020", "2019"], "employers": ["amazon"], "maxItems": 20 }
One row per Amazon entity with the five years added up, yearCount and the first and last year with decisions.
Hospitals and clinics above a size threshold
{ "fiscalYears": ["2023"], "naics": ["62"], "minTotalApprovals": 10, "sort": "approvalsDesc", "maxItems": 25 }
Universities in one state, 20 rows at a time
{ "fiscalYears": ["2023"], "naics": ["61"], "states": ["MA"], "onlyNew": true, "maxItems": 20 }
Run it again and the next 20 Massachusetts universities arrive, without paying for the first 20 twice.
Output
A real row from a cloud run of 2026-10-08 (byYear mode, FY2023, employers: ["microsoft"]):
{"fiscalYear": 2023,"sourceRow": 18720,"employer": "MICROSOFT CORPORATION","matchedEmployer": "microsoft","taxIdLast4": "4442","naics": "51","naicsLabel": "Information: software publishing, telecoms, media","city": "REDMOND","state": "WA","stateName": "Washington","zip": "98052","initialApprovals": 39,"initialDenials": 0,"continuingApprovals": 1950,"continuingDenials": 45,"totalApprovals": 1989,"totalDenials": 45,"totalDecisions": 2034,"denialRate": 0.0221,"sourceMode": "byYear","url": "https://www.uscis.gov/sites/default/files/document/data/h1b_datahubexport-2023.csv","fetchedAt": "2026-10-08T20:02:55.704Z"}
| Field | Type | Meaning |
|---|---|---|
fiscalYear | integer | Fiscal year of the file the row comes from. Always present in byYear; absent in employerTotals. |
sourceRow | integer | The data line of the file this row was read from. A few lines repeat an employer, an address and the same counts exactly; this is what tells them apart. byYear only. |
employer | string | Petitioner name as filed, in capitals and often shortened. Empty on the rows where the file carries no name. |
matchedEmployer | string | The employers value that found this row; empty when no employer filter was used. |
taxIdLast4 | string | Last four digits of the petitioner's tax id, as published. Empty where the file leaves it empty. |
naics | string | Two-digit NAICS sector as the file records it; 99 = unknown, empty = no code in the file. |
naicsLabel | string | Plain name of the sector (computed by this actor). |
city, state, zip | string | Employer's mailing address as filed. state and zip are empty on some rows of the oldest files. |
stateName | string | Full name of the state code (computed by this actor). |
initialApprovals, initialDenials | integer | Workers first decided for initial employment. |
continuingApprovals, continuingDenials | integer | Workers first decided for continuing employment (extensions, employer changes, amended petitions). |
totalApprovals, totalDenials, totalDecisions | integer | The four published counts added up — computed by this actor, not published. Always present. |
denialRate | number or null | totalDenials / totalDecisions, four decimals — computed by this actor; null when the row holds no decision at all. |
yearCount, firstFiscalYear, lastFiscalYear, fiscalYearsCovered | integer / integer[] | employerTotals only: how many of the selected fiscal years carry decisions for this employer, and which. |
sourceMode | string | byYear or employerTotals. |
url | string | The fiscal-year file the row was read from. The published rows have no page of their own, so the file is the row's provenance. Always present. |
fetchedAt | string | When this run read the file, ISO 8601 UTC. Always present. |
Dataset views: H-1B employers (employer, place, industry name, the approval split, denials, rate), Decisions in detail (the four published counts plus the computed ones), Employer totals over the selected years.
Use it from code / agents
curl -X POST "https://api.apify.com/v2/acts/yadroo~uscis-h1b-employers/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"fiscalYears":["2023"],"states":["CA"],"minTotalApprovals":50,"sort":"approvalsDesc","maxItems":25}'
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('yadroo/uscis-h1b-employers').call({fiscalYears: ['2023'],employers: ['microsoft'],maxItems: 25,});const { items } = await client.dataset(run.defaultDatasetId).listItems();
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("yadroo/uscis-h1b-employers").call(run_input={"mode": "employerTotals","fiscalYears": ["2023", "2022", "2021"],"employers": ["amazon"],"maxItems": 20,})items = client.dataset(run["defaultDatasetId"]).list_items().items
MCP: add https://mcp.apify.com to Claude / Cursor / any MCP client and call the yadroo/uscis-h1b-employers tool
with the same JSON input. Two things help an agent: fields keeps the answer small (["employer","totalApprovals","denialRate"]),
and mode, fiscalYears, states, naics and sort are validated by the platform before the run starts, so a
wrong value comes back as an error with the allowed values instead of a wrong dataset.
Pricing
Pay per event: $0.001 per run start + $0.002 per dataset row. The start event is charged on every run, including a run that ends with no rows. Tier discounts apply to the row event (Bronze −10 %, Silver −20 %, Gold and above −30 %); the start event is flat.
Typical runs at the full price:
- a company lookup in one fiscal year, 5 rows:
0.001 + 5 × 0.002= $0.011; - a state shortlist, 25 rows:
0.001 + 25 × 0.002= $0.051; - the default 50 rows:
0.001 + 50 × 0.002= $0.101; - a five-year totals run, 20 rows:
0.001 + 20 × 0.002= $0.041 (reading five files costs time, not rows).
Every row written to the dataset is charged, and only rows: an employer filter that matched nothing writes no
marker row, so you pay the start event alone and the status message tells you what did not match. Reading a file
always costs the same few seconds whatever maxItems is, so filter first, sort second and keep maxItems small. The
run stops as soon as your Maximum cost per run is reached, keeps every row it already wrote and says so in its
status message ("Stopped at your spending limit: N rows delivered").
Limits & FAQ
FY2024 and later are not in here. Only fiscal years 2009 to 2023 are published as files. Newer years exist inside
the interactive data hub, whose pages and export buttons the site's robots.txt closes to automated clients, so this
actor does not read them and offers no workaround.
Speed and the site's pace. robots.txt of uscis.gov asks for a crawl delay of 10 seconds, and this actor keeps
it: one request per 10 seconds to the site, retries included. Everything else is fast. Measured in the Apify cloud on
2026-10-08: a one-fiscal-year run takes 4-7 seconds end to end (the FY2023 file is 2.1 MB with 33,332 published rows,
FY2022 3.9 MB with 59,983 rows, and each one downloads and parses in well under a second); a run over all 15 published
fiscal years read 15 files and wrote 913 rows in 145 seconds — about 6 files and 380 rows a minute, nearly all of it
waiting politely between requests. Nothing was throttled or blocked in that run. The largest input the schema allows
therefore fits comfortably into the 900 s run timeout, and a run that still would not fit stops by itself, keeps the
rows it has written and says so ("Stopped before the run timeout: N rows saved" — verified in the cloud with a 60 s
run timeout: 40 rows of 4 fiscal years kept).
What the numbers are and are not. They are first decisions on petitions — appeals, revocations, reopened cases
and pending petitions are not in them, so a case later won on appeal still counts as a denial here. They count
workers, not petitions in flight. The address is the employer's mailing address, not the worksite, so a
national employer filing from one headquarters appears in one state only. 99 means the file does not know the
industry; some rows of the oldest files have no state and no ZIP, and some rows of the newest files have no employer
name at all — those rows are real decisions and are kept, with empty values. Data-entry spellings in the source are
kept as filed (SAGINAW VLY ST UNIV), which is why the employer filter matches a part of the name. The files
themselves are not uniform — the newest ones name the columns "Initial Approval", the older ones "Initial Approvals",
and the quoting differs too; the actor reads both and stops with the header it saw if a column is ever missing.
Repeated lines and employer identity. A few lines of the published files repeat an employer, an address and the
same counts exactly; every published line becomes its own row and sourceRow tells them apart. In employerTotals
mode employers are grouped by their name (ignoring case and punctuation) plus taxIdLast4, so
AMAZON.COM SERVICES LLCAMAZON COM SERVICES LLC are one employer — but a changed legal form (… INC to … LLC) counts as two,
because the files give no company id beyond the name and the four digits. A totals run with no employer, state, city,
ZIP or industry filter is cut at 120,000 employers; the status message says so when it happens.
Not personal data. The rows carry an employer name plus the last four digits of a tax id — an employer identifier. No beneficiaries, no salaries, no contact details.
H-1B is not a green card. This is the record of H-1B petition decisions. It says nothing about permanent residence, PERM or the green-card lottery.
Salaries are elsewhere. Wages and job titles come from the Department of Labor's own disclosure data, a different dataset that is not in this actor.
Empty results. A filter that matched nothing is a successful run with no rows and a status message naming what
did not match (an employer value, a city) — never a silent empty dataset. With onlyNew a run that finds nothing new
says so and how many matching rows earlier runs already delivered. A bad value (a ZIP that is not digits, a
denial rate above 1) stops the run at once with a message saying what to change, and an off-list value in one of the
enum fields is refused by the platform before the run even starts.
Questions or problems: open an issue on the Issues tab of this actor.
Made by Yadroo. Sibling actors: federal-register-documents · sec-edgar-filings · data-gov-datasets · bls-release-calendar · himalayas-remote-jobs