- Job Update API workflow
- Job Update API references
- Authentication
- Update job posting
- Request – Update job posting
- Confirm your updates
- Partial updates
- Response – Update job posting
- Choose the query method to get job posting information
- Get job posting status by ID
- Authentication
- Single job query (node)
- Batch job query (nodes)
- Work with webhooks
- List job postings by criteria
- Clear job posting updates
- Request – Clear job posting updates
- Response – Clear job posting updates
- Job status
- Organic non-sponsored jobs
- Sponsored jobs
- Rejected jobs
- Rate limits
- Troubleshoot errors
- FAQs
Job Update API guide
Update, clear updates, get details for, and list 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 Update API workflow
Use the Job Update API to update jobs, clear updates, get job details, and list jobs on Indeed. These calls are free and do not count toward Sponsored Jobs API usage policy limits.
- 1.Review Before you start — Your client uses an ATS with Indeed Apply, and you have a sub-account. Under the Single-Source Feed Policy, the ATS is the source of truth for employer jobs.
- 2.Authentication
- 3.Update job posting — Change supported fields on Indeed and Indeed PLUS.
- 4.Choose the query method to get job posting information
- 5.Get job posting status by ID — If you have job IRIs, use the
nodeornodesquery. - 6.List job postings by criteria — For bulk retrieval, use
findEmployerJobsPartner. - 7.Clear job posting updates — Use
clearSourcedJobPostingUpdatesto remove updates. - 8.Check job status
- 9.Review rate limits
- 10.Troubleshoot errors
- 11.Read the FAQs — Find answers to common questions.
Job Update API references
clearSourcedJobPostingUpdates— Clear job updates.findEmployerJobsPartner— List jobs for an employer.node— Get one job by ID.nodes— Get multiple jobs by ID.updateSourcedJobPostings— Update supported job fields.
The ATS controls job creation and expiration.
Authentication
When you become an Indeed partner, Indeed creates an app for your integration. Sign in to Partner Console to view the app and your OAuth credentials: client ID, secret, and a 3-legged OAuth authorization code. Exchange those credentials for an access token to authenticate API calls.
For setup details, see Integrate with Indeed and call APIs.
The findEmployerJobsPartner, node, and nodes queries require one of these OAuth token types:
| Token type | Description |
|---|---|
| 2‑legged OAuth token that specifies an advertiser | If you already use the Sponsored Jobs API with the client credentials grant type (2-legged OAuth), you might already have this token type. |
| 3-legged OAuth token | If you already have a 3-legged OAuth token, you can use it. |
The access token must include these scopes:
employer_accessemployer.hosted_job
For more information about scopes, see Scopes.
After you get an access token, include it in the query. Indeed recommends refreshing access tokens before they expire so users do not need to sign in each time they view updated job statuses.
Update job posting
This feature is in beta and is not available in Japan. Contact Indeed for more information.
- 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:
Enables ad agencies to update job posting fields on Indeed and Indeed PLUS.
Update only jobs authorized by your client. Unauthorized updates can prevent the client from using other Indeed tools on that job. To add a location the ATS does not have, use your XML or API integration.
To update a job posting that you did not send to Indeed:
-
Choose clients to add: Contact Indeed.
-
Call
findEmployerJobsPartnerand look for jobs whereJobPostSurfaceStatus.isRejectedisfalse. Those jobs are searchable on Indeed. SaveEmployerJob.idfor the next step. If a job is rejected, checkstatusCommunicationfor the reason. -
Call
updateSourcedJobPostingswith the savedEmployerJob.idassourcedPostingId. You can update fields such as the tracking URL and job URL. For the full list, seeUpdateSourcedJobPostingMetadataInputandUpdateSourcedJobPostingBodyInput. After you update a field, later ATS changes to that field no longer appear to job seekers. -
Confirm your updates
Use one of these queries:
findEmployerJobsPartner: Call at most once per hour per access token. Updates usually appear within 30 seconds, but Indeed does not guarantee timing.node: Fetch one job byEmployerJob.id.nodes: Fetch multiple jobs byEmployerJob.idvalues in one call.
To list sponsored jobs for a campaign, set
campaignCategoriestoad_campaign_category:{category}. For example,ad_campaign_category:fooreturns sponsored jobs for thefoocampaign.
For rate limits on this call, see Rate limits.
Request – Update job posting
To update selected fields in a job posting, call the jobsIngest.updateSourcedJobPostings mutation.
This operation requires an access token that represents the advertiser.
The access token must include these scopes:
employer_accessemployer.hosted_job
See Get access token that represents employer. Instead of an employer ID, associate the token with an advertiser ID.
Provide only the fields that you want to update. To leave a field unchanged, omit it or set it to null.
This example calls the updateSourcedJobPostings mutation to update several fields in a job posting:
mutation UpdateSourcedJobPostings { jobsIngest { updateSourcedJobPostings( input: { updates: [ { sourcedPostingId: "<SOURCED POSTING ID OF JOB POSTING>" metadata: { url: "https://www.example.com/jobs/123" campaignCategories: ["springCampaign"] trackingUrl: "https://www.example.com/jobs/123?utm_source=indeed" } body: { title: "Software Developer" description: "Come build the future with us!" jobLocation: { general: { cityRegionPostal: "Phoenix, AZ 85003" streetAddress: "1234 Sunny Lane, Phoenix, AZ 85003" } } salary: { currency: "USD" maximumMinor: 1000 minimumMinor: 1000 period: "HOUR" } } } ] } ) { results { jobPosting { sourcedPostingId employerJobId } } } }}mutation UpdateSourcedJobPostings { jobsIngest { updateSourcedJobPostings(input: { updates: [{ sourcedPostingId: "<SOURCED POSTING ID OF JOB POSTING>" body: { description: "<h2>About the role</h2><p>Join our team as a software engineer. You design and build scalable backend services.</p><ul><li>Competitive salary</li><li>Remote-friendly</li><li>Health and dental benefits</li></ul>" descriptionFormatting: HTML } }] }) { results { jobPosting { sourcedPostingId employerJobId } } } }}updateSourcedJobPostings takes one argument: input, of type UpdateSourcedJobPostingsInput.
input has one field, updates, which is an array of UpdateSourcedJobPostingInput objects. Each object defines updates for one job posting.
Each UpdateSourcedJobPostingInput object supports these fields:
| Field | Type | Description |
|---|---|---|
sourcedPostingId | ID! | Required. For each job posting, provide one of these values:
|
metadata.url | WebUrl | The ad agency URL where a job seeker can apply if Indeed Apply is not available. |
metadata.campaignCategories | [String!] | Category tags that group related jobs for campaign selection. Query Sponsored Jobs API with ad_campaign_category:{category} for the values you provide. |
metadata.trackingUrl | WebUrl | The job tracking URL. When a job seeker views the job on Indeed, Indeed sends an HTTP request to this URL. |
body.title | String | The job title. |
body.description | String | The job description:
|
body.descriptionFormatting | DescriptionFormatting | The job description format. Set this field to |
body.jobLocation .general.cityRegionPostal | String! | Required if you update location. The city, administrative region such as state, county, or prefecture, and postal code for the job. If you set this field for a remote job, Indeed treats the job as a regional remote job. |
body.jobLocation .general.streetAddress | String | The street address of the job's primary location. Include the full address so Indeed can improve location matching and show the address to job seekers. |
body.salary.currency | CurrencyCode | The salary currency code in ISO 4217 format. |
body.salary.maximumMinor | Int64 | The maximum salary in local minor currency. For USD, |
body.salary.minimumMinor | Int64 | The minimum salary in local minor currency. For USD, |
body.salary.period | JobSalaryPeriod! | Required if you update salary. The rate used to calculate salary, such as hourly, daily, or weekly. |
body.companyName | String | Updates the company name for the job posting. To leave it unchanged, omit this field or set it to |
For more information, see the API reference.
Confirm your updates
The updateSourcedJobPostings response includes job IRIs. To verify changes, use the node query with the returned IRIs.
See Get job posting status by ID.
Partial updates
Field updates are not atomic. If you update multiple fields in one request, the API can rarely apply only some changes. When that happens, the response includes an error.
An error response does not always mean a partial update occurred. If a multi-field request returns an error, view the updated job posting to confirm which fields, if any, succeeded. For error-handling guidance, see Troubleshoot GraphQL errors.
Response – Update job posting
updateSourcedJobPostings returns an UpdateSourcedJobPostingsPayload object. It includes a results field, which is an array of UpdateSourcedJobPostingResult objects. Each object corresponds to one job posting update.
Each UpdateSourcedJobPostingResult object includes a jobPosting field of type SourcedJobPostingUpdate. If Indeed does not accept the update, this field is null. Otherwise, it includes:
sourcedPostingId: The job posting UUID. This matchesSourcedJobPosting.sourcedPostingId, which Indeed generated when it created the job posting. Use this value to expire or update the job posting.employerJobId: The Indeed Resource Identifier (IRI) for the job posting. This matchesEmployerJob.id.
Choose the query method to get job posting information
Use one of these methods to get job information:
-
Get job posting status by ID (
nodeornodesquery)Use this method when you already have the job IRI, which is the
EmployerJobID. It is faster thanfindEmployerJobsPartnerwhen you already know the IRI.Use cases:
- Confirm updates: Call
updateSourcedJobPostingsto get the job IRI, then usenodeto verify the change. - Webhook handling: After you receive a webhook, use
nodewith the job IRI from the payload to get the current job state.
- Confirm updates: Call
-
List job postings by criteria (
findEmployerJobsPartner)Use this search-based method to list all jobs. It works best for bulk inventory retrieval.
Use case:
- Inventory sync: When you do not have IRIs, call
findEmployerJobsPartnerto retrieve associated jobs in bulk. OnlylegacySourceIdandfeedTypefilters are available.
- Inventory sync: When you do not have IRIs, call
Get job posting status by ID
To get job status by IRI, which is the EmployerJob ID, call the GraphQL node or nodes query. These queries are more efficient than list job postings by criteria.
Authentication
Use the same authentication and authorization as findEmployerJobsPartner. See Authentication. The access token must include these scopes:
employer_accessemployer.hosted_job
Single job query (node)
Get one job by its IRI, which is the EmployerJob ID, with the Relay Node pattern:
query GetJob($id: ID!) { node(id: $id) { ...on EmployerJob { id jobData { title description company dateCreated datePostedOnIndeed jobLocation { city countryCode fullAddress } salary { max min period } externalPostingMetadata { jobPostingId jobRequisitionId } } managementUrls { viewJob } } }}Variables:
{ "id": "dXJuOmluZGVlZDplbXBsb3llcmpvYjphMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTA="}Job IRIs (EmployerJob IDs) are base64-encoded. The id field in the response is the encoded IRI.
See EmployerJob.
POST https://apis.indeed.com/graphqlAuthorization: Bearer YOUR_ACCESS_TOKENContent-Type: application/json
{ "query": "query GetJob($id: ID!) { node(id: $id) { ... on EmployerJob { id jobData { title description company } } } }", "variables": { "id": "dXJuOmluZGVlZDplbXBsb3llcmpvYjphMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTA=" }}Batch job query (nodes)
Get status for multiple jobs by their IRIs (EmployerJob IDs).
Use this query to confirm multiple updates or process batches of webhook events.
query GetMultipleJobs($ids: [ID!] !) { nodes(ids: $ids) { ... on EmployerJob { id jobData { title description company datePostedOnIndeed jobLocation { city countryCode } } } }}Variables:
{ "ids": [ "dXJuOmluZGVlZDplbXBsb3llcmpvYjphMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTA=", "dXJuOmluZGVlZDplbXBsb3llcmpvYjpiMmMzZDRlNS1mNmE3LTg5MDEtYmNkZS1mMjM0NTY3ODkwMTI=", "dXJuOmluZGVlZDplbXBsb3llcmpvYjpjM2Q0ZTVmNi1hN2I4LTkwMTItY2RlZi0zNDU2Nzg5MDEyMzQ=" ]}Job IRIs (EmployerJob IDs) are base64-encoded. The id fields in the response are the encoded IRIs.
See EmployerJob.
Work with webhooks
Webhook payloads include the job IRI:
{ "eventType": "job.updated", "jobIri": "dXJuOmluZGVlZDplbXBsb3llcmpvYjphMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTA=", "timestamp": "2025-12-10T15:30:00Z"}Use the node query with the IRI to get the current job status. For examples, see Get job posting status by ID.
Benefits:
- Real-time updates
- Fast IRI-based retrieval
- Pre-visibility enrichment window
List job postings by criteria
Contact Indeed to use this feature. Additional requirements apply.
Lists job postings for an employer.
This call is rate limited.
If you call this operation once per hour for each client, you might never hit the limit. However, HTTP 429 can still occur. If it does, reduce your request rate. See 429 error code.
For rate limits on this call, see Rate limits.
Request – List job postings by criteria
To list an employer's jobs, call findEmployerJobsPartner:
This example calls findEmployerJobsPartner to list job postings in descending order by the Indeed post date:
query FindEmployerJobsPartner { findEmployerJobsPartner(input: { filters: { legacySourceId: "60a9614a5d973a21", jobFeedType: ["INTEGRATED_FROM_PARTNER"] }, sort: [{ sortDirection: DESC, sortField: datePostedOnIndeed }] }, first: 10, before: null, after: null) { employerJobs { id jobData { title datePostedOnIndeed dateCreated description company jobLocation { countryCode city postalCode fullAddress } externalJobPageUrl externalPostingMetadata { jobPostingId jobRequisitionId campaignCategories trackingUrls rawInputLocation isIntegratedJob } } managementUrls { viewJob } seatsConnection { pageInfo { endCursor hasNextPage hasPreviousPage startCursor } seats { jobPost { id externalPartnerCallToAction(input: { locale: "en-us" }) { imageAltText imageUrl } status { globalStatus { isIndeedApplyActive } surfaceStatuses { isRejected isSponsorshipRequired isMissingRequiredSponsorship statusCommunication { messagingTagMatches { message } } } } } } } } estimatedTotalResultsCount pageInfo { endCursor hasNextPage hasPreviousPage startCursor } }}findEmployerJobsPartner finds jobs by source ID or lists an employer's jobs. It supports these input fields:
| Field | Description |
|---|---|
Filters jobs by feed type, which identifies where the job came from. Valid values are:
Specify one value, unless you pair Default: By default, | |
Filters jobs by the ATS requisition ID on the job ( Send the value exactly as that field returns it. Matching is case-insensitive and does not trim whitespace. A fragment such as Requisition IDs are not unique, so one filter can match more than one job. By default, | |
input.sort | An array of objects that defines how to sort job postings in the response. Each object contains these fields:
|
first | The number of job postings to return.
|
before | Returns items with a cursor value before this value. Use PageInfo in the response for pagination details. |
after | Returns items with a cursor value after this value. Use PageInfo in the response for pagination details. |
Response – List job postings by criteria
findEmployerJobsPartner lists jobs for the employer associated with the access token.
If you include filters or sorting in the request, the API applies them to the results.
If your token does not return the jobs you expect, ask the user to contact their Indeed support representative through their Indeed employer account page. The representative can help connect jobs on Indeed to the advertiser or user account, if appropriate.
The API returns a FindEmployerJobsPartnerConnection object with these fields:
| Field | Type | Description |
|---|---|---|
employerJobs | [EmployerJob]! | An array of job objects. For field details, see Response - View job posting. |
estimatedTotalResultsCount | Int! | The estimated total number of jobs in the response. |
pageInfo | PageInfo! | Pagination information. |
This example response lists job postings.
The API returns only one item in seats, so startCursor and endCursor are the same, and hasNextPage and hasPreviousPage are false.
{ "data": { "findEmployerJobsPartner": { "employerJobs": [{ "id": "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0VtcGxveWVySm9iLzkxZGU0ZjVhLWE1MWYtNGQ1Ni1iOWI0LWNhMDQzZWVjNDAzMQ==", "jobData": { "title": "Certified Nursing Assistant CNA", "externalPostingMetadata": { "jobPostingId": "CBA-Anytown-Posting-Id", "jobRequisitionId": "CBA-Anytown-Req-Id" } }, "seatsConnection": { "seats": [{ "jobPost": { "id": "aXJpOi8vYXBpcy5pbmRlZWQuY29tL0pvYlBvc3QvNDAxNTY1MmNhZDc2YTQxNQ==", "status": { "surfaceStatuses": [{ "isRejected": true, "isSponsorshipRequired": false, "isMissingRequiredSponsorship": false, "statusCommunication": { "messagingTagMatches": [{ "message": "This job does not meet Indeed's job posting standards. Review the job description and resubmit." }] } }] } } }] } }], "estimatedTotalResultsCount": 1, "pageInfo": { "endCursor": "MQ==", "hasNextPage": false, "hasPreviousPage": false, "startCursor": "MQ==" } } }}To troubleshoot validation errors, see Troubleshoot GraphQL errors. For more information, see Validation in the GraphQL documentation.
Clear job posting updates
This beta feature is not available in Japan. Contact Indeed for more information.
As an ad agency, use this operation to clear updates that you made to a client's job postings. The job returns to the latest ATS data, so the client can manage it again with other Indeed tools. For example, clear your updates when you stop working with a client.
-
Add clients and update their job postings.
-
Call
findEmployerJobsPartnerto confirm the updated values. SetlegacySourceIdto thesourcedPostingIdthat the update operation returned. -
Call
clearSourcedJobPostingUpdatesto clear the updated fields. For the request format, see Request – Clear job posting updates. -
Call
findEmployerJobsPartneragain with the samelegacySourceIdto confirm that the fields now show the ATS data.
For rate limits on this call, see Rate limits.
Request – Clear job posting updates
To clear fields that updateSourcedJobPostings previously changed, call the jobsIngest.clearSourcedJobPostingUpdates mutation.
This operation requires an access token for the advertiser that originally made the updates.
The access token must include these scopes:
employer_accessemployer.hosted_job
See Get access token that represents employer. Instead of an employer ID, associate the token with an advertiser ID.
This example calls the clearSourcedJobPostingUpdates mutation to clear updates from a job posting:
mutation ClearSourcedJobPostingUpdates { jobsIngest { clearSourcedJobPostingUpdates(input: { updates: [{ sourcedPostingId: "<SOURCED POSTING ID OF JOB POSTING>" }] }) { results { jobPosting { sourcedPostingId employerJobId } } } }}clearSourcedJobPostingUpdates takes one argument, input, of type ClearSourcedJobPostingUpdatesInput.
input has one field, updates, which is an array of ClearSourcedJobPostingUpdateInput objects. Each object identifies one job posting whose updates you want to clear.
To clear updates for multiple job postings in one request, include multiple ClearSourcedJobPostingUpdateInput objects.
Each ClearSourcedJobPostingUpdateInput object supports these fields:
| Field | Type | Description |
|---|---|---|
sourcedPostingId | ID! | Required. For each job posting, provide one of these values:
|
Response – Clear job posting updates
clearSourcedJobPostingUpdates returns a ClearSourcedJobPostingUpdatesPayload object. It includes a results field, which is an array of ClearSourcedJobPostingUpdateResult objects. Each object corresponds to one job posting whose updates were cleared.
Each ClearSourcedJobPostingUpdateResult object includes a jobPosting field of type SourcedJobPostingUpdate. If Indeed does not accept the request, this field is null. Otherwise, it includes:
sourcedPostingId: The job posting UUID. This matchesSourcedJobPosting.sourcedPostingId, which Indeed generated when it created the job posting. Use this value to expire or update the job posting.employerJobId: The Indeed Resource Identifier (IRI) for the job posting. This matchesEmployerJob.id.
Job status
Because feed policy has changed, most jobs you manage on Indeed no longer come from your feeds.
A job has one of these statuses:
- Organic:
"isRejected": falseand"isSponsorshipRequired": false. See Organic non-sponsored jobs. - Sponsored only with spend:
"isRejected": false,"isSponsorshipRequired": true, and"isMissingRequiredSponsorship": false. See Sponsored jobs. - Sponsored only without spend:
"isRejected": true,"isSponsorshipRequired": true, and"isMissingRequiredSponsorship": true. See Sponsored jobs. - Nowhere:
"isRejected": trueand"isSponsorshipRequired": false. See Rejected jobs.
Organic non-sponsored jobs
To get organic traffic data for jobs that are not sponsored, use one of these approaches:
- Add the
trackingUrlfield: You receive an HTTP request for each job click, for both organic and sponsored traffic, with or without Indeed Apply. SeetrackingUrlinUpdateSourcedJobPostingMetadataInput. - Use custom job URLs: Replace the job URL with a career page that you own, or work with ATS partners to append your tracking metadata. Job seekers go to this URL only if the job does not use Indeed Apply. See
urlinUpdateSourcedJobPostingMetadataInput. - Collaborate with client ATS: Work with your clients and their ATS to gather organic performance data.
- Have clients sign in to Indeed Analytics: Clients can view complete organic performance for all jobs, sponsored or not.
Sponsored jobs
To get sponsored-job traffic data, call Sponsored Jobs API v8 or later to get employer job IDs that match the IDs returned by the Job Update API.
Rejected jobs
When isRejected is true and isSponsorshipRequired is false, the job is not searchable on Indeed. Retrieve the status message to see why and what to fix:
-
Call
findEmployerJobsPartnerto list job postings by criteria. -
In the response, look for jobs where
JobPostSurfaceStatushasisRejectedset totrueandisSponsorshipRequiredset tofalse.Save
EmployerJob.idfor later API calls. -
Review
JobPostStatusMessagingTagMatch.messageinJobPostSurfaceStatus.statusCommunicationfor the rejection reason.
Rate limits
The Job Update API enforces rate limits on these operations:
findEmployerJobsPartnerto list job postings by criteriajobsIngest.updateSourcedJobPostingsto update job postingsjobsIngest.clearSourcedJobPostingUpdatesto clear job posting updates
These limits help keep the API available. Indeed sets them above normal daily Job Update API usage, but throttles large request volumes over short periods. To reduce the chance of throttling, spread requests over 10 minutes.
If you exceed a limit, the API returns HTTP 429, either in the HTTP response or in the GraphQL JSON errors array.
You do not need to take special action. If you need help working around these limits, request support.
If you exceed these rate limits, the API notifies you:
| Query or mutation | Rate limit |
|---|---|
findEmployerJobsPartner query | 5 requests per second |
| 20 requests per second total across both mutations |
See also:
Troubleshoot errors
- Troubleshoot Job Update API errors — Common Job Update API errors and how to resolve them.
- Troubleshoot OAuth errors — Troubleshoot OAuth errors that can occur before you access GraphQL.
- Troubleshoot GraphQL errors.
FAQs
Job visibility
If a job uses Indeed Apply, EmployerJob.seatConnections.jobPost.isIndeedApplyActive returns true. Partners can retrieve this field with findEmployerJobsPartner.