Authorization code flow (3-legged OAuth)

Authenticate your app to act on behalf of other Indeed user accounts and the users' associated employers.

📘

Note:

By using this API and its documentation and building an integration, you agree to the Additional API Terms and Guidelines.

Authorization code flow (3-legged OAuth) overview

📘

Note:

For an overview of Indeed authentication, see Indeed authentication.

Authorize your app to act on behalf of other Indeed user accounts and those user accounts' associated employer accounts.

To use this flow, complete these steps:

See also Additional tasks.

Step 1. Register your app

Register your app to get a client ID and secret, which identify your app.

  1. On the Manage app credentials page, sign in to your Indeed account, then click Register new application.

  2. Enter your app name and description, select the OAuth 2.0 credential type, then click Save and continue.

    📘 Notes:

    • Register both test and production versions of an app to generate separate API credentials. During registration, add these suffixes to their names:

      • -dev to test apps. For example, AceRecruitersApp-dev.
      • -prod to production apps. For example, AceRecruitersApp-prod.
    • The Indeed Apply credential type is for Indeed Apply integrations only.

  1. For the OAuth 2.0 credential type:

    Field Value
    Public client

    Indicate whether the app is a public client.

    Allowed grant types

    Select the authorization code grant type.

    Redirect URL

    Define one or more redirect URLs.

    Then, click Save and continue.

  2. Indicate whether you want your app to be reviewed, then click Save and continue.

  3. Optionally, provide additional company information, then click Save and continue.

  4. Preview your app information, then click Complete registration.

    The page shows information about your app, including the client ID, and if your app is not a public client, your client secret.

    📘 Important:

    Secure these credentials. Never share them or store them in a public Git repository.

Step 2. Get an authorization code

To get an authorization code, call the oauth/v2/authorize endpoint with your client ID. In response to this call, Indeed shows the OAuth consent screen to the user, the user authorizes your app, then Indeed passes an authorization code to the redirect URL that you specified during app registration.

  1. To add an Indeed authorization link to your app, call the https://secure.indeed.com/oauth/v2/authorize endpoint with request body parameters:

    https://secure.indeed.com/oauth/v2/authorize?client_id=6nwwcdklwgktryjw2j5fxh5t2fyneule7zg7mvw3pf9jbx3wmewzlxkdz1jxvs6b&redirect_uri=http%3A%2F%2Fwww.acerecruitersllc.com%2Foauth%2Findeed&response_type=code&state=employer1234&scope=email+offline_access+employer_access
    

    The call shows the OAuth consent screen to the user. If the user clicks Allow, Indeed redirects them to your app at the redirect_uri, with the code and state response fields appended.

  2. Capture the code parameter from the redirect_uri page:

    GET http://www.acerecruitersllc.com/oauth/indeed?code=rXZSMNyYQHQ&state=employer1234
    

Step 3. Get an access token

To make API calls, you must include an access token in each call. The token identifies an Indeed account and proves that your app is authorized to make calls on behalf of this account.

To get an access token, you exchange your client ID and secret and either an authorization code or, if the access token expires, a refresh token, for one.

  1. Make a POST request to the https://apis.indeed.com/oauth/v2/tokens endpoint.

    In the request, include:

    • Content-Type and Accept request headers.
    • code, client_id, client_secret, redirect_uri, and the grant_type=authorization_code parameters.

    For example:

    curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: application/json' \\
      -d 'code=rXZSMNyYQHQ' \\
      -d 'client\_id=6nwwcdklwgktryjw2j5fxh5t2fyneule7zg7mvw3pf9jbx3wmewzlxkdz1jxvs6b' \\
      -d 'client\_secret=02KKpg6yLXw2v3FKf5lqyFGtMQCvPBNbJIw89SoSd9fts1LAdlvwUQQ6dwhAhEXv' \\
      -d 'redirect\_uri=http://localhost:3000/oauth/callback' \\
      -d 'grant\_type=authorization\_code' \\
      https://apis.indeed.com/oauth/v2/tokens
    

    The JSON response shows the following response fields:

    {
      "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXV[...]",
      "id_token": "eyJraWQiOiJlMzEzZTc4My1lM2YwLTQ3ZWMtY[...]",
      "refresh_token": "rXZSMNyYQHQ",
      "expires_in": 3600,
      "token_type": "Bearer",
      "scope": "email offline_access",
      "consented_scope": "email offline_access"
    }
    

    The JSON response for an error includes these fields:

    {
      "error": "invalid_request",
      "error_description": "Invalid authentication request."
    }
    
  2. Securely store the access token and refresh token.

📘

Note:

When you get an access token, you can use the basic authentication scheme instead of passing your client ID and secret in the /oauth/v2/tokens request body.

Step 4. Refresh your access token

Use a refresh token to refresh your access token.

Indeed returns a refresh token with the access token when your app gets the offline_access scope.

Access tokens are valid for one hour. Refresh tokens are valid for 60 days from when they are issued. With each refresh, the refresh token's expiration date is extended to 60 days from the most recent refresh.

To refresh your access token:

  1. Make a POST request to the https://apis.indeed.com/oauth/v2/tokens endpoint.

    In the call, include the Content-Type and Accept request headers and the refresh_token, client_id, client_secret, and the grant_type=refresh_token request body parameters.

    curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: application/json' \
      -d 'refresh_token=8qE_nKXUng4' \
      -d 'client_id=6nwwcdklwgktryjw2j5fxh5t2fyneule7zg7mvw3pf9jbx3wmewzlxkdz1jxvs6b' \
      -d 'client_secret=02KKpg6yLXw2v3FKf5lqyFGtMQCvPBNbJIw89SoSd9fts1LAdlvwUQQ6dwhAhEXv' \
      -d 'grant_type=refresh_token' \
      https://apis.indeed.com/oauth/v2/tokens
    

    The JSON response includes these fields:

    {
      "access_token": "eyJhbGciOiJIUzI1NiIslKxwRJSMeKKeJf36POk6yJV_adQs[...]",
      "id_token": "eyJraWQiOiJlMzEzZTc4My1lM2YwLTQ3ZWMtYmJlMi1jMWRlNGYy[...]",
      "refresh_token": "rXZSMNyYQHQ",
      "convid": "1c1a1s8540kkt89p",
      "scope": "email offline_access",
      "token_type": "Bearer",
      "expires_in": 3600
    }
    

    If an error occurs, the JSON response includes these fields:

    {
      "error": "invalid_request",
      "error_description": "Invalid authentication request."
    }
    
  2. Pass your access token in each API request.

Step 5. Call an Indeed API

In each API call, pass the access token in the Authorization header with the Bearer authentication scheme.

For example:

Authorization: Bearer <token>

Additional tasks

Enable user to select an employer

You can associate an Indeed user account with one or more employer accounts. When you call an Indeed API, the API might require you to represent a particular employer. To associate an employer with an access token, show the user either the Indeed-hosted employer selection screen or your own employer selection screen to enable the user to select an employer from a list of employers associated with the user.

📘

Note:

An access token can represent only one employer at a time. To switch employers, generate an access token. For example, use a refresh token to generate an access token.

Show the Indeed employer selection screen

  1. To request the creation of an authorization link to display the Indeed employer selection screen, specify the prompt=select_employer and scope=employer_access request body parameters. Otherwise, the Indeed employer selection screen does not appear.

    For example, this link includes both the prompt=select_employer parameter and a request for the employer_access scope:

    https://secure.indeed.com/oauth/v2/authorize?client_id=bf7622efee705862320df0f5c7690b22f60ba24f236aaf4adf5b7a36fa0adcf1&redirect_uri=https%3A%2F%2Fexample.com%2Foauth&response_type=code&state=random&scope=email+offline_access+employer_access&prompt=select_employer
    

    This authorization link triggers these screens to appear:

    Screen Description
    Authentication Appears when the user is logged out of Indeed.
    OAuth consent Enables a user to grant consent for any scopes that the OAuth app requests, such as the employer_access scope.
    Indeed employer selection Enables a user to select an employer from a list of employers associated with the user account.
  2. If the user selects an employer, a redirect URI parameter transmits the employer ID to your OAuth app:

    https://example.com/oauth/callback?state=random&employer=6d2f02224e30d401810b1726eb246d8d&code=e_IEr5UlBys
    

    The URL includes an employer parameter, which contains an employer ID value, and associates the access token with that employer:

    curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: application/json' \
     -d 'code=e_IEr5UlBys' \
     -d 'client_id=bf7622efee705862320df0f5c7690b22f60ba24f236aaf4adf5b7a36fa0adcf1' \
     -d 'client_secret=02KKpg6yLXw2v3FKf5lqyFGtMQCvPBNbJIw89SoSd9fts1LAdlvwUQQ6dwhAhEXv' \
     -d 'redirect_uri=https://example.com/oauth' \
     -d 'grant_type=authorization_code' \
     -d 'employer=6d2f02224e30d401810b1726eb246d8d' \
     https://apis.indeed.com/oauth/v2/tokens
    

    📘

    Notes:

    • You can also use the employer parameter when you exchange a refresh token for an access token.
    • If the user does not select an employer or the user account has no associated employers, then the redirect URI does not include an employer parameter.

    🚧

    Warning:

    Don't assume that the redirect URI includes an employer parameter even when you use the prompt=select_employer parameter.

Show a custom employer selection screen

Although Indeed recommends that you enable users to select an employer from the standard Indeed employer selection screen, you can also create a custom employer selection screen:

  1. Get an access token to trigger the OAuth consent screen. When you get the token, request the employer_access and offline_access scopes.

    The query returns an access token, ID token, and refresh token.

  2. To list employers for the current user, use the JSON Web Tokens Debugger to decode the ID token.

  3. Build a custom employer selection screen that enables the user to select an employer.

  4. To get an access token that represents the employer that the user selected, pass the employer ID in the employer parameter of the oauth/v2/tokens endpoint.

    curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: application/json' \
      -d 'refresh_token=4U0xku8M9u0' \
      -d 'client_id=b0c3b1092225d3e99f85d7aa3fe1e6001f9a0bb798717cbc2008e58fbda3ef16' \
      -d 'client_secret=2YFoyZOWmr83njlsIuyCL9QQq5jZkRCR4UtmHGp22MRzjIhe5RbynnAGmuYLFbYx' \
      -d 'grant_type=refresh_token' \
      -d 'employer=13ef9940a7c1f0500a7e411e74178c4e' \
      https://apis.indeed.com/oauth/v2/tokens
    

    In this request:

    Parameter Contains
    grant_type refresh_token
    refresh_token The refresh token
    employer An employer ID

    When you use a refresh token to get an access token, an OAuth consent screen is not necessary.

    Use this access token for API calls on behalf of the consenting user and only for this employer.

Get user information

To get information about the user account that registers an app, use either the:

ID token

An ID token is a Base64-encoded JSON Web Token (JWT) that you get automatically when you get an access token. This token proves that the user has been authenticated.

📘

Note:

An Indeed API reads an access token while a third-party app reads an ID token to verify the user's identity.

The ID token contains information about the current user. See Response fields for v2/api/userinfo.

Because the JSON Web Token is signed, use the public key to verify that the token is valid:

https://secure.indeed.com/.well-known/keys

For information about ID tokens, see ID Token in the OpenID Connect Core 1.0 incorporating errata set 1 specification.

  1. So that the ID token includes the email and email_verified fields, your app must get the email scope.

  2. Request an access token:

    curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: application/json' \
      -d 'code=qCwk_cdC4Ms' \
      -d 'client_id=a0c3b1092225d3e99f85d7aa3fe1e6001f9a0bb798717cbc2008e58fbda3ef16' \
      -d 'client_secret=1YFoyZOWmr83njlsIuyCL9QQq5jZkRCR4UtmHGp22MRzjIhe5RbynnAGmuYLFbYx' \
      -d 'redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Foauth%2Fcallback' \
      -d 'grant_type=authorization_code' \
      https://apis.indeed.com/oauth/v2/tokens
    

    The JSON response shows the oauth/v2/tokens response fields:

    {
      "access_token": "eyJraWQiOiINiYzIwMDh9hrYboL9BMnU_Pfbv41Nqj3nUuMbprUtRgQ",
      "convid": "1erq23om7t5bu800",
      "scope": "email employer_access",
      "id_token": "eyJraWQiMWQ4NTciLCJ0eXAiOiJKV1LmNvbSzNjMwfQ.Jrb1fX-aPtkV9nQK9U1LFP0V61eEApo3pFFPaGjT9zA",
      "token_type": "Bearer",
      "expires_in": 3600
    }
    
  3. To decode the ID token value, copy it into the JSON Web Tokens Debugger.

    • If the user does not grant your app any scopes, the response shows the sub field, which represents a user's account ID:

      {
        "sub": "d2d1962c0664d970"
      }
      
    • If the user grants your app the email and employer_access scopes, the response shows the email and email_verified fields:

      {
        "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": "[email protected]"
      }
      

The userinfo endpoint

To get account information for the current user, call the v2/api/userinfo endpoint, which returns the same information the ID token returns.

For example, you need to get account information when you create Log in with Indeed buttons.

The URL path is:

GET/POST https://secure.indeed.com/v2/api/userinfo

This endpoint does not have any request body parameters.

In this request, pass the access token in the Authentication header with the Bearer authentication scheme.

Authorization: Bearer <token>

where <token> is the your access token.

For example:

GET /v2/api/userinfo HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Host: secure.indeed.com

A successful request returns the HTTP status code 200 and a response payload:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "sub": "248289761001",
  "email": "min[email protected]",
  "email_verified": true
}

See Response fields for v2/api/userinfo.

To call the v2/api/userinfo endpoint:

  1. Request the email scope to return the email and email_verified fields.

  2. Get an access token with the authorization code grant type.

  3. Call the userinfo endpoint.

    This curl command shows how to call the endpoint, with a shortened token for readability:

    curl -H 'Authorization:Bearer eyJraWQiOiI1OTdjYTgxNC03YTA2LTR0zh0Elm9A' \
    https://secure.indeed.com/v2/api/userinfo
    

    The userinfo endpoint returns information for the scopes that a user grants to your OAuth app:

    • If the user does not grant your app any scopes, the response shows the sub field, which represents a user's account ID:

      {
        "sub": "d2d1962c0664d970"
      }
      
    • If the user grants your app the email scope, the response shows the email and email_verified fields:

      {
        "sub": "a95064930d19bbc7",
        "email": "swalther[email protected]",
        "email_verified": true
      }
      
    • If the user grants your app the employer_access scope, the response shows a list of employers, or advertisers, for the user account:

      {
        "sub": "bc8c847009a955c9",
        "email": "[email protected]",
        "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

When you register an OAuth client app that uses the authorization code flow (3-legged OAuth), Indeed limits the number of redirect URLs to five, which conforms to the security best practices for OAuth apps.

To dynamically redirect a user to multiple URLs:

  1. Add a state parameter 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
    
  2. After the Indeed user completes the authorization code flow (3-legged OAuth), Indeed returns the state query parameter value in your redirect URL:

    GET http://www.acerecruitersllc.com/oauth/indeed?code=rXZSMNyYQHQ&state=AnyValue
    
  3. To pass a URL with the state parameter, such as https://somesite.com, 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
    
  4. When Indeed redirects the user to your app, use the state parameter value to redirect them to another destination, such as https://somesite.com.

Guidelines

Do not expose the authorization code

📘

Note:

If you redirect a user to an untrusted website, you inadvertedly reveal the OAuth authorization code in the HTTP Referer header, which contains the URL that requested a page.

The HTTP referer specification misspells referer.

You might unintentionally expose the authorization code to the website that the state parameter represents. That website likely logs the authorization code in its website logs.

To prevent the authorization code from leaking:

  • Redirect the user to another trusted page in your app before you redirect them to the untrusted app. The HTTP Referer header reveals only the latest URL and not any URLs requested before that.

Do not append query parameters to the redirect URI

Currently, Indeed supports query parameters in the redirect URI.

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.com

The authorize URL includes a redirect_uri parameter with the https://somesite.com?return=https://someothersite.com value.

📘

Note:

  • The redirect_uri parameter includes the return query parameter, which contains another redirect URL.
  • Indeed encourages you to use the state parameter rather than using query parameters in the redirect_uri parameter because Indeed might discontinue that support of query parameters in the redirect_uri parameter.

See also

Topic Description
OAuth glossary Get descriptions for common OAuth terms.
OAuth reference Specify HTTP request headers and parameters and review response fields in the client credentials flow (2-legged OAuth) and the authorization code flow (3-legged OAuth).
Scopes Indeed authorization scopes.
Error Response in RFC6749: The OAuth 2.0 Authorization Framework The error response.
Log in with Indeed Indeed images to create Log In with Indeed buttons.
Upgrade OAuth How to upgrade your OAuth integration to the Indeed OAuth v2 endpoint