- API overview
- Key features
- Available datasets
- Integration steps
- 1. Get an API key
- 2. Authenticate your requests
- 3. Test your integration
- 4. Handle authentication errors
- API steps
- 1. Get job postings data
- 2. Get remote work data
- 3. Get wage growth data
- 4. Get AI job postings data
- 5. List available data coverage
- 6. Paginate results
- 7. Troubleshoot errors
- Hiring Lab API FAQs
Hiring Lab API
Access Indeed Hiring Lab labor market data for analysis and research.
By using this API and its documentation and building an integration, you agree to Indeed's terms of service (opens in a new window).
API overview
#Use the Hiring Lab API to get the labor market data that Indeed Hiring Lab publishes at data.indeed.com (opens in a new window). The data includes job posting trends, wage growth, remote work patterns, and AI-related jobs.
Researchers, policymakers, economists, and analysts use the API to get reliable labor market data. The level of detail and how often the data updates depend on the dataset.
Key features
#- Comprehensive coverage — Get data for multiple countries and regions.
- Multiple granularities — Get national, regional, and sectoral data.
- Regular updates — Get daily or monthly updates, depending on the dataset.
- Cursor-based pagination — Page through large datasets.
- Public and private data — Your authentication determines which data you can access.
Available datasets
#The Hiring Lab API provides these datasets:
| Dataset | Description | Granularity | Frequency | |||
|---|---|---|---|---|---|---|
| National | Regional | Sectoral | Daily | Monthly | ||
| Job Postings | Job postings indices, with and without seasonal adjustment | ✔ | ✔ | ✔ | ✔ | |
| Remote Work | Remote and hybrid job postings and searches | ✔ | ✔ | ✔ | ||
| Wage Growth | Year-over-year wage growth from job postings | ✔ | ✔ | ✔ | ||
| AI Jobs | AI-related job postings, including generative AI | ✔ | ✔ | |||
Integration steps
#To integrate the Hiring Lab API, get an API key, add it to your requests, and test it.
1. Get an API key
#Indeed issues API keys to partners and researchers who need Hiring Lab data.
To request an API key:
- 1.Email hiring-lab-api@indeed.com with your use case and organization details.
- 2.Indeed reviews your request and creates an API key.
- 3.Indeed emails you the API key.
Store your API key securely. Don't put it in client-side code or commit it to version control.
2. Authenticate your requests
#Include your API key in the Indeed-API-Key header of every GraphQL request:
curl -X POST 'https://apis.indeed.com/graphql' \ -H 'Content-Type: application/json' \ -H 'Indeed-API-Key: YOUR_API_KEY' \ --data-raw '{ "query": "query { findHiringLabPostingsPublic(first: 10, input: { filter: { country: [\"US\"] } }) { edges { node { ... on HiringLabNationalPosting { date indexSa } } } } }" }'Replace YOUR_API_KEY with your API key.
Add a Referer header
#Include a Referer header for requests from public IPs so that security filters don't block your requests. The header identifies the domain that calls the API:
curl -X POST 'https://apis.indeed.com/graphql' \ -H 'Content-Type: application/json' \ -H 'Indeed-API-Key: YOUR_API_KEY' \ -H 'Referer: https://your-domain.com' \ --data-raw '{ "query": "query { findHiringLabPostingsPublic(first: 10, input: { filter: { country: [\"US\"] } }) { edges { node { ... on HiringLabNationalPosting { date indexSa } } } } }" }'Replace https://your-domain.com with your domain or service identifier.
3. Test your integration
#Test your API key with a simple query:
curl -X POST 'https://apis.indeed.com/graphql' \ -H 'Content-Type: application/json' \ -H 'Indeed-API-Key: YOUR_API_KEY' \ -H 'Referer: https://your-domain.com' \ --data-raw '{ "query": "query { findHiringLabPostingsPublic(first: 5, input: { filter: { country: [\"US\"], postingType: TOTAL } }) { edges { node { ... on HiringLabNationalPosting { id date country { countryCode countryName } postingType indexSa indexNsa } } cursor } pageInfo { hasNextPage } } }" }'A successful response looks like this:
{ "data": { "findHiringLabPostingsPublic": { "edges": [{ "node": { "id": "...", "date": "2024-10-01", "country": { "countryCode": "US", "countryName": "United States" }, "postingType": "TOTAL", "indexSa": 102.5, "indexNsa": 103.2 }, "cursor": "..." }], "pageInfo": { "hasNextPage": true } } }}4. Handle authentication errors
#If your API key is missing or invalid, Indeed returns a 401 Unauthorized error.
If your request from a public IP address doesn't include a Referer header, security filters might block it.
If your query calls an API other than the Hiring Lab API, Indeed returns a FORBIDDEN error:
{ "errors": [{ "message": "The client doesn't have access to any requested field from '[API NAME]'.", "extensions": { "code": "FORBIDDEN" } }], "data": { "[value]": null }}Make sure that your query uses only Hiring Lab API queries: findHiringLabPostingsPublic, findHiringLabRemotePublic, findHiringLabWagesPublic, findHiringLabAiPublic, and related metadata queries.
API steps
#1. Get job postings data
#To get job postings data, call the findHiringLabPostingsPublic query:
query { findHiringLabPostingsPublic(first: 50, input: { filter: { country: ["US"], postingType: TOTAL } }) { edges { node { ...on HiringLabNationalPosting { id date country { countryCode countryName } postingType indexSa indexNsa } } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } }}Regional job postings data
#To get regional data, filter the same query by geography and region:
query { findHiringLabPostingsPublic(input: { filter: { country: ["US"], geography: STATES, region: ["California", "Texas"] } }) { edges { node { ...on HiringLabRegionalPosting { date country { countryCode countryName } region { regionCode regionName } geography { geography } indexSa indexNsa } } } }}Sectoral job postings data
#To get data by sector, filter by sector:
query { findHiringLabPostingsPublic(input: { filter: { country: ["US"], sector: ["Technology", "Healthcare"] } }) { edges { node { ...on HiringLabSectoralPosting { date country { countryCode countryName } sector { sectorCode sectorName } indexSa indexNsa } } } }}2. Get remote work data
#To get remote work trends, call the findHiringLabRemotePublic query:
query { findHiringLabRemotePublic(input: { filter: { country: ["US"], metric: POSTINGS } }) { edges { node { ...on HiringLabNationalRemote { date country { countryCode countryName } metric remoteShare } } } }}The remoteShare field returns the percentage of remote or hybrid job postings or searches. To choose one, set metric to POSTINGS or SEARCHES.
3. Get wage growth data
#To get wage growth data, call the findHiringLabWagesPublic query:
query { findHiringLabWagesPublic(input: { filter: { country: ["US"] } }) { edges { node { ...on HiringLabNationalWage { date country { countryCode countryName } wageGrowthYoy wageGrowth3Ma } } } }}The response includes:
wageGrowthYoy: Year-over-year change in posted wages.wageGrowth3Ma: 3-month lagged moving average of the year-over-year change.
4. Get AI job postings data
#To get AI-related job trends, call the findHiringLabAiPublic query:
query { findHiringLabAiPublic(input: { filter: { country: ["US"] } }) { edges { node { ...on HiringLabNationalAI { date country { countryCode countryName } aiShare } } } }}The response includes aiShare, the percentage of job postings that are AI-related.
5. List available data coverage
#To find out which data is available, call these queries.
List countries with data coverage
#query { findHiringLabCountriesPublic(input: { filter: { datasetId: "job_postings", granularity: NATIONAL } }) { results { countryCode countryName } }}List available sectors
#query { findHiringLabSectorsPublic(input: { filter: { datasetId: "job_postings", country: "US" } }) { results { sectorCode sectorName } }}List available geographies
#query { findHiringLabGeographiesPublic(input: { filter: { datasetId: "job_postings", country: "US" } }) { results { geography } }}List available regions
#query { findHiringLabRegionsPublic(input: { filter: { datasetId: "job_postings", country: "US", geography: STATES } }) { results { regionCode regionName } }}List all datasets
#query { allHiringLabDatasetsPublic { results { id name description granularity frequency } }}6. Paginate results
#The Hiring Lab data queries use cursor-based pagination. Set first to the number of results per page, up to 15000. To get the next page, set after to pageInfo.endCursor from the previous response. Coverage queries and allHiringLabDatasetsPublic return their results without first or after:
query { findHiringLabPostingsPublic(first: 50, after: "cursor_value_from_previous_response", input: { filter: { country: ["US"] } }) { edges { node { #...your fields } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } }}7. Troubleshoot errors
#For GraphQL errors, see Troubleshoot GraphQL errors.
For Hiring Lab API terms, see Hiring Lab API glossary.
Hiring Lab API FAQs
#Every job postings index uses February 1, 2020 as the baseline (= 100). This baseline keeps comparisons consistent across countries and time periods.
Seasonally adjusted (SA) indices remove typical seasonal patterns to reveal underlying trends. Non-seasonally adjusted (NSA) indices show raw data, including normal seasonal fluctuations such as holiday hiring.
- Indeed updates job postings and remote work data daily.
- Indeed updates wage growth data monthly.
- Indeed updates AI job postings data daily.
Coverage varies by dataset. Use the findHiringLabCountriesPublic query to see the available countries for each dataset and granularity level.
To get historical data, use cursor-based pagination. Start with your filters, then use the after parameter and the cursor values from previous responses to page through the results.
The maximum is 50 results per request. Use pagination to retrieve larger datasets.
The API returns data in chronological order. Use pagination and the date field in your application logic to filter to your desired time range.