- Overview
- Integration steps
- 1. Get access
- 2. Choose an integration option
- Option 1: Apply with Indeed JavaScript
- 1. Load the JavaScript
- 2. Enable the Apply with Indeed button
- 3. Specify parameters
- 4. Choose callback methods
- 5. Handle attribute encryption
- 6. Choose button design
- Option 2: applyUrlForEmployers mutation
- 1. Create Apply with Indeed URL
- 2. Integrate button design with mutation
- 3. Change the button language
- 4. Troubleshoot errors
- See also
- Guides
- Reference
Apply with Indeed
Add an Apply with Indeed button to job pages for partner ATSs and employers.
- By using this API and its documentation and building an integration, you agree to the Additional API Terms and Guidelines.
Overview
#Apply with Indeed adds an Apply with Indeed button to jobs that you post through the Job Sync API or an XML feed. Job seekers select the button on an employer's career page to apply with their saved Indeed profile.
Integration steps
#1. Get access
#-
Post the job through the Job Sync API or an XML feed, and enable Indeed Apply for it.
See the Job Sync API guide or the XML feed guide.
-
Request an Apply with Indeed integration through the request form (opens in a new window).
-
In Partner Console (opens in a new window), find your Apply with Indeed credentials:
- OAuth client ID and secret: Exchange these for an access token to call the GraphQL API.
- Apply With Indeed Token and secret: Use these for the Apply with Indeed button, where the token is the
partnerApiTokenvalue. This token is different from the Indeed Apply API token that you use to post jobs.
2. Choose an integration option
#To add the Apply with Indeed button to ATS pages, use one of these methods:
- Option 1: Apply with Indeed JavaScript: Add a script to your page, and it handles the button design and errors. Use this method for the fastest setup, if you can easily change your front-end code.
- Option 2: applyUrlForEmployers mutation: Get an apply form URL from your back end and place it anywhere on your page. This method is more flexible, but you build the button and handle errors yourself.
The button's parameters differ for jobs posted through the Job Sync API and jobs posted through an XML feed. Check which one you use before you start.
Option 1: Apply with Indeed JavaScript
#Load Indeed's script on your ATS job pages, and it adds and manages the Apply with Indeed button.
1. Load the JavaScript
#To load Apply with Indeed JavaScript on an ATS job page, add this <script> tag to the page. If your page encoding needs it, you can also include a <meta charset> tag.
<!DOCTYPE html><html> <head> ... <!-- Load the Apply with Indeed JavaScript --> <script src="https://apply.indeed.com/indeedapply/static/scripts/app/awi-bootstrap.js"></script> ... </head>...</html>2. Enable the Apply with Indeed button
#After you load the script, add an HTML element with the required attributes. Use the sample that matches how you post jobs.
Job Sync API code sample
#<!DOCTYPE html><html> <head> ... <!-- Step 1: Load the script --> <script src="https://apply.indeed.com/indeedapply/static/scripts/app/awi-bootstrap.js"></script> ... </head> <body> ... <!-- Step 2: Specify where to display the **Apply with Indeed** button --> <div data-indeed-apply-widget-type="<WIDGET-TYPE>" data-indeed-apply-sourceJobPostingId="<JobId>" data-indeed-apply-partnerApiToken="<PartnerApiToken>" data-indeed-apply-encryptedJobUrl="<Encrypted Job URL>" data-indeed-apply-encryptedContinueUrl="<ContinueURL>" data-indeed-apply-hl="en" data-indeed-apply-co="US" data-indeed-apply-newTab="true" ></div> ... </body></html>XML feed code sample
#<body> ... <!-- Step 2: Specify where to display the **Apply with Indeed** button --> <div data-indeed-apply-widget-type="<WIDGET-TYPE>" data-indeed-apply-encryptedSourceName="<SourceName>" data-indeed-apply-encryptedFeedUrl="<FeedUrl>" data-indeed-apply-encryptedReferenceId="<ReferenceId or ReferenceNumber>" data-indeed-apply-partnerApiToken="<PartnerApiToken>" data-indeed-apply-encryptedJobUrl="<Encrypted Job URL>" data-indeed-apply-encryptedContinueUrl="<ContinueURL>" data-indeed-apply-hl="en" data-indeed-apply-co="US" data-indeed-apply-newTab="true" ></div> ... </body>3. Specify parameters
#In the code samples, replace the values in angle brackets (<>) with your own. The data-indeed-apply-* attributes are:
| Attribute | Required | Encrypted | Description | Example |
|---|---|---|---|---|
Type: String | Required | No | The button type. The only supported value is AWI. | AWI |
Type: String | Required for Job Sync API | No | The employer job ID that the Job Sync API returns when you create a job posting. The Job Sync API guide calls it sourcePostingId. See Response – Create job posting. | 57caad99-6441-46ec-913c-e018e5013689 |
Type: String | Required for XML feed | Yes | The encrypted source name from the XML feed: the parent organization that's hiring for the role. | 1d76ab187297cf4c284e2621c2c7462fbfb216704ed0fdc325de0baf6dca5718 |
Type: String | Required for XML feed | Yes | The XML feed URL. | 0906256350d8ed657d9f70ffe91e84d4a117328ab5d42c22ccbac1689af2d1cb |
Type: String | Required for XML feed | Yes | The encrypted reference ID of the XML feed job. This ID uniquely identifies the job instance. | 1d76ab187297cf4c284e2621c2c7462fbfb216704ed0fdc325de0baf6dca5718 |
Type: String | Required | No | Your partner API token. | 98922e8fa1355c944b0f7efc6ae9b182ebb1f3ed8ce0b36ea40b33a9b73e2211 |
Type: String | Required | Yes | The encrypted URL of the public job description page that shows the Apply with Indeed button. | 0906256350d8ed657d9f70ffe91e84d4a117328ab5d42c22ccbac1689af2d1cb |
Type: String | Optional | Yes | The encrypted URL that applicants go to when they select Continue after they submit the application. | 0906256350d8ed657d9f70ffe91e84d4a117328ab5d42c22ccbac1689af2d1cb |
Type: String | Optional | Yes | The encrypted URL that applicants go to when they select the Exit link to leave the Indeed Apply form before they submit. | 0906256350d8ed657d9f70ffe91e84d4a117328ab5d42c22ccbac1689af2d1cb |
Type: String | Optional | No | The language of the apply form. | en |
Type: String | Optional | No | The applicant's country code. | US |
Type: String | Optional | No | The name of the JavaScript method that runs when an applicant selects Apply with Indeed. | _onClick |
Type: String | Optional | No | The name of the JavaScript method that runs after the Apply with Indeed button loads successfully. | _onButtonReady |
Type: Boolean | Optional | No | Stops the button CSS from loading, so that you can style the button yourself. | true |
Type: Boolean | Optional | No | Whether the application form opens in a new tab. | false |
Encrypt the attributes marked Yes in the Encrypted column. See Handle attribute encryption.
4. Choose callback methods
#To run your own code when the button is selected or loads, set data-indeed-apply-onClick or data-indeed-apply-onReady to the name of a JavaScript method:
-
onClicktakes one argument: the buttonHTMLElement. -
onReadytakes two arguments: the buttonHTMLElementandbuttonLoadStatus. ThebuttonLoadStatusobject has these fields:success: Whether the button loaded.message: The success or error message.error_code: The error code, if the button failed to load.
5. Handle attribute encryption
#Encrypt every attribute marked Yes in the Encrypted column of the parameters table. Use AES in CBC mode with PKCS5 padding and an initialization vector of 16 zero bytes. The key is the first 16 bytes (128 bits) of the secret associated with your partnerApiToken.
To encrypt an attribute:
- Read the plain-text value as UTF-8 bytes.
- Encrypt the bytes.
- Convert the encrypted bytes to a hex string.
- Use the hex string as the
data-indeed-apply-{attribute-name}value, instead of the plain-text value. Indeed Apply recognizes it as encrypted.
This sample encrypts and decrypts data:
import java.nio.charset.Charset;import java.lang.RuntimeException;import javax.crypto.Cipher;import javax.crypto.spec.*;import javax.crypto.spec.SecretKeySpec;public class EncryptionTest { public static void main(String[] args) { String url = "YOUR_URL"; String apiSecret = "YOUR_API_SECRET"; String encrypted_url = encrypt(url, apiSecret); String decrypted_url = decrypt(encrypted_url, apiSecret); System.out.println(encrypted_url); System.out.println(decrypted_url); } static String encrypt(String message, String apiSecret) { try { byte[] keyBytes = apiSecret.getBytes(Charset.forName("UTF-8")); byte[] message_bytes = message.getBytes("UTF-8"); SecretKeySpec key = new SecretKeySpec(keyBytes, 0, 16, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivspec = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); cipher.init(Cipher.ENCRYPT_MODE, key, ivspec); byte[] email_encrypted = cipher.doFinal(message_bytes); return bytesToHexString(email_encrypted); } catch (Exception e) { System.out.println(e.getMessage()); throw new RuntimeException(e); } } static String decrypt(String message, String apiSecret) { try { byte[] keyBytes = apiSecret.getBytes(Charset.forName("UTF-8")); byte[] message_bytes = message.getBytes(Charset.forName("UTF-8")); message_bytes = decodeHex(message_bytes); SecretKeySpec key = new SecretKeySpec(keyBytes, 0, 16, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivspec = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); cipher.init(Cipher.DECRYPT_MODE, key, ivspec); byte[] email_decrypted = cipher.doFinal(message_bytes); return new String(email_decrypted, "UTF-8"); } catch (Exception e) { System.out.println(e.getMessage()); throw new RuntimeException(e); } } static String bytesToHexString(byte[] in) { final StringBuilder builder = new StringBuilder(); for (byte b: in) { builder.append(String.format("%02x", b)); } return builder.toString(); } static byte[] decodeHex(byte[] data) throws Exception { String text = new String(data, "UTF-8"); char[] chars = text.toCharArray(); int len = chars.length; byte[] out = new byte[len >> 1]; int i = 0; for (int j = 0; j < len; ++i) { int f = toDigit(chars[j], j) << 4; ++j; f |= toDigit(chars[j], j); ++j; out[i] = (byte)(f & 255); } return out; } static int toDigit(char ch, int index) throws Exception { int digit = Character.digit(ch, 16); if (digit == -1) { throw new Exception("Illegal hexadecimal character " + ch + " at index " + index); } else { return digit; } }}These samples show the attributes with encrypted values.
Job Sync API sample data
#<div data-indeed-apply-widget-type="<WIDGET-TYPE>" data-indeed-apply-sourceJobPostingId="<JobId>" data-indeed-apply-partnerApiToken="<PartnerApiToken>" data-indeed-apply-encryptedJobUrl="b18d0be4c173dfa2b7cb7856e7c7b6f02a563867a9df67ca208c8b9654966299" data-indeed-apply-encryptedContinueUrl="417856e7c173dfa2b7cb7856e7c7b6f02a563867a9df67ca208c8b9654966299" data-indeed-apply-encryptedExitUrl="2b7cb78563dfa2b7cb7856e7c7b6f02a563867a9df67ca208c8b9654966299"></div>XML feed sample data
#<div data-indeed-apply-widget-type="<WIDGET-TYPE>" data-indeed-apply-encryptedSourceName="1d76ab187297cf4c284e2621c2c7462fbfb216704ed0fdc325de0baf6dca5718" data-indeed-apply-encryptedFeedUrl="b18d0be4c173dfa2b7cb7856e7c7b6f02a563867a9df67ca208c8b9654966299" data-indeed-apply-encryptedReferenceId="<ReferenceId>" data-indeed-apply-partnerApiToken="<PartnerApiToken>" data-indeed-apply-encryptedJobUrl="b18d0be4c173dfa2b7cb7856e7c7b6f02a563867a9df67ca208c8b9654966299" data-indeed-apply-encryptedContinueUrl="417856e7c173dfa2b7cb7856e7c7b6f02a563867a9df67ca208c8b9654966299" data-indeed-apply-encryptedExitUrl="2b7cb78563dfa2b7cb7856e7c7b6f02a563867a9df67ca208c8b9654966299"></div>Put only one URL in each URL attribute: encryptedJobUrl, encryptedContinueUrl, encryptedExitUrl, and encryptedFeedUrl. If an attribute has more than one URL, the button shows an error and appears gray and inactive.
6. Choose button design
#This image shows a successful Apply with Indeed button.
This image shows an unsuccessful Apply with Indeed button.
If the button doesn't load, see Troubleshoot errors and Troubleshoot Indeed Apply errors.
Make the Apply with Indeed button the same size and style as your other Apply buttons.
Option 2: applyUrlForEmployers mutation
#Use this mutation to create an Apply with Indeed URL, also called an Indeed Apply URL, each time you show an Apply with Indeed button.
1. Create Apply with Indeed URL
#Call ApplyUrlMutations.applyUrlForEmployers with the inputs that identify the job. Use the inputs that match how you post jobs.
Job Sync API inputs
#sourcedPostingId(required): The employer job ID that the Job Sync API returns when you create a job posting.jobUrl(required): The URL of the public job description page that shows the Apply with Indeed button.continueUrl: Where applicants go when they select Continue on the success screen.exitUrl: Where applicants go when they exit the Indeed Apply form.
XML feed inputs
#sourceName(required): The parent organization that's hiring for the role, such as Test Company Name or Indeed Japan.feedUrl(required): The URL of your XML feed.referenceId(required): The job's<referenceNumber>or<referenceId>value from the XML feed.jobUrl(required): The URL of the public job description page that shows the Apply with Indeed button.continueUrl: Where applicants go when they select Continue on the success screen.exitUrl: Where applicants go when they exit the Indeed Apply form.
Each Apply with Indeed URL works only once. Generate one every time you show the button.
Request – Job Sync API
#mutation { applyUrl { createApplyUrlForEmployers(input: { jobId: { sourceJobPostingId: "jobId" } jobUrl: "http://example.com/careers/job1.html" continueUrl: "http://example.com/careers/indeedapply-continue.html" exitUrl: "http://example.com/careers" }) { applyUrl } }}Request – XML feed
#mutation { applyUrl { createApplyUrlForEmployers(input: { xmlFeedParams: { sourceName: "sourceName", feedUrl: "feedUrl", referenceId: "referenceNumber" } jobUrl: "http://example.com/careers/job1.html" continueUrl: "http://example.com/careers/indeedapply-continue.html" exitUrl: "http://example.com/careers" }) { applyUrl } }}Response – ApplyUrlMutations.applyUrlForEmployers
#The mutation returns an applyUrl to use for the Apply with Indeed button on the ATS job page:
{ applyUrl: "https://smartapply.indeed.com/beta/indeedapply/applybyapplyablejobid?indeedApplyableJobId=b957be31-16ed-4f65-9376-cf383594eee5-Y21ocWEx"}Headers – ApplyUrlMutations.applyUrlForEmployers
#Include these headers in your request:
| Header | Meaning | Required or optional | Details | Example |
|---|---|---|---|---|
authorization | Access token | Required | Authorizes access to the mutation. See Get an access token. | Bearer eyJrEIOeWQiOiI5NzVkOWZkMC01NzE3LTQzMzQtOTM |
indeed-co | Country | Optional | Sets the country for the request. | US |
indeed-locale | Locale | Optional | Sets the client's locale for the original request. | en-US (format) |
indeed-user-agent | Applicant's user agent string | Optional | Sets the user agent for the original browser request. | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 |
indeed-user-ip | Applicant's IP | Optional | Sets the client's IP address for the original browser request. | 192.168.0.0 |
2. Integrate button design with mutation
#If you use the mutation, you can load Indeed's button CSS with this script:
<!DOCTYPE html><html><head> ... <!-- Step 1: Load the script --> <script src="https://apply.indeed.com/indeedapply/static/scripts/app/buttonStyles.js" ></script></head><body> ... <!-- Step 2: Specify data-style as indeed-apply-button for the button where you want to enable button style --> <button data-style="indeed-apply-button">Apply Now</button></body></html>This image shows the Apply with Indeed button with the CSS applied.
To disable the button after a failure, add aria-disabled=true to the button element.
<!-- Set aria-disabled attribute to true to disable the button --><button data-style="indeed-apply-button" aria-disabled=true>Apply Now</button>aria-disabled=true communicates a disabled state but does not prevent a native <button> from activating. If the URL request fails, the example button can still run its click handler. Add the native disabled attribute, or explicitly suppress activation if the button must remain focusable.
This image shows the disabled Apply with Indeed button.
3. Change the button language
#To change the button's language, add the hl query parameter to the script URL, such as hl=ja or hl=es.
<!DOCTYPE html><html><head> ... <!-- Step 1: Specify query parameter --> <script src="https://apply.indeed.com/indeedapply/static/scripts/app/buttonStyles.js?hl=ja"></script></head><body> ... <!-- Step 2: Specify data-style as indeed-apply-button for the button where you want to enable button style --> <button data-style="indeed-apply-button">Apply Now</button></body></html>This image shows the Apply with Indeed button in Japanese.
4. Troubleshoot errors
#When a request fails, the data field is null, and the errors field contains details about the failure. Common errors include GRAPHQL_PARSE_FAILED, BAD_USER_INPUT with sub-codes such as JOB_NOT_FOUND and PARTNER_DATA_NOT_FOUND, and INTERNAL_SERVER_ERROR.
If a request fails and you don't get a URL, hide the Apply with Indeed button. Show the button again after you get a URL. Retry only INTERNAL_SERVER_ERROR, with exponential backoff. For GRAPHQL_PARSE_FAILED and BAD_USER_INPUT, fix the request before you send it again. For the full retry strategy, see Troubleshoot Indeed Apply errors.
For detailed error descriptions, sub-codes, and resolution steps, see Troubleshoot Indeed Apply errors.
See also
#Guides
#- Partner Console — Manage your Indeed API and service integrations.
- Integrate with Indeed and call APIs — Get OAuth credentials and make your first API call.
- Job Sync API guide — Create and manage job postings on Indeed.
- Job Sync XML feed guide — Use an XML feed to define your jobs and how they appear on Indeed.
- Job Sync API > Response – Create job posting
- Troubleshoot Indeed Apply errors — Common Indeed Apply errors and how to resolve them.
Reference
#- Supported language codes, country codes, and locales — Accepted languageCode, countryCode, and locale values, with the display text Indeed shows for each.