- Job Sync API workflow
- Job Sync API references
- Authentication
- Create job posting
- Request – Create job posting
- Response – Create job posting
- Create job posting with screener questions
- Request – Create job posting with screener questions
- Response – Create job posting with screener questions
- Get job posting status by ID
- Authentication for get job posting status by ID
- Request – Get job posting status by ID
- Response – Get job posting status by ID
- Job status
- List job postings by IDs
- Request – List job postings by IDs
- Response – List job postings by IDs
- Upsert job posting
- Request – Upsert job posting
- Response – Upsert job posting
- Expire job posting
- Request – Expire job posting
- Response – Expire job posting
- Rate limits
- Troubleshoot errors
Job Sync API guide
Create and manage job postings on Indeed.
By using this API and its documentation and building an integration, you agree to the Additional API Terms and Guidelines.
Job Sync API workflow
You can call the Job Sync API to create and manage job postings on Indeed.
After you integrate with the Job Sync API, you can call these operations:
- 1.Authenticate.
- 2.Create job posting without screener questions.
- 3.Create job posting with screener questions.
- 4.Get job posting status by ID.
- 5.List job postings by IDs.
- 6.Upsert job posting if it exists. If not, create one.
- 7.Expire job posting.
- 8.Rate limits.
- 9.Troubleshoot GraphQL errors - Resolve GraphQL errors.
- 10.FAQs.
Job Sync API references
jobsIngest.createSourcedJobPostings- Creates, upserts, or reactivates job postings on Indeed.node- Get job posting status by ID.nodes- List job postings by IDs.jobsIngest.expireSourcedJobsBySourcedPostingId- Expires a job posting.
Authentication
When you become an Indeed partner, Indeed sets up an app for your integration. Sign in to Partner Console to view your app and OAuth credentials (client ID, secret, and authorization code for 3-legged OAuth). Exchange credentials for an access token to authenticate API calls.
See Integrate with Indeed and call APIs.
Calls to the Job Sync API do not incur costs and are excluded from the costs and limits in the Sponsored Jobs API usage policy.
Create job posting
Creates a job posting on Indeed.
To create a job posting with screener questions, see Create job posting with screener questions.
When you create a job posting on Indeed, Indeed might require different fields by country to comply with local regulations or with Indeed policies. Technically, job postings that you create in the USA can behave differently from those that you create, for example, in Japan. Create your job postings by country, following the local regulations and Indeed policies for that country. The API reference describes any country-specific regulations.
For jobs in Japan, see Job posting guidelines and examples for Japan.
For information about rate limits on this call, see Rate limits.
Request – Create job posting
To create a job posting, call the jobsIngest.createSourcedJobPostings mutation.
In the jobPostings field in the jobsIngest.createSourcedJobPostings request, provide a job title, description, location, benefits for the job, and details about the job source, including the source name.
Define these fields in CreateSourcedJobPostingInput:
body field
The required body field of type SourcedJobPostingBodyInput contains the job description. Set this field in the input object:
| Field | Type | Description |
|---|---|---|
description | String | Required. Job description. Includes working hours, salary, qualifications, holidays, and other information. Specify the job description in HTML format. For more information, see Job description formatting. To format HTML in descriptions in job postings, see Job description formatting. |
metadata field
The required metadata field of type SourcedJobPostingMetadataInput! contains information about the job posting. Set these fields in the input object:
| Field | Type | Description |
|---|---|---|
jobPostingId | String | Required. ID for the job in your ATS. Some requirements are:
|
sourceName | String | Required. The So, if an employer has multiple job groups that separate people manage, choose a unique You cannot change a job's |
contactEmail | EmailAddress! | Required. The contact email address in RFC 822 format. |
contactPhone | PhoneNumber | Required. The phone number for the client's contact in E.164 format. If the phone number is not in E.164 format, a validation error occurs. |
trackingUrl | WebUrl | note This feature is not available in Japan. Optional. A URL that is triggered when a job is viewed, enabling external analytics for job views. |
applyMethod field
The applyMethod field of type SourcedJobPostingApplyMethodInput defines how to apply to the job. Required to create a job posting with Indeed Apply screener questions, otherwise optional. For screener question fields, see Screener questions fields.
-
jobRequisitionIdis optional in the schema, but Indeed requires it when it differs fromjobPostingId.For Japanese job postings: Set it to the same value as the ID or
jobPostingId. -
Hiring manager and recruiter values in
SourcedJobPostingJobContactInputare optional in the schema, but Indeed requires them when available.
See also:
The API strips any leading or trailing spaces from the SourcedJobPostingJobSourceInput.sourceName and SourcedJobPostingMetadataInput.jobPostingId fields.
For information about other request fields, see createSourcedJobPostings.
Creates a job posting:
mutation { jobsIngest { createSourcedJobPostings(input: { jobPostings: [{ body: { title: "title 1" description: "description 1" location: { country: "US" cityRegionPostal: "Syracuse, New York 13209" } benefits: [] } metadata: { jobSource: { companyName: "Company" sourceName: "Source" sourceType: "Employer" } jobPostingId: "JobId1" datePublished: "2023-01-02T12:00Z" url: "http://example.com/careers/job1.html" contacts: [{ contactType: ["contact", "recruiter"] contactInfo: { contactEmail: "songdatadrop2@gmail.com" contactPhone: "+10001112223" contactName: "SL1" } }], trackingUrl: "https://www.example.com/" } }] }) { results { jobPosting { sourcedPostingId } } } }}mutation CreateJobPostingExample { jobsIngest { createSourcedJobPostings(input: { jobPostings: [{ body: { title: "Customer Support" description: "<h2 data-segment-type=\"header\" data-segment-label=\"WorkHours\">Working hours\n</h2><div data-segment-type=\"content\" data-segment-label=\"WorkHours\">8 working hours per day\n 5 working days per week</div>" descriptionFormatting: RICH_FORMATTING benefits: [] salary: { currency: "JPY" period: "HOUR" minimumMinor: 2000 fineGrainedSalaryInformation: { totalSalaryMinor: 2000 workingHours: 1 fixedOvertimePay: false } } hasProbationaryPeriod: NO location: { country: "JP" streetAddress: "108-0023 東京都港区芝浦3-1-21 田町ステーションタワーS18階" } } metadata: { jobSource: { companyName: "Sample Company" sourceName: "Sample Company" sourceType: "Employer" employerIds: [{ type: "YOUR EMPLOYER TYPE PROVIDED BY INDEED" id: "EmployerId1" }] } taxonomyClassification: { jobTypes: ["75GKK"] occupations: ["N3YNG"] attributes: ["7ADHN"] } jobPostingId: "YourJobId1" datePublished: "2023-01-01T12:34:56+09:00" url: "https://example.com/jobs/aaabbbccc" contacts: { contactType: "contact", contactInfo: { contactEmail: "contact@career.example.com" } }, trackingUrl: "https://www.example.com/" } applyMethod: { indeedApply: { postUrl: "https://example.com/applypost" apiToken: "API TOKEN HERE" } } }] }) { results { jobPosting { sourcedPostingId } } } }}Response – Create job posting
Successful response: Indeed generates a unique sourcedPostingId for each job posting.
{ "data": { "jobsIngest": { "createSourcedJobPostings": { "results": [ { "jobPosting": { "sourcedPostingId": "e29aaba8-2cef-447f-a1e0-bcb7ec3fa730", "employerJobId": "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0VtcGxveWVySm9iL2UyOWFhYmE4LTJjZWYtNDQ3Zi1hMWUwLWJjYjdlYzNmYTczMA==" } } ] } } }}The value in sourcedPostingId depends on whether the API accepts or rejects the posting:
-
The API accepts a complete job posting. The
sourcedPostingIdvalue is the Indeed employer job ID, which you use to expire or update the job. Pending spam and fraud detection, Indeed indexes and makes the job available on Indeed in minutes to hours.noteIf you create a job through either the
createSourcedJobPostingsmutation or an XML job feed, expiration of the job requires an action on your part. You must explicitly expire that job.Even if someone calls the
updateSourcedJobPostingsmutation or clicks Edit on the Indeed job details page for this job, you must explicitly expire that job.See also:
-
The API rejects the job posting if it has missing or malformed data, which causes an error at call time. The
sourcedPostingIdvalue isnull, and the API returns an error in the standard GraphQLerrorsarray.
To troubleshoot validation errors, See Troubleshoot GraphQL errors. For more information about validation errors, see Validation in the GraphQL docs.
Use the sourcedPostingId value to expire a job posting.
Create job posting with screener questions
After you integrate with the Job Sync API, you can create a job posting with screener questions.
Screener questions enable employers to quickly determine whether an applicant meets their criteria. Adding screener questions to jobs enables employers to focus on connecting with rather than vetting candidates. Indeed does not host screener questions on your behalf. If your system supports screener questions, you must implement them to have Indeed approve your integration.
Request – Create job posting with screener questions
To create a job posting with Indeed Apply screener questions, call the jobsIngest.createSourcedJobPostings mutation. In the jobPostings field in the jobsIngest.createSourcedJobPostings request, provide a job title, description, location, benefits for the job, and details about the job source, including the source name. When you create a job posting on Indeed, Indeed might require different fields by country to comply with local regulations or with Indeed policies. Technically, job postings that you create in the USA can behave differently from those that you create, for example, in Japan. Create your job postings by country, following the local regulations and Indeed policies for that country. The API reference describes any country-specific regulations. See the createSourcedJobPostings mutation fields table for required fields.
To surface screener questions in the Indeed Apply application flow, you must also specify the CreateSourcedJobPostingInput.applyMethod field.
Use one of these methods to surface screener questions in the Indeed Apply application flow:
- Method 1
Format questions in JSON, then
POSTthe questions to an HTTPS URL.For each job, define the URL in the Job Sync API
SourcedJobPostingIndeedApplyInput.applyQuestionsfield.- Method 2
Define the screener questions schema in the Job Sync API
SourcedJobPostingIndeedApplyInput.applyQuestionsDetailsfield.See Considerations.
The applyMethod field of type SourcedJobPostingApplyMethodInput defines how to apply to the job. Required to create a job posting with Indeed Apply screener questions, otherwise optional.
This table describes the applyQuestions field and the applyQuestionsDetails field and their related fields:
| Field | Type | Description |
|---|---|---|
applyQuestions | URI | Required to create a job posting with screener questions by using method 1. URL that returns a JSON-formatted string of questions to ask during the Indeed Apply application process. |
applyQuestionsDetails | IndeedApplyQuestionsDetailsInput | Required to create a job posting with screener questions by using method 2. Screener questions schema for Indeed Apply application process. |
questions | IndeedApplyScreenerQuestionsDefinitionInput | Screener questionnaire definition for the Indeed Apply application process, aligned to the V1.x standard. |
screenerQuestions | [ScreenerQuestionBodyInput!]! | General, well-formatted questions that pertain to the underlying job, such as work experience, skills, or certifications. Do not mix demographic questions about ethnicity, gender, or disability in this list. Instead place demographic questions in the |
demographicQuestions | [DemographicQuestionsBodyInput!]! | Specific, well-formatted questions that pertain to the job applicant's protected class, such as ethnicity, gender, or disability. Certain jurisdictions regulate these sorts of questions. Indeed systems and potentially your systems must handle these questions in special ways. Demographic questions are limited to U.S.-based jobs for EEO compliance. Do not mix general screener questions, such as those pertaining to work experience, skills, or certifications, in this list. Instead place screener questions in the |
Method 1 example: Create Indeed Apply screener questions using applyQuestions:
mutation CreateSourcedJobPostings($companyName: String!$sourceName: String!$indeedApplyToken: ID!) { jobsIngest { createSourcedJobPostings(input: { jobPostings: [{ body: { title: "Software Engineer - All SQ and SQ types" description: "We are looking for a talented software engineer" descriptionFormatting: TEXT location: { latitude: 43.0729 longitude: -76.2161 country: "US" streetAddress: "123 Main St" cityRegionPostal: "Syracuse, New York 13209" } benefits: ["Health Insurance", "401k"] } metadata: { jobSource: { companyName: $companyName sourceName: $sourceName sourceType: "Employer" } jobPostingId: "JOB-12345" datePublished: "2023-01-01T12:00:00Z" url: "https://example.com/jobs/12345" contacts: [{ contactType: ["contact"] contactInfo: { contactEmail: "jobs@example.com" contactName: "HR Department" } }], visibility: { hideFromIndeedSearch: { reason: "OPT_OUT" } } trackingUrl: "https://www.example.com/" } applyMethod: { indeedApply: { postUrl: "https://example.com/apply" apiToken: $indeedApplyToken, resumeRequired: NO, applyQuestions: "https://your-webserver.com/job/questions/jb12345" } } }] }) { results { jobPosting { sourcedPostingId employerJobId } } } }}Method 2 example: Create Indeed Apply screener questions using applyQuestionsDetails:
mutation CreateSourcedJobPostings($companyName: String!$sourceName: String!$indeedApplyToken: ID!) { jobsIngest { createSourcedJobPostings(input: { jobPostings: [{ body: { title: "Software Engineer - All SQ and SQ types" description: "We are looking for a talented software engineer" descriptionFormatting: TEXT location: { latitude: 43.0729 longitude: -76.2161 country: "US" streetAddress: "123 Main St" cityRegionPostal: "Syracuse, New York 13209" } benefits: ["Health Insurance", "401k"] } metadata: { jobSource: { companyName: $companyName sourceName: $sourceName sourceType: "Employer" } jobPostingId: "JOB-12345" datePublished: "2023-01-01T12:00:00Z" url: "https://example.com/jobs/12345" contacts: [{ contactType: ["contact"] contactInfo: { contactEmail: "jobs@example.com" contactName: "HR Department" } }], visibility: { hideFromIndeedSearch: { reason: "OPT_OUT" } } trackingUrl: "https://www.example.com/" } applyMethod: { indeedApply: { postUrl: "https://example.com/apply" apiToken: $indeedApplyToken, resumeRequired: NO, applyQuestionsDetails: { questions: { screenerQuestions: [{ text: { integer: { questionInput: { id: "int1" question: "How many years of experience do you have?" minValue: 0 maxValue: 5 required: true, } } } } { text: { decimal: { questionInput: { id: "dec1" question: "What is your current hourly rate?" minValue: 10.0 maxValue: 120.0 required: false } qualification: { type: NON_BLOCKING range: { minValue: 15.0, maxValue: 100.0 } } } } } { text: { freeform: { id: "free1" question: "Briefly describe your relevant experience" maxCharCount: 500 required: true } } } { textarea: { id: "ta1" question: "Tell us about your most challenging project" maxCharCount: 2000 required: true } } { select: { questionInput: { id: "sel1" question: "What programming language are you most proficient in?" options: [{ value: "java", label: "Java" } { value: "python", label: "Python" } { value: "javascript", label: "JavaScript" } { value: "csharp", label: "C#" }] required: true } qualification: { type: NON_BLOCKING match: { values: ["java", "python"] } } } } { multiselect: { questionInput: { id: "multi1" question: "Which of the following technologies have you worked with?" options: [{ value: "react", label: "React" } { value: "angular", label: "Angular" } { value: "vue", label: "Vue" } { value: "svelte", label: "Svelte" }] required: true } qualification: { type: NON_BLOCKING match: { type: ANY, values: ["react", "angular"] } } } } { date: { questionInput: { id: "date1" question: "When can you start?" format: "MM-dd-yyyy" minDate: "2025-02-01T00:00:00Z" maxDate: "2025-12-31T00:00:00Z" required: true } qualification: { type: NON_BLOCKING range: { minValue: "2025-02-15T00:00:00Z" maxValue: "2025-12-31T00:00:00Z" } } } } { file: { id: "file1" question: "Please upload a sample of your work" format: ["pdf", "doc", "docx"] required: true, min: 1 } } { information: { id: "info1" text: "<p>Please note that all candidates must complete a coding assessment as part of the interview process.</p>" } } { pageBreak: { id: "page1" } } { hierarchical: { id: "hier1" question: "Select your location" options: [{ value: "us", label: "United States" } { value: "ca", label: "Canada" } { value: "uk", label: "United Kingdom" }] hierarchicalOptions: [{ id: "states" condition: { id: "hier1", values: ["us"] } options: [{ value: "ny", label: "New York" }, { value: "ca", label: "California" }] } { id: "provinces" condition: { id: "hier1", values: ["ca"] } options: [{ value: "on", label: "Ontario" }, { value: "bc", label: "British Columbia" }] }] required: true } }] demographicQuestions: [{ textarea: { id: "demo_ta1" question: "Please describe any accommodations you may need" maxCharCount: 1000 required: false } } { text: { freeform: { id: "demo_free1" question: "How did you hear about this position?" maxCharCount: 200 required: false } } } { text: { integer: { id: "demo_int1" question: "How many dependents do you have?" minValue: 0 maxValue: 20 required: false } } } { text: { decimal: { id: "demo_dec1" question: "What is your desired salary?" minValue: 30000.0 maxValue: 200000.0 required: false } } } { text: { numeric: { id: "demo_num1" question: "What is your zip code?" required: false } } } { select: { id: "demo_sel1" question: "What is your gender?" options: [{ value: "male", label: "Male" } { value: "female", label: "Female" } { value: "non-binary", label: "Non-binary" } { value: "prefer-not", label: "Prefer not to say" }] required: false } } { multiselect: { id: "demo_multi1" question: "Which of the following describe your ethnicity? (Select all that apply)" options: [{ value: "hispanic", label: "Hispanic or Latino" } { value: "white", label: "White" } { value: "black", label: "Black or African American" } { value: "asian", label: "Asian" } { value: "native" label: "Native American or Alaska Native" } { value: "pacific" label: "Native Hawaiian or Pacific Islander" } { value: "prefer-not", label: "Prefer not to say" }] required: false } } { date: { id: "demo_date1" question: "What is your date of birth?" format: "MM/dd/yyyy" required: false } } { information: { id: "demo_info1" text: "<p>The following questions are for Equal Employment Opportunity purposes only and will not affect your application status.</p>" } } { pageBreak: { id: "demo_page1" } }] } } } } }] }) { results { jobPosting { sourcedPostingId employerJobId } } } }}Response – Create job posting with screener questions
For the example response, see Response - Create job posting.
Get job posting status by ID
Get the status for a job posting, by ID.
Authentication for get job posting status by ID
The get job posting status by ID method requires one of these OAuth token types:
| Token type | Description |
|---|---|
| 3-legged OAuth token | If the user is not signed in or their access token has expired, display a Log in with Indeed button in place of the job status. After the user signs in, get a 3-legged OAuth token. Register a redirect URL to the ATS job list. |
| 2‑legged OAuth token that specifies an advertiser | Use this token instead of requiring users to sign in. Ad agencies using the Sponsored Jobs API with 2-legged OAuth already have this token type. |
The OAuth access token must have these scopes:
employer_accessemployer.hosted_job
For more information about adding scopes to OAuth tokens, see Scopes.
Securely store access tokens in your ATS, and don't share them between users. Indeed trusts that your system is the source of truth for a job; if you use a user's access token for a job that another user posted, Indeed might grant that user access to the other user's job on Indeed.
After you get an access token, include this token in the query. Indeed recommends that you refresh your access tokens before they expire to avoid asking users to sign in every time they need to view updated job statuses.
Request – Get job posting status by ID
To get the status for a job posting, by ID, call the node query with an OAuth token.
node takes id (employer job ID in Base64-encoded IRI format). Use the employerJobId returned from createSourcedJobPostings.
query { node(id: "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0VtcGxveWVySm9iLzkxZGU0ZjVhLWE1MWYtNGQ1Ni1iOWI0LWNhMDQzZWVjNDAzMQ==" ) { ...on EmployerJob { id jobData { title datePostedOnIndeed dateCreated description company jobLocation { countryCode city postalCode fullAddress } externalJobPageUrl externalPostingMetadata { jobPostingId jobRequisitionId campaignCategories trackingUrls isIntegratedJob } } managementUrls { viewJob } seatsConnection { pageInfo { endCursor hasNextPage hasPreviousPage startCursor } seats { jobPost { id externalPartnerCallToAction(input: { locale: "en-us" }) { imageAltText imageUrl } status { globalStatus { lifecycleStatus isIndeedApplyActive } surfaceStatuses { isRejected isSponsorshipRequired isMissingRequiredSponsorship } } } } } } }}Response – Get job posting status by ID
node returns the EmployerJob object, which implements the Node interface, with job data for the specified employer job ID.
One result returned: cursors are identical, no next/previous pages.
{ "data": { "node": { "id": "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0VtcGxveWVySm9iLzkxZGU0ZjVhLWE1MWYtNGQ1Ni1iOWI0LWNhMDQzZWVjNDAzMQ==", "jobData": { "title": "Certified Nursing Assistant CNA", "datePostedOnIndeed": "2022-03-22T20:33:28Z", "dateCreated": "2022-03-22T20:33:28Z", "description": "Anytown Health and Rehabilitation Center in Anytown, USA is a 186-bed center offering a variety of individualized, health care services for our patients and residents. We are seeking a qualified and committed team member to join our team.", "company": "Anytown Health and Rehabilitation Center", "jobLocation": { "countryCode": "US", "city": "Cambridge", "postalCode": null, "fullAddress": null }, "externalJobPageUrl": "http://www.indeed.com/job/certified-nursing-assistant-cna-9354ed892ad3a1b6", "externalPostingMetadata": { "jobPostingId": "CBA-Anytown-Posting-Id", "jobRequisitionId": "CBA-Anytown-Req-Id", "campaignCategories": [], "trackingUrls": [], "isIntegratedJob": false } }, "managementUrls": { "viewJob": "https://employers.indeed.com/jobs/view?employerJobId=aXJpOi8vYXBpcy5pbmRlZWQuY29tL0VtcGxveWVySm9iLzkxZGU0ZjVhLWE1MWYtNGQ1Ni1iOWI0LWNhMDQzZWVjNDAzMQ==" }, "seatsConnection": { "pageInfo": { "endCursor": "YVhKcE9pOHZZWEJwY3k1cGJtUmxaV1F1WTI5dEwwcHZZbEJ2YzNRdk5EQXhOVFkxTW1OaFpEYzJZVFF4TlE9PQ==", "hasNextPage": false, "hasPreviousPage": false, "startCursor": "YVhKcE9pOHZZWEJwY3k1cGJtUmxaV1F1WTI5dEwwcHZZbEJ2YzNRdk5EQXhOVFkxTW1OaFpEYzJZVFF4TlE9PQ==" }, "seats": [ { "jobPost": { "id": "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0pvYlBvc3QvNDAxNTY1MmNhZDc2YTQxNQ==", "externalPartnerCallToAction": [ { "imageUrl": "https://dlogqfjusi9uq.cloudfront.net/cta/en_US/not_searchable.svg", "imageAltText": "Update needed on Indeed" } ], "status": { "globalStatus": [ { "lifecycleStatus": "INACTIVE", "isIndeedApplyActive": false } ], "surfaceStatuses": [ { "isRejected": false, "isSponsorshipRequired": false, "isMissingRequiredSponsorship": false } ] } } } ] } } }}EmployerJob contains these fields:
| Field | Description |
|---|---|
idType: ID! | Employer Job ID (EJID), an encoded version of sourcedPostingId in the Indeed Resource Identifier (IRI) format. Use this value to expire a job posting, update a job posting, or upsert a job posting. |
jobDataType: EmployerJobData | Data associated with the job posting.
|
managementUrls.viewJobType: WebUrl! | URL for the employer's job detail view on Indeed. On this page, your users can see details about their job posting, including moderation information, and see how it is displayed to job candidates. |
seatsConnectionType: EmployerJobSeatsConnection | Paginated list of job seats. A job seat contains location, schedule, and compensation details. A job can have zero or more seats, and each seat maps to a job posting. |
| |
Whether Indeed Apply is enabled for the job. |
Job status
Show the Indeed job status with a link to Indeed's job management page. When users click the image, they access additional job information on Indeed.
-
To get the call to action image, call the
nodequery'sexternalPartnerCallToActionsubquery underseats.jobPost. Specify a supported locale.noteThe
localesupports fallback logic. If you requestfr_CH, and that is not supported, thelocalefalls back to the more generalfr.If an appropriate match does not exist, default is
en_US.The
externalPartnerCallToActionquery returns animageUrlfor one of these job status images:Job status Image Description The job is live on Indeed.
The job is available in search results.
The job is expired on Indeed.
The job is not available in search results.
The job needs sponsorship on Indeed.
The job is not available in search results.
The status of the job is unknown on Indeed.
Either the job is not found on Indeed or the user account does not have access to this job.
Note:
The API does not return the
not_found.svgimage because the call-to-action images are part of the job data, so if the job is not found, the image cannot be returned. However, the ATS can use this image if desired.An update is needed on Indeed to make the job searchable.
-
To get the URL for the job detail page for their job, query
managementUrls.viewJob. -
Embed the call-to-action image as a clickable link on the job detail page.
-
Update the job posting status when a user visits the page.
See Frequently asked questions > Job visibility.
Example ATS job status displays:
Job details page (signed in to Indeed)

Job list page (signed in to Indeed)

List job postings by IDs
View details for multiple job postings. For authentication, see Authentication for get job posting status by ID.
Request – List job postings by IDs
nodes accepts multiple IDs. Takes ids (array of employer job IDs in IRI format).
query { nodes(ids: [ "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0VtcGxveWVySm9iLzkxZGU0ZjVhLWE1MWYtNGQ1Ni1iOWI0LWNhMDQzZWVjNDAzMQ==" ]) { ...on EmployerJob { id jobData { title datePostedOnIndeed dateCreated description company jobLocation { countryCode city postalCode fullAddress } externalJobPageUrl externalPostingMetadata { jobPostingId jobRequisitionId campaignCategories trackingUrls isIntegratedJob } } managementUrls { viewJob } seatsConnection { pageInfo { endCursor hasNextPage hasPreviousPage startCursor } seats { jobPost { id externalPartnerCallToAction(input: { locale: "en-us" }) { imageAltText imageUrl } status { globalStatus { lifecycleStatus isIndeedApplyActive } surfaceStatuses { isRejected isSponsorshipRequired isMissingRequiredSponsorship } } } } } } }}Response – List job postings by IDs
Returns EmployerJob array. See Response – Get job posting status by ID table.
{ "data": { "nodes": [{ "id": "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0VtcGxveWVySm9iLzkxZGU0ZjVhLWE1MWYtNGQ1Ni1iOWI0LWNhMDQzZWVjNDAzMQ==", "jobData": { "title": "Certified Nursing Assistant CNA", "datePostedOnIndeed": "2022-03-22T20:33:28Z", "dateCreated": "2022-03-22T20:33:28Z", "description": "Anytown Health and Rehabilitation Center in Anytown, USA is a 186-bed center offering a variety of individualized, health care services for our patients and residents. We are seeking a qualified and committed team member to join our team.", "company": "Anytown Health and Rehabilitation Center", "jobLocation": { "countryCode": "US", "city": "Cambridge", "postalCode": null, "fullAddress": null }, "externalJobPageUrl": "http://www.indeed.com/job/certified-nursing-assistant-cna-9354ed892ad3a1b6", "externalPostingMetadata": { "jobPostingId": "CBA-Anytown-Posting-Id", "jobRequisitionId": "CBA-Anytown-Req-Id", "campaignCategories": [], "trackingUrls": [], "isIntegratedJob": false } }, "managementUrls": { "viewJob": "https://employers.indeed.com/jobs/view?employerJobId=aXJpOi8vYXBpcy5pbmRlZWQuY29tL0VtcGxveWVySm9iLzkxZGU0ZjVhLWE1MWYtNGQ1Ni1iOWI0LWNhMDQzZWVjNDAzMQ==" }, "seatsConnection": { "pageInfo": { "endCursor": "YVhKcE9pOHZZWEJwY3k1cGJtUmxaV1F1WTI5dEwwcHZZbEJ2YzNRdk5EQXhOVFkxTW1OaFpEYzJZVFF4TlE9PQ==", "hasNextPage": false, "hasPreviousPage": false, "startCursor": "YVhKcE9pOHZZWEJwY3k1cGJtUmxaV1F1WTI5dEwwcHZZbEJ2YzNRdk5EQXhOVFkxTW1OaFpEYzJZVFF4TlE9PQ==" }, "seats": [{ "jobPost": { "id": "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0pvYlBvc3QvNDAxNTY1MmNhZDc2YTQxNQ==", "externalPartnerCallToAction": [{ "imageUrl": "https://dlogqfjusi9uq.cloudfront.net/cta/en_US/not_searchable.svg", "imageAltText": "Update needed on Indeed" }], "status": { "globalStatus": [{ "lifecycleStatus": "INACTIVE", "isIndeedApplyActive": false }], "surfaceStatuses": [{ "isRejected": false, "isSponsorshipRequired": false, "isMissingRequiredSponsorship": false }] } } }] } }, { "id": "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0VtcGxveWVySm9iLzkxZGU0ZjVhLWE1MWYtNGQ1Ni1iOWI0LWNhMDQzZWVjNDAzMZ==", ... }] }}If your users access your ATS application to get Indeed job information, you can show the status of the Indeed job with a link to Indeed's job management page. For information about job status, see Job status.
Upsert job posting
- If you submitted the job posting to Indeed, upsert it.
- If another partner submitted it, update the posting. Updates are primarily for ad agencies.
Japan only: All partners except ad agencies and Indeed PLUS Publisher Network partners can update the posting.
See also:
Upserts a job posting on both Indeed and the Indeed PLUS publishing network.
The upsert operation updates a job posting if it exists, and if not, creates one.
Provide all required job fields even if you are not changing those values.
For information about rate limits on this call, see Rate limits.
Request – Upsert job posting
To upsert a job posting, call the jobsIngest.createSourcedJobPostings mutation, just like you would to create a job posting.
Upsert job posting example
This example upserts a US-based job posting for a single-feed client. Note that it is the same example as the one in Create job posting, but it updates the title and description fields.
Provide the same values for the SourcedJobPostingMetadataInput.jobPostingId and SourcedJobPostingJobSourceInput.sourceName fields and OAuth client ID that you used when you created the job posting.
Provide all required fields even if you are not changing them. The API returns the same unique ID, sourcedPostingId, that Indeed generated when you created the job posting.
mutation { jobsIngest { createSourcedJobPostings(input: { jobPostings: [{ body: { title: "Updated job title" description: "Updated job description" location: { country: "US" cityRegionPostal: "Syracuse, New York 13209" } benefits: [] } metadata: { jobSource: { companyName: "Company" sourceName: "Source" sourceType: "Employer" } jobPostingId: "JobId1" datePublished: "2023-01-02T12:00Z" url: "http://example.com/careers/job1.html" contacts: [{ contactType: ["contact", "recruiter"] contactInfo: { contactEmail: "songdatadrop2@gmail.com" contactPhone: "+10001112223" contactName: "SL1" } }] } }] }) { results { jobPosting { sourcedPostingId } } } }}Response – Upsert job posting
The mutation returns the same sourcedPostingId value that Indeed generated for the job posting when it was created. This value is the Indeed employer job ID, which you use to expire or update the job.
Expire job posting
Removes a job posting from Indeed and the Indeed PLUS publishing network.
Indeed does not automatically expire job postings that were posted through the Job Sync API.
If you remove a job posting from your career page, you must explicitly call the Job Sync API to expire the job posting.
Indeed never automatically expires jobs that you submit through the Job Sync API.
The applicant tracking system (ATS) controls the creation and expiration of job postings. When you remove a job posting from your career page, you must explicitly call the Job Sync API to expire that job posting.
If you create a job through either the createSourcedJobPostings mutation or an XML job feed, expiration of the job requires an action on your part. You must explicitly expire that job.
Even if someone calls the updateSourcedJobPostings mutation or clicks Edit on the Indeed job details page for this job, you must explicitly expire that job.
See also:
To reactivate an expired job, call jobsIngest.createSourcedJobPostings with the jobPostingId and sourceName values from the expired job, just as if you were updating an active job.
To define the date that the job was reactivated in your ATS, update the datePublished field value.
You can reactivate a job for 30 days after it has expired.
After 30 days, Indeed might archive statistics and configuration related to the job. If you reactivate a job after 30 days, the API will likely return a new sourcedPostingId value. Ensure that your code can handle either the same, or a new, sourcedPostingId value.
Make sure you always save the sourcedPostingId value. You need this value to either expire jobs or reactivate expired jobs.
Request – Expire job posting
To expire a job posting, call the jobsIngest.expireSourcedJobsBySourcedPostingId mutation with the SourcedJobPosting.sourcedPostingId. Set this field to the Indeed employer job ID value from the job posting.
Expire job posting example
This example expires jobs for a single-feed client:
mutation { jobsIngest { expireSourcedJobsBySourcedPostingId(input: { jobs: [{ sourcedPostingId: "123" }, { sourcedPostingId: "456" }] }) { results { trackingKey } } }}Response – Expire job posting
Indeed does not verify whether the job exists before responding. The response is always ACCEPTED.
Response shows the tracking ID:
{ "data": { "jobsIngest": { "expireSourcedJobsBySourcedPostingId": { "results": [ { "trackingKey": "1h958ob2601bv800" } ] } } }}For each sourcedPostingId that you specify in the jobsIngest.expireSourcedJobsBySourcedPostingId mutation, the API returns the ExpireSourcedJobResult object, which contains:
| Item | Description |
|---|---|
| Tracking ID from Indeed. |
| Union that includes unique job ID. |
The API response can include error responses, such as invalid requests or backend errors, for each failed job expiration attempt. You can log the error response on your system so that you can tell what type of failure happens on Indeed. The API returns an error in the standard GraphQL errors array. For information about validation errors, see Validation in the GraphQL docs.
Rate limits
The Job Sync API enforces rate limits on calls to the jobsIngest.createSourcedJobPostings mutation to create job postings and upsert job postings.
Rate limits do not affect calls to the jobsIngest.expireSourcedJobsBySourcedPostingId.
See jobsIngest.expireSourcedJobsBySourcedPostingId mutation to expire job postings.
These rate limits align with best practices that ensure API availability. Indeed has configured these limits to be above normal everyday usage of the Job Sync API, but large volumes of requests over short durations are throttled and require retrying. To avoid reaching these rate limits, spread your API requests over 10 minutes.
No action is needed. If you exceed the rate limit, you receive the HTTP 429 status code, either at the HTTP level or in the errors array in the JSON response to the GraphQL request.
You are notified if you exceed these rate limits:
| Request size | Rate limit |
|---|---|
| Small requests that contain 1 job |
|
| Medium requests that contain from 2 to 10 jobs |
|
Large requests that contain more than 10 jobs |
|
See also:
Troubleshoot errors
For Job Sync API-specific errors, see Troubleshoot errors.
To troubleshoot OAuth errors that can occur before you access GraphQL, see Troubleshoot OAuth errors.
For common issues that cause Indeed to block jobs from being posted, see FAQs.