- How to detect errors
- FORBIDDEN error
- BAD_USER_INPUT error
- Missing required fields
- Invalid datetime
- Invalid country code
- Invalid currency code
- Invalid phone number
- String length exceeded
- GRAPHQL_PARSE_FAILED error
- HTTP 429 Too Many Requests
- Feed type errors
- All jobs on this feed must have seats
- No jobs on this feed may have seats
- Contact validation errors
- No contacts found with type "contact"
- Multiple contacts with type "contact" found
- Salary validation errors
- Maximum amount cannot be less than minimum amount
- Duplicate entry errors
- The request contains duplicate job entries
- The request contains duplicate seat entries
- Multiseat validation errors
- All seats must be located in the same country
- Location validation errors
- cityRegionPostal cannot be null when remote type is not "Fully Remote"
- Job lifecycle issues
- Created job does not appear on Indeed
- Job won't reactivate
- Jobs don't auto-expire
- Retry strategy
- Retryable errors
- Best practices
- Get support
- See also
- Guides
- Specifications
- GraphQL API reference
Troubleshoot Job Sync API errors
Common Job Sync API errors and how to fix them.
How to detect errors
#The Job Sync API returns errors in these ways:
- HTTP status codes: Only for rate limiting (HTTP
429) and network failures. - GraphQL
errorsarray: Most errors return HTTP200, with error details in the response body.
Always check the errors array in the response body, even when the status is HTTP 200:
{ "data": null, "errors": [{ "message": "Error description", "extensions": { "code": "BAD_USER_INPUT", "classification": "ValidationError" } }]}The extensions.code field gives the error type, such as FORBIDDEN, BAD_USER_INPUT, GRAPHQL_PARSE_FAILED, or SUBREQUEST_HTTP_ERROR.
FORBIDDEN error
#message | Client is not authorized |
|---|---|
code | FORBIDDEN |
| Summary | The access token is missing, invalid, expired, or lacks the required permissions. |
A FORBIDDEN error with the Client is not authorized message means your OAuth client isn't authorized to use the Job Sync API:
{ "data": null, "errors": [{ "extensions": { "code": "FORBIDDEN" }, "message": "Client is not authorized." }]}To fix this error:
- 1.Check the
Authorizationheader format:Authorization: Bearer <YOUR_ACCESS_TOKEN>. - 2.Confirm that the token hasn't expired. Tokens are valid for 1 hour.
- 3.Confirm that your OAuth application has Job Sync API permissions.
- 4.Confirm that
sourceNamematches your registered feed. - 5.If the error continues, ask your Indeed representative to check your API access.
BAD_USER_INPUT error
#message | Exception while fetching data |
|---|---|
code | BAD_USER_INPUT |
| Summary | The request contains invalid data. |
This BAD_USER_INPUT example has an invalid phone number:
{ "errors": [{ "extensions": { "code": "BAD_USER_INPUT", "classification": "ValidationError" }, "message": "Validation error (WrongType@[jobsIngest/createSourcedJobPostings]) : argument 'input.jobPostings[0].metadata.contacts[0].contactInfo.contactPhone' with value 'StringValue{value='5551234567'}' is not a valid 'PhoneNumber' - String value `5551234567` is not a valid E.164 phone number" }]}Common causes of BAD_USER_INPUT errors:
Missing required fields
#The error message names the missing field. Required fields include:
| Section | Required fields |
|---|---|
body | title, description, benefits (can be empty array), location.country |
metadata | jobPostingId, jobSource.sourceName, datePublished, contacts (at least one) |
Invalid datetime
#DateTime values must use RFC 3339 (opens in a new window) format and include a time zone:
| Format | Valid? |
|---|---|
2024-01-15T10:30:00-05:00 | Yes |
2024-01-15T15:30:00Z | Yes |
2024-01-15 | No (missing time) |
01/15/2024 10:30 | No (wrong format) |
Invalid country code
#Country codes must use ISO-3166-1 alpha-2 (opens in a new window) format (2 uppercase letters):
| Format | Valid? |
|---|---|
US | Yes |
USA | No (use 2-letter code) |
us | No (must be uppercase) |
Invalid currency code
#Currency codes must use ISO-4217 format (3 uppercase letters):
| Format | Valid? |
|---|---|
USD | Yes |
$ | No (use ISO code) |
usd | No (must be uppercase) |
Invalid phone number
#Phone numbers must use E.164 (opens in a new window) format:
| Format | Valid? |
|---|---|
+15125551234 | Yes |
512-555-1234 | No (missing country code; remove dashes) |
(512) 555-1234 | No (remove formatting characters) |
String length exceeded
#| Field | Maximum length |
|---|---|
title | 255 characters |
description | 65,000 bytes |
seatPostingId | 250 characters |
sourceName | 250 characters |
GRAPHQL_PARSE_FAILED error
#message | Syntax Error: Invalid number, expected digit but got: "a". |
|---|---|
code | GRAPHQL_PARSE_FAILED |
| Summary | The query or mutation has a syntax error. This error often comes from quotes or backslashes in string values that are not escaped. |
Example:
{ "errors": [{ "message": "Syntax Error: Invalid number, expected digit but got: \"a\".", "locations": [{ "line": 7, "column": 33 }], "extensions": { "code": "GRAPHQL_PARSE_FAILED" } }]}Common causes:
- Unescaped special characters, such as quotes or backslashes, in string values
- Malformed GraphQL syntax
- Invalid JSON in variables
With descriptionFormatting: RICH_FORMATTING, the description field accepts raw HTML, so you don't need to escape HTML tags.
HTTP 429 Too Many Requests
#You exceeded your rate limit. This is one of the few errors that returns a status code other than HTTP 200.
To fix this error:
- Before you retry, wait the number of seconds in the
Retry-Afterresponse header. - Spread requests over time instead of sending large batches.
- Send one job per request, in real time, instead of in hourly batches.
Feed type errors
#All jobs on this feed must have seats
#Your feed is a multiseat feed, but the job has no seats.
Add a seats array with at least one seat:
body: { title: "Software Engineer" location: { country: "US" } seats: [{ seatPostingId: "SEAT-001" seatLocation: { country: "US" cityRegionPostal: "Dallas, TX 75201" } }]}No jobs on this feed may have seats
#Your feed is a standard feed, but the job includes seats.
Remove the seats array, and set the location at the job level:
body: { title: "Software Engineer" location: { country: "US" cityRegionPostal: "Austin, TX 78701" streetAddress: "100 Congress Ave" }}If you're not sure which feed type you have, submit a job without seats first.
Contact validation errors
#No contacts found with type "contact"
#No contact in the request has "contact" in contactType.
Include exactly one contact with "contact" in its contactType array:
contacts: [{ contactType: ["contact", "hiring manager"] contactInfo: { email: "hr@example.com" }}]Multiple contacts with type "contact" found
#More than one contact has contact in its contactType array. Only one contact can have this type. For other contacts, use types such as recruiter or hiring manager.
Salary validation errors
#Maximum amount cannot be less than minimum amount
#maximumMinor is less than minimumMinor.
Set maximumMinor to a value greater than or equal to minimumMinor. For a minimum-only salary, omit maximumMinor:
salary: { currency: "USD" minimumMinor: 5000000 # $50000 maximumMinor: 7500000 # $75000 period: "YEAR"}Salary values use minor currency units, such as cents for USD. For example, $50,000 is 5000000, not 50000.
Duplicate entry errors
#The request contains duplicate job entries
#The same sourceName and jobPostingId pair appears more than once in the request. Each job needs a unique pair.
The request contains duplicate seat entries
#The same sourceName and seatPostingId pair appears more than once in the request. Each seat in the request needs a unique sourceName and seatPostingId pair.
Multiseat validation errors
#All seats must be located in the same country
#A seat location uses a different country from the job's top-level location.
Every seat must use the same country code as the top-level location:
body: { location: { country: "US" } seats: [{ seatPostingId: "SEAT-001" seatLocation: { country: "US" # Must match top - level cityRegionPostal: "Dallas, TX 75201" } }, { seatPostingId: "SEAT-002" seatLocation: { country: "US" # Must match top - level cityRegionPostal: "Austin, TX 78701" } }]}Location validation errors
#cityRegionPostal cannot be null when remote type is not "Fully Remote"
#Jobs that aren't fully remote require cityRegionPostal.
Provide cityRegionPostal, or set the job as fully remote:
# Option 1: Provide locationlocation: { country: "US" cityRegionPostal: "Austin, TX 78701"}# Option 2: Set as fully remotelocation: { country: "US"}taxonomyClassification: { remoteType: "Fully Remote"}Job lifecycle issues
#Created job does not appear on Indeed
#Possible causes:
- The job is still processing, which can take 1 to 2 hours.
- Indeed rejected the job during processing.
- The
sourceNameis incorrect.
To fix this issue:
- 1.Wait 1 to 2 hours for jobs to become searchable.
- 2.Query the job status by
employerJobId. - 3.Check the
lifecycleStatusandisRejectedfields. - 4.Verify that
sourceNamematches your registered feed.
Job won't reactivate
#You can reactivate a job only within 30 days of expiration.
-
Within 30 days: Call
createSourcedJobPostingswith the originaljobPostingIdandsourceName, and updatedatePublished. -
After 30 days: Create the job as a new posting. Indeed might assign a new
sourcedPostingId.
Jobs don't auto-expire
#Jobs don't expire automatically. To expire a job, call expireSourcedJobsBySourcedPostingId.
Retry strategy
#The Job Sync API uses an upsert pattern, so retries are safe. If you submit a job with the same sourceName and jobPostingId, Indeed updates the job instead of creating a duplicate.
Retryable errors
#| Error | Retryable? | Strategy |
|---|---|---|
| HTTP 429 Too Many Requests | Yes | Wait the time in the Retry-After header |
| Network timeout | Yes | Exponential backoff |
| Connection refused | Yes | Exponential backoff |
FORBIDDEN (expired token) | Yes | Refresh token, then retry once |
BAD_USER_INPUT | No | Fix the request |
GRAPHQL_PARSE_FAILED | No | Fix the query syntax |
Best practices
#- Use exponential backoff with jitter for retries.
- Start with a 1-second delay and double it on each retry.
- Add 0 to 1 second of random jitter so that clients don't all retry at the same time.
- Limit retries to 3 to 5 attempts.
Get support
#If this guide doesn't cover your issue:
- 1.Read the GraphQL error
messageandextensionsobject. - 2.Confirm that all required fields are present and correctly formatted.
- 3.Test with the minimum input, then add fields one at a time.
- 4.Contact your Indeed representative for feed-specific issues.
See also
#Guides
#Specifications
#- RFC 3339: Date and Time on the Internet: Timestamps (opens in a new window) — The
DateTimeformat. - ISO 3166-1 alpha-2 (opens in a new window) — Two-letter country codes.
- E.164 (opens in a new window) — The international phone number format.
GraphQL API reference
#createSourcedJobPostings— Submits a job posting to Indeed.expireSourcedJobsBySourcedPostingId— Expires a job posting bysourcedPostingId.