- Overview
- Steps
- 1. Get your OAuth credentials
- 2. Get an authorization code
- 3. Get a 3-legged access token
- 4. Refresh your 3-legged access token
- 5. Call Indeed APIs
- Additional tasks
- Get an employer access token
- Get user information
- Dynamically redirect a user to multiple URLs
- Prevent the authorization code from leaking
- Don't append query parameters to the redirect URI
- See also
- Guides
- References
Authorization code grant type (3-legged OAuth)
Authorize your app to act on behalf of Indeed user accounts and associated employer accounts.
By using this API and its documentation and building an integration, you agree to the Additional API Terms and Guidelines.
Overview
#Use the authorization code grant type (3-legged OAuth) to let your app act on behalf of Indeed user accounts and their associated employer accounts. The user signs in to Indeed and approves your app's access. Your app then exchanges the resulting authorization code for an access token.
You can also:
Steps
#1. Get your OAuth credentials
#When you become an Indeed partner, Indeed creates an app for your integration. Sign in to Partner Console (opens in a new window) to view the app and your OAuth credentials: client ID and secret. Exchange those credentials for an access token to authenticate API calls.
If your app uses 3-legged OAuth, Partner Console also shows its settings, such as the redirect URL allowlist.
2. Get an authorization code
#To get an authorization code, send the user to Indeed's OAuth consent screen. If the user approves, Indeed sends the code to your redirect URL. The authorization code expires in 10 minutes. See Response fields for oauth/v2/authorize.
-
Build the request URL for the Indeed OAuth 2.0 server at
https://secure.indeed.com/oauth/v2/authorize.This example requests the
email,offline_access, andemployer_accessscopes. It includes line breaks for readability:https://secure.indeed.com/oauth/v2/authorize?client_id=c4acac1b35917a14c11947f6564ee28a0758398cd0fdf4deec685c40f14bb8a8&redirect_uri=http%3A%2F%2Fwww.acerecruitersllc.com%2Foauth%2Findeed&response_type=code&scope=email+offline_access+employer_access&code_challenge=<code_challenge>&code_challenge_method=S256&state=employer1234The query parameters for
oauth/v2/authorizeare:oauth/v2/authorize query parameters Query parameter Required Description client_id✔
Your client ID.
redirect_uri✔
Your URL-encoded redirect URL: the page on your site that receives the authorization code. It must match a redirect URL that you registered for your app.
response_type✔
Always
code.scope✔
The scopes that your app requests, separated by spaces and URL-encoded. URL-encoding replaces the spaces with plus signs (
+).stateRecommended
A unique string that your app generates. Indeed returns it to your redirect URI, so you can match the callback to your request and prevent cross-site request forgery (CSRF) attacks.
-
Redirect the user to the request URL.
Indeed shows the OAuth consent screen to the user who owns the account that you want to access.
If the user selects Allow, Indeed redirects them to your
redirect_uriwith thecode,iss, andstatequery parameters. -
On your
redirect_uripage, capture thecodeparameter.For example:
GET http://www.acerecruitersllc.com/oauth/indeed?code=rXZSMNyYQHQ&state=employer1234&iss=https%3A%2F%2Fsecure.indeed.com
3. Get a 3-legged access token
#Access tokens last one hour. To refresh an access token, see Refresh your 3-legged access token.
To get an access token, send a POST request to https://apis.indeed.com/oauth/v2/tokens with your authorization code:
curl -L -X POST 'https://apis.indeed.com/oauth/v2/tokens' \ -H 'Accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'grant_type=authorization_code' \ -d 'client_id=<client_id>' \ -d 'client_secret=<client_secret>' \ -d 'code=<authorization_code>' \ -d 'code_verifier=<code_verifier>' \ -d 'redirect_uri=http://www.acerecruitersllc.com/oauth/indeed'The request headers are:
| Header | Value |
|---|---|
Accept | application/json |
Content-Type | application/x-www-form-urlencoded |
The request body parameters for oauth/v2/tokens are:
| Request body parameter | Required | Value |
|---|---|---|
grant_type | ✔ | authorization_code |
client_id | ✔ | Your client ID. |
client_secret | ✔ | Your client secret. |
scope | Conditional | Scopes that grant your app permissions. |
code | ✔ | Your authorization code. |
code_verifier | ✔ when you use PKCE | The code verifier that you used to generate the code_challenge. Confidential clients should also use PKCE. See RFC 7636: Proof Key for Code Exchange by OAuth Public Clients (opens in a new window). |
redirect_uri | ✔ | Your URL-encoded redirect URL. It must match the redirect URL that you used to get the authorization code. |
{ "access_token": "<access_token>", "convid": "1eis1tplg01fe802", "refresh_token": "<refresh_token>", "scope": "offline_access email", "token_type": "Bearer", "expires_in": 3600}4. Refresh your 3-legged access token
#To refresh an access token, send a POST request to https://apis.indeed.com/oauth/v2/tokens with your refresh token:
curl -L -X POST 'https://apis.indeed.com/oauth/v2/tokens' \ -H 'Accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'grant_type=refresh_token' \ -d 'client_id=<client_id>' \ -d 'client_secret=<client_secret>' \ -d 'refresh_token=<refresh_token>' The request headers are:
| Header | Value |
|---|---|
Accept | application/json |
Content-Type | application/x-www-form-urlencoded |
The request body parameters for oauth/v2/tokens are:
| Request body parameter | Required | Value |
|---|---|---|
grant_type | ✔ | refresh_token |
client_id | ✔ | Your client ID. |
client_secret | ✔ | Your client secret. |
refresh_token | ✔ | Your refresh token. |
{ "access_token": "<access_token>", "id_token": "<id_token>", "refresh_token": "<refresh_token>", "convid": "1c1a1s8540kkt89p", "scope": "email offline_access", "token_type": "Bearer", "expires_in": 3600}5. Call Indeed APIs
#To call an Indeed GraphQL API, send a POST request to https://apis.indeed.com/graphql with these headers and your GraphQL query or mutation:
curl -L 'https://apis.indeed.com/graphql' \ -H 'Authorization: Bearer <access_token>' \ -H 'Content-Type: application/json' \ -d '{"query":"query {\n jobSearch(\n location: { radius: 5, radiusUnit: MILES, where: \"Austin\" }\n what: \"Nurse\"\n limit: 5\n ) {\n results {\n job {\n title\n sourceEmployerName\n }\n }\n }\n}","variables":{}}'Request headers:
| Header | Value | Description |
|---|---|---|
Authorization | Bearer <token> | Use this header to authenticate with the server and access protected resources. Pass the access token in this header with the Bearer authentication scheme. For the oauth/v2/tokens endpoint, use the basic authentication scheme instead of passing client_id and client_secret in the request body. See Get an access token, Authorization header (opens in a new window), and Basic authentication scheme. |
Content-Type | application/json | The media type (opens in a new window) of the resource. See Content-Type header (opens in a new window). |
The -d parameter specifies the GraphQL query:
query { jobSearch(location: { radius: 5, radiusUnit: MILES, where: "Austin" } what: "Nurse" limit: 5) { results { job { title sourceEmployerName } } }}{ "errors": [{ "message": "The client does not have access to the 'job-retrieval-service' service.", "extensions": { "code": "INTERNAL_SERVER_ERROR" } }], "data": null}Additional tasks
#Get an employer access token
#Some Indeed APIs require an access token that represents an employer or advertiser. In the following procedure, use advertiser instead of employer where needed.
Show the user one of these screens:
On either screen, the user selects one of the employers associated with their account. Then you get an access token for that employer.
Each access token represents one employer. To switch employers, get a new access token.
Show the Indeed employer selection screen
#-
To show the Indeed employer selection screen, build an authorization URL for the
https://secure.indeed.com/oauth/v2/authorizeendpoint with theprompt=select_employerandscope=employer_accessquery parameters:https://secure.indeed.com/oauth/v2/authorize?client_id=<client_id>&redirect_uri=https%3A%2F%2Fexample.com%2Foauth&response_type=code&state=random&scope=email+offline_access+employer_access&prompt=select_employerThis URL shows the user these screens:
Indeed-provided screens Screen Description Authentication
Appears when the user is signed out of Indeed.
OAuth consent
Lets the user approve the scopes that your app requests, such as
employer_access.Indeed employer selection
Lets the user select one of the employers associated with their account.
To preview the OAuth consent screen that your users see, use Partner Console.
-
When the user selects an employer, Indeed adds the employer ID to your redirect URI in the
employerparameter:https://example.com/oauth/callback?state=random&employer=6d2f02224e30d401810b1726eb246d8d&code=e_IEr5UlBys&iss=https%3A%2F%2Fsecure.indeed.comDon't assume that the redirect URI always includes the
employerparameter, even when you setprompt=select_employer. Indeed omits it if the user doesn't select an employer or has no associated employers.
Show a custom employer selection screen
#Indeed recommends the Indeed employer selection screen. To build your own screen instead:
-
Get a 3-legged access token with the
employer_accessandoffline_accessscopes.The response includes an access token, ID token, and refresh token.
-
Decode the ID token to list the current user's employers. For example, use the JSON Web Tokens Debugger (opens in a new window).
-
Build a custom screen that lets the user select an employer.
Get employer access token
#After the user selects an employer, send a POST request to https://apis.indeed.com/oauth/v2/tokens with the employer's ID:
curl -L -X POST 'https://apis.indeed.com/oauth/v2/tokens' \ -H 'Accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'grant_type=authorization_code' \ -d 'client_id=<client_id>' \ -d 'client_secret=<client_secret>' \ -d 'redirect_uri=http://localhost' \ -d 'code=<authorization_code>' \ -d 'employer=<employer_id>'The request headers are:
| Header | Value |
|---|---|
Accept | application/json |
Content-Type | application/x-www-form-urlencoded |
The request body parameters for oauth/v2/tokens are:
| Request body parameter | Required | Value |
|---|---|---|
grant_type | ✔ |
Or:
If you use a refresh token, the user doesn't see the OAuth consent screen again. |
client_id | ✔ | Your client ID. |
client_secret | ✔ | Your client secret. |
code | ✔ with authorization_code | The authorization code. |
redirect_uri | ✔ | Your URL-encoded redirect URL. |
refresh_token | ✔ with refresh_token | The refresh token. |
employer | ✔ | The ID of the employer that the user selected. |
The response contains an access token that represents the employer:
{ "access_token": "<access_token>", "scope": "employer_access", "token_type": "Bearer", "expires_in": 3600}If the employer isn't associated with the user who registered the app, Indeed returns this error:
{ "error_description": "Invalid request", "error": "invalid_request"}Use this access token to call APIs on behalf of the consenting user, for this employer only.
You can also pass the employer parameter when you exchange a refresh token for an access token.
Get user information
#To get information about the current user, use either:
- The ID token
- The userinfo endpoint
ID token
#An ID token is a Base64-encoded JSON Web Token (JWT) that you receive when you get a 3-legged access token. It proves that Indeed authenticated the user, and it carries information about the current user. See id_token.
Indeed APIs read the access token. Your app reads the ID token to verify the user's identity.
The ID token is signed. Verify the signature with Indeed's public keys:
https://secure.indeed.com/.well-known/keysFor more about ID tokens, see ID Token (opens in a new window) in the OpenID Connect Core 1.0 incorporating errata set 1 specification.
-
To include the
emailandemail_verifiedfields in the ID token, request theemailscope. -
Get an access token. Send a
POSTrequest tohttps://apis.indeed.com/oauth/v2/tokenswith your authorization code:curl -L -X POST 'https://apis.indeed.com/oauth/v2/tokens' \-H 'Accept: application/json' \-H 'Content-Type: application/x-www-form-urlencoded' \-d 'grant_type=authorization_code' \-d 'client_id=<client_id>' \-d 'client_secret=<client_secret>' \-d 'redirect_uri=http://localhost' \-d 'code=<authorization_code>'The request headers are:
Get access token request headers Header Value Acceptapplication/jsonContent-Typeapplication/x-www-form-urlencodedThe request body parameters for
oauth/v2/tokensare:oauth/v2/tokens request body parameters Request body parameter Required Value grant_type✔ authorization_codeOr:
refresh_tokenIf you use a refresh token, the user doesn't see the OAuth consent screen again.
client_id✔ Your client ID.
client_secret✔ Your client secret.
code✔ with authorization_codeThe authorization code. redirect_uri✔ Your URL-encoded redirect URL. refresh_token✔ with refresh_tokenThe refresh token. The response includes the
id_tokenfield:{"access_token": "<access_token>","convid": "1erq23om7t5bu800","scope": "email employer_access","id_token": "<id_token>","token_type": "Bearer","expires_in": 3600} -
Decode the ID token, for example in the JSON Web Tokens Debugger (opens in a new window).
The decoded payload includes the
subfield, which holds the user's account ID:{"sub": "d2d1962c0664d970"}If the user grants the
emailandemployer_accessscopes, the payload also includes theemail,email_verified, andemployersfields:{"aud": "a0c3b1092225d3e99f85d7aa3fe1e6001f9a0bb798717cbc2008e58fbda3ef16","sub": "d2d1962c0664d970","employers": [{"id": "13ef9940a7c1f0500a7e411e74178c4e","name": "Dharma Initiative"}, {"id": "6d2f02224e30d401810b1726eb246d8d","name": "Umbrella Corporation"}],"email_verified": true,"iss": "https://secure.indeed.com","exp": 1610417960,"iat": 1610414360,"email": "somebody@indeed.com"}
The userinfo endpoint
#To get account information for the current user, call the https://secure.indeed.com/v2/api/userinfo endpoint. It takes no query parameters or request body.
For example, call this endpoint when you build Log in with Indeed buttons.
The response contains the same information that the ID token returns.
A successful request returns the HTTP status code 200 and a response payload:
HTTP/1.1 200 OKContent-Type: application/json
{ "sub": "248289761001", "email": "mina.ray@myemail.world", "email_verified": true}To call the v2/api/userinfo endpoint:
-
To get the
emailandemail_verifiedfields, request theemailscope. -
Send a
GETrequest tohttps://secure.indeed.com/v2/api/userinfowith your access token in theAuthorizationheader:The response depends on the scopes that the user grants to your app:
-
With no scopes, the response includes only
sub, the user's account ID:{"sub": "d2d1962c0664d970"} -
With the
emailscope, the response also includesemailandemail_verified:{"sub": "a95064930d19bbc7","email": "somebody+samples@indeed.com","email_verified": true} -
With the
employer_accessscope, the response also lists the employers, or advertisers, associated with the account:{"sub": "bc8c847009a955c9","email": "somebody+employer@indeed.com","email_verified": true,"employers": [{"id": "084a39249af95beedfb90cc5d2b8833c","name": "Dharma Initiative"},{"id": "865e08b649774436ee1f410b611fad7c","name": "Umbrella Corporation"},{"id": "4bc393648e880bc94dd6cef8efbc8486","name": "US Robotics and Mechanical Men"}]}
-
Dynamically redirect a user to multiple URLs
#Following OAuth security best practices, Indeed allows up to five redirect URLs for each 3-legged OAuth app. To send users to more destinations, use the state parameter:
-
Add a
stateparameter to your authorize URL:https://secure.indeed.com/oauth/v2/authorize?client_id=6nwwcdklwgktryjw2j5fxh5t2fyneule7zg7mvw3pf9jbx3wmewzlxkdz1jxvs6b&redirect_uri=http%3A%2F%2Fwww.acerecruitersllc.com%2Foauth%2Findeed&response_type=code&scope=email+offline_access+employer_access&state=AnyValue -
Indeed adds the
code,iss, andstatequery parameters to your redirect URL:GET http://www.acerecruitersllc.com/oauth/indeed?code=rXZSMNyYQHQ&state=AnyValue&iss=https%3A%2F%2Fsecure.indeed.comYou can ignore the
issparameter unless you authenticate against multiple providers. -
To pass a URL, such as
https://somesite.com, in thestateparameter, URL-encode it:https://secure.indeed.com/oauth/v2/authorize?client_id=6nwwcdklwgktryjw2j5fxh5t2fyneule7zg7mvw3pf9jbx3wmewzlxkdz1jxvs6b&redirect_uri=http%3A%2F%2Fwww.acerecruitersllc.com%2Foauth%2Findeed&response_type=code&scope=email+offline_access+employer_access&state=https%3A%2F%2Fsomesite.com -
When Indeed redirects the user to your app, use the
stateparameter to redirect them to another destination, such ashttps://somesite.com.
Prevent the authorization code from leaking
#If you redirect a user to an untrusted website, the HTTP Referer header can expose the authorization code to that site. The header carries the URL of the page that made the request.
The website that the state parameter points to can also see the authorization code, and it might record the code in its logs.
To prevent this, first redirect the user to another trusted page in your app, then to the untrusted site. The Referer header shows only the most recent URL, not earlier URLs in the chain.
Don't append query parameters to the redirect URI
#Indeed currently accepts query parameters in the redirect_uri parameter, as in this example:
https://secure.indeed.com/oauth/v2/authorize?client_id=80f9f4bd6a34cac31daebe1a093a606ce6b34e91ae6cfa139432ae387269a529&response_type=code&state=random&scope=email+offline_access+employer_access&redirect_uri=https%3A%2F%2Fsomesite.com%3Freturn%3Dhttps%3A%2F%2Fsomeothersite.comHere, the redirect_uri value is https://somesite.com?return=https://someothersite.com, which embeds another redirect URL in the return query parameter.
Use the state parameter instead of query parameters in redirect_uri.
See also
#Guides
#- Credentials — Explains the OAuth client ID and secret that let your app use Indeed services.
- Basic authentication scheme — Explains how to send your client ID and secret in a header instead of the
/oauth/v2/tokensrequest body. - Troubleshoot OAuth errors — Explains how to fix OAuth errors that occur before you reach GraphQL.
- Troubleshoot GraphQL errors — Explains how to diagnose errors that GraphQL APIs return.
References
#- HTTP request headers — Lists the HTTP request headers for OAuth requests.
- oauth/v2/tokens endpoint — Explains how to exchange an authorization code or refresh token for an access token.
- Request body parameters for oauth/v2/tokens — Lists the request body parameters for the
oauth/v2/tokensendpoint. - v2/api/appinfo endpoint — Explains how to list the employers associated with the user account that registered the app.